Единица работы с внедрением зависимостей

Я создаю относительно простое веб-приложение в ASP.NET MVC 4, используя Entity Framework для общения с MS SQL Server. Есть много возможностей для расширения приложения в будущем, поэтому я стремлюсь к шаблону, который максимизирует возможность повторного использования и адаптируемость в коде, чтобы сэкономить работу в будущем. Идея такая:

  • Шаблон «Единица работы», чтобы избежать проблем с базой данных, фиксируя изменения только в конце каждого набора действий.
  • Общий репозиторий с использованием BaseRepository<T>, потому что репозитории будут в основном такими же; нечетное исключение может расширять и добавлять свои дополнительные методы.
  • Внедрение зависимости для привязки этих репозиториев к IRepository<T>, которые будут использовать контроллеры, чтобы я мог переключать методы хранения данных и тому подобное с минимальными усилиями (не только для лучшей практики; есть реальная вероятность, что это произойдет). Я использую для этого Ninject.

Раньше я действительно не пробовал делать что-то подобное с нуля, поэтому я читал и думаю, что я где-то запутался. Пока у меня есть интерфейс IRepository<T>, реализованный BaseRepository<T>, который содержит экземпляр DataContext, который передается в его конструктор. В этом интерфейсе есть методы для добавления, обновления, удаления и различных типов Get (одиночный по идентификатору, одиночный по предикату, группировка по предикату, все). Единственный репозиторий, который не соответствует этому интерфейсу (пока), - это репозиторий Users, который добавляет User Login(string username, string password) для разрешения входа в систему (реализация которого обрабатывает все операции слияния, хеширования, проверки и т. Д.).

Из того, что я прочитал, теперь мне нужен класс UnitOfWork, содержащий экземпляры всех репозиториев. Эта единица работы будет открывать репозитории, а также метод SaveChanges (). Когда я хочу манипулировать данными, я создаю экземпляр единицы работы, получаю доступ к его репозиториям (которые создаются по мере необходимости), а затем сохраняю. Если что-то выходит из строя, в базе данных ничего не меняется, потому что до единственного сохранения в конце не удастся. Все в порядке. Моя проблема в том, что все примеры, которые я могу найти, похоже, делают одно из двух:

  • Некоторые передают контекст данных в единицу работы, из которой они извлекают различные репозитории. Это сводит на нет суть DI за счет наличия моего DbContext, специфичного для Entity-Framework (или унаследованного от него класса) в моей единице работы.
  • Некоторые вызывают метод Get для запроса репозитория, который является шаблоном локатора служб, который, по крайней мере, непопулярен, если не антипаттерн, и в любом случае я бы хотел избежать этого здесь.

Нужно ли мне создать интерфейс для моего источника данных и также внедрить его в единицу работы? Я не могу найти никакой документации по этому поводу, которая была бы ясной и / или достаточно полной, чтобы объяснить.

ИЗМЕНИТЬ

Я думаю, что я слишком усложнял это; Теперь я сворачиваю свой репозиторий и единицу работы в один - мой репозиторий является полностью универсальным, поэтому я просто получаю несколько общих методов (Add, Remove, Update и несколько видов Get) плюс метод SaveChanges. Это дает мне интерфейс рабочего класса; Затем у меня может быть фабричный класс, который предоставляет его экземпляры (также с интерфейсом). Если у меня также есть эта рабочая реализация IDisposable, я могу использовать ее в блоке с ограниченной областью видимости. Итак, теперь мои контроллеры могут делать что-то вроде этого:

using (var worker = DataAccess.BeginTransaction())
{
    Product item = worker.Get<Product>(p => p.ID == prodName);
    //stuff...
    worker.SaveChanges();
}

Если что-то пойдет не так до SaveChanges(), все изменения отменяются, когда он выходит из блока области видимости, и рабочий объект удаляется. Я могу использовать внедрение зависимостей для предоставления конкретных реализаций поля DataAccess, которое передается в конструктор базового контроллера. Бизнес-логика полностью реализована в контроллере и работает с IQueryable объектами, поэтому я могу отключить DataAccess объект-поставщик для чего угодно, если он реализует IRepository интерфейс; нигде нет ничего специфического для Entity Framework.

Итак, есть какие-нибудь мысли по поводу этой реализации? Это на правильном пути?


person anaximander    schedule 16.03.2013    source источник
comment
Я хочу указать вам на принцип YAGNI. Нет ничего плохого в том, чтобы подойти к вопросу с академической точки зрения, но я чувствую, что то, что вы предвидите, подвержено широкому спектру изменений, и вы можете увидеть, что вам нужно сильно изменить свой дизайн. Хуже того, вы можете застрять в этом.   -  person Srikanth Venugopalan    schedule 16.03.2013
comment
Я ценю это и обычно соглашусь, но я знаю, что это может понадобиться. Я создаю для развертывания на сервере с MSSQL Server, но у меня есть другие люди, которым также нужны версии, и по крайней мере у одного из них не будет доступа к этому. Нет единой системы, общей для всех мест, где это будет развернуто. Если я знаю, что могу просто написать новую реализацию уровня доступа к данным и отключить несколько привязок для развертывания в другом месте, это сэкономит мне тонус работы позже. Это, и я хочу научиться делать это правильно, и это хорошая возможность.   -  person anaximander    schedule 16.03.2013
comment
Полностью с вами, я не имею ни малейшего представления о вашей настройке, просто ваше описание, кажется, предполагает, что вы хотите построить что-то, ориентированное на будущее (что я не большой поклонник), и, следовательно, ссылка на YAGNI . Но с этим разъяснением, я думаю, вы достигли момента, когда вы могли бы также разработать что-то общее.   -  person Srikanth Venugopalan    schedule 16.03.2013
comment
Гарантия будущего - опасный термин; будущее туманно и непредсказуемо. Это не столько ориентирование на будущее, сколько разработка Фазы 1, чтобы не заставлять больше работать на Фазе 2.   -  person anaximander    schedule 16.03.2013
comment
связанные: stackoverflow.com/questions/10520729/   -  person Ruben Bartelink    schedule 18.03.2013


Ответы (1)


Я предпочитаю, чтобы UnitOfWork или UnitOfWorkFactory были введены в репозитории, поэтому мне не нужно беспокоиться об этом каждый раз, когда добавляется новый репозиторий. Ответственность UnitOfWork будет заключаться в том, чтобы просто управлять транзакцией.

Вот пример того, что я имею в виду.

person Srikanth Venugopalan    schedule 16.03.2013