Должны ли репозитории использовать один и тот же экземпляр контекста в Entity Framework 1.0

Я начал смотреть на Entity Framework для проекта, который я делаю, и иду по пути использования BLL против него через шаблон репозитория. Насколько я понимаю, для каждого объекта я должен создать для него репозиторий, чтобы у меня было

public class UserRepository : IRepository<User>
{ ... }

и

public class AccountRepository : IRepository<Account>
{ ... }

В примерах, которые я видел, обычной практикой является создание контекста сущностей в операторе using и выполнение операций получения, обновления, сохранения и т. д. внутри.

using(var ctx = new AppEntities()
{
    //do whatever
    ctx.SaveChanges();
}

Для простого доступа к репозиторию это было бы нормально, но что, если бы я хотел создать взаимодействие между двумя (или более) репозиториями внутри с помощью BLL...

public void SaveSomethingMoreComplex()
{
    //BLL here stuff like validation etc

    _userRepository.GetSomeData();
    _accountRepository.SaveSomeData(account);
    _userRepository.SaveSomeMore(user);

    // Probably should have one final save that affects both repositories???
    // Should be in a transaction scope also?
}

Было бы лучше использовать один и тот же экземпляр AppEntities для обоих репозиториев?

Кроме того, в этом примере окончательное сохранение, вероятно, должно быть в конце блока, а не иметь 2, как в моем примере, и часть транзакции?

Если я использую один и тот же экземпляр, то безопасно ли вводить его в конструктор репозиториев и поддерживать его в течение всего срока службы приложения, или есть ли какая-то причина, по которой примеры, которые я видел, имеют тенденцию создавать и удалять в пределах одного вызова метода ?

Спасибо за любую оказанную помощь.


person aqwert    schedule 22.06.2010    source источник


Ответы (2)


На самом деле это не такая уж необычная проблема при работе с шаблоном репозитория, и она сводится к тому, чтобы предоставить способ явного управления жизненным циклом вашей единицы работы (который в случае структуры сущностей является вашим контекстом).

Вы не указали, занимаетесь ли вы веб-разработкой или разработкой для Windows, но в контексте веб-разработки нередко устанавливается жизненный цикл вашей единицы работы для одного запроса. Поэтому, когда ваш запрос начинается, вы создаете свой контекст, а затем, когда он заканчивается, вы можете вызвать SaveChanges (или что-то еще для структуры сущностей), и это применит изменения ко всем сущностям, с которыми вы возились в ходе выполнения запрос.

В контексте Windows/сервиса вы, вероятно, захотите настроить какое-то явное управление жизненным циклом для вашего подразделения или работы, чтобы вы могли определить область действия UoW в зависимости от того, что вы делаете. Мне нравится метафора Разговор для переноса операций UoW, что означает, что я могу использовать что-то вроде этого:

using(Conversation.Start())
{
    // mess with the entities
} // Dispose on the object returned from Start will 
  // Save Changes and close the session

Конечно, это замалчивает некоторые элементы управления исключениями, которые вы хотели бы иметь, чтобы вы могли откатывать изменения в случае сбоя.

Что касается реализации, это зависит от вашей инфраструктуры. Обычно я использую контейнер IoC, поэтому у меня будет вызов Conversation.Start() создать для меня мою единицу работы и настроить IoC для возврата этого конкретного экземпляра, поэтому, когда я создаю свои репозитории, они получают текущую UoW. Вы также можете создать некоторые фабричные методы в беседе, чтобы вы могли получать экземпляры репозитория из беседы. Типа зависит от API, который вы хотите иметь в наличии.

Надеюсь это поможет.

person ckramer    schedule 22.06.2010
comment
Я надеялся использовать слои BLL и DAL для обоих, но я сначала начну с выигрышного приложения и посмотрю, как оно пойдет. - person aqwert; 23.06.2010
comment
Также я быстро закодировал что-то похожее на вашу концепцию разговора (хотя мне больше нравится термин «разговор»), который может включать управление транзакциями. Я определенно буду использовать IoC везде, где смогу. Спасибо - person aqwert; 23.06.2010
comment
Вы должны иметь возможность достаточно легко повторно использовать компоненты между winforms и ASP.Net, что является своего рода тем, для чего предназначен материал IRepository. Пока вы предоставляете интерфейсы для сервисов, с которыми работаете в BLL, вы должны быть в хорошей форме. - person ckramer; 23.06.2010
comment
Я следовал вашему подходу, когда я могу внедрить IUnitOfWork‹TContext› в репозитории, которые могут получить текущий экземпляр контекста. - person aqwert; 23.06.2010

Нет, вы не хотите, чтобы у каждой сущности был репозиторий. Вы хотите сгруппировать общие наборы функций в классы репозитория. Все функции учетной записи должны быть в одном репозитории. Это распространено в приложениях типа ASP.NET MVC, хотя я считаю, что это не лучший вариант на практике.

Если я собираюсь приложить усилия для добавления новых функций в репозиторий, то, что я, вероятно, захочу позже, — это сделать это доступным для самой сущности, поэтому я создаю частичные классы, которые содержат такого рода бизнес-логику. Таким образом, я могу сделать что-то вроде ShoppingCart.AddProduct(int id) и выполнить логику в частичном классе.

Другим распространенным сценарием является создание одноразовых моделей представлений. На самом деле это мое предпочтение.

http://blogs.msdn.com/b/dphill/archive/2009/01/31/the-viewmodel-pattern.aspx

Просто помните, что ваша сущность в Entity Framework уже является абстракцией источника данных или может отличаться от LINQ To SQL, где она представляет собой однозначное сопоставление источника данных.

person Paul Mendoza    schedule 22.06.2010
comment
Спасибо, я подумывал о размещении своей логики в частичном классе, но, возможно, я захочу использовать WCF и DTO, поэтому больше логики в службе может быть более подходящим. Я увижу, как я получаю больше в этом. - person aqwert; 23.06.2010