IQueryable репозиторий с StructureMap (IoC) - Как я могу реализовать IDisposable? - PullRequest
5 голосов
/ 08 сентября 2010

Если у меня есть следующий репозиторий:

public IQueryable<User> Users()
{
   var db = new SqlDataContext();
   return db.Users;
}

Я понимаю, что соединение открывается только при запуске запроса:

public class ServiceLayer
{
   public IRepository repo;

   public ServiceLayer(IRepository injectedRepo)
   {
       this.repo = injectedRepo;
   }

   public List<User> GetUsers()
   {
       return repo.Users().ToList(); // connection opened, query fired, connection closed. (or is it??)
   }
}

Если это так, нужно ли мне делать так, чтобы мой репозиторий реализовывал IDisposable?

Метрики кода Visual Studio определенно считают, что я должен.

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

BTW - SqlDataContext - мой пользовательский класс, который расширяет класс ObjectContext Entity Framework (так что я могу иметь POCO стороны).

Итак, вопрос - действительно ли я должен реализовать IDisposable?

Если это так, я понятия не имею, как это возможно, поскольку каждый метод использует один и тот же экземпляр хранилища.

EDIT

Я использую Depedency Injection (StructureMap), чтобы внедрить конкретный репозиторий в сервисный уровень. За этим шаблоном следует стек приложений - я использую ASP.NET MVC, и конкретный сервис внедряется в контроллеры.

Другими словами:

  1. Пользователь запрашивает URL
  2. Создается экземпляр контроллера, который получает новый экземпляр ServiceLayer, который создается с новым экземпляром репозитория.
  3. Контроллер вызывает методы в службе (все вызовы используют один и тот же экземпляр репозитория)
  4. Как только запрос обслужен, контроллер исчезает.

Я использую гибридный режим для ввода зависимостей в мои контроллеры, что в соответствии с документацией StructureMap приводит к тому, что экземпляры хранятся в HttpContext.Current.Items.

Итак, я не могу этого сделать:

   using (var repo = new Repository())
   {
      return repo.Users().ToList();
   }

Поскольку это побеждает весь смысл DI.

Ответы [ 3 ]

3 голосов
/ 08 сентября 2010

Общий подход, используемый с nhibernate, состоит в том, чтобы создать ваш сеанс (ObjectContext) в begin_request (или каком-либо другом подобном событии жизненного цикла) и затем поместить его в end_request. Вы можете поместить этот код в HttpModule.

Вам необходимо изменить свой репозиторий, чтобы в него вставлялся ObjectContext. Ваш репозиторий должен прекратить управлять жизненным циклом ObjectContext.

3 голосов
/ 08 сентября 2010

Я бы сказал, что ты определенно должен. Если Entity Framework не обрабатывает соединения совсем иначе, чем LinqToSql (что я и использовал), вы должны реализовать IDisposable всякий раз, когда работаете с соединениями. Может быть верно, что соединение автоматически закрывается после успешного завершения вашей транзакции. Но что произойдет, если он не завершится успешно? Реализация IDisposable является хорошей гарантией того, что после того, как вы поработаете с ними, у вас не останется открытых соединений. Более простая причина заключается в том, что рекомендуется применять IDisposable.

.

Реализация может быть такой же простой, как если бы вы поместили это в свой класс репозитория:

public void Dispose()
{
    SqlDataContext.Dispose();
}

Затем, когда вы что-либо делаете со своим репозиторием (например, со своим сервисным уровнем), вам просто нужно обернуть все в предложение using. Вы также можете выполнить несколько операций «CRUD» в одном предложении using, поэтому вы можете распоряжаться им только тогда, когда закончите.

Обновление

В моем слое обслуживания (который я разработал для работы с LinqToSql, но, надеюсь, это применимо к вашей ситуации), я делаю каждый раз при создании нового хранилища. Чтобы обеспечить возможность тестирования, у меня есть проход инжектора зависимости в репозитории provider (вместо экземпляра репозитория). Каждый раз, когда мне нужен новый репозиторий, я заключаю вызов в оператор using, например:

using (var repository = GetNewRepository())
{
    ...
}


public Repository<TDataContext, TEntity> GetNewRepository()
{
    return _repositoryProvider.GetNew<TDataContext, TEntity>();
}

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

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

public void ExecuteAndSave(Action<Repository<TDataContext, TEntity>> action)
{
    using (var repository = GetNewRepository())
    {
        action(repository);
        repository.Save();
    }
}

action может быть серией действий CRUD или сложным запросом, но вы знаете, если вы позвоните ExecuteAndSave(), когда все это будет сделано, ваше хранилище будет правильно расположено.

2 голосов
/ 09 сентября 2010

РЕДАКТИРОВАТЬ - Советы, полученные от Ayende Rahien

Получил ответ по электронной почте от Ayende Rahien (из Rhino Mocks, Raven, Hibernating Rhinos).

Вот что он сказал:

Вы проблема в том, что вы инициализируете ваш контекст, как это: _genericSqlServerContext = new GenericSqlServerContext (new EntityConnection ( "Name = EFProfDemoEntities"));

Это означает, что контекст не владеть сущностью связи, что означает что он не располагает этим. В Вообще, это гораздо предпочтительнее иметь контекст создать подключение. Вы можете сделать это с помощью: _genericSqlServerContext = new GenericSqlServerContext ("name = EFProfDemoEntities");

Что определенно имеет смысл - однако я бы подумал, что удаление SqlServerContext также избавит от основного соединения, думаю, я ошибся.

В любом случае, это решение - теперь все утилизируется должным образом.

Так что мне больше не нужно делать , используя в хранилище:

public ICollection<T> FindAll<T>(Expression<Func<T, bool>> predicate, int maxRows) where T : Foo
        {
            // dont need this anymore
            //using (var cr = ObjectFactory.GetInstance<IContentRepository>())
            return _fooRepository.Find().OfType<T>().Where(predicate).Take(maxRows).ToList();

И в моем базовом репозитории я реализую IDisposable и просто делаю это:

Context.Dispose(); // Context is an instance of my custom sql context.

Надежда, которая помогает другим.

...