IQueryable репозиторий с StructureMap (IoC) - Как я могу реализовать IDisposable?
Если у меня есть следующий репозиторий:
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, потому что я даю контроль над запросами моему сервисному слою (фильтрам, подкачке страниц и т. Д.), Поэтому, пожалуйста, не обсуждайте архитектуру с тем фактом, что я его использую.
КСТАТИ - SqlDataContext это мой пользовательский класс, который расширяет класс ObjectContext Entity Framework (так что я могу иметь POCO стороны).
Итак, вопрос - действительно ли я должен реализовать IDisposable?
Если это так, я понятия не имею, как это возможно, так как каждый метод использует один и тот же экземпляр репозитория.
РЕДАКТИРОВАТЬ
Я использую Depedency Injection (StructureMap), чтобы внедрить конкретный репозиторий в сервисный слой. За этим шаблоном следует стек приложений - я использую ASP.NET MVC, и конкретный сервис внедряется в контроллеры.
Другими словами:
- Пользователь запрашивает URL
- Создается экземпляр контроллера, который получает новый экземпляр ServiceLayer, который создается с новым экземпляром репозитория.
- Контроллер вызывает методы в службе (все вызовы используют один и тот же экземпляр Repository)
- Как только запрос обслужен, контроллер пропал.
Я использую гибридный режим для внедрения зависимостей в мои контроллеры, которые в соответствии с документацией StructureMap заставляют экземпляры храниться в HttpContext.Current.Items.
Итак, я не могу сделать это:
using (var repo = new Repository())
{
return repo.Users().ToList();
}
Как это побеждает весь смысл DI.
3 ответа
РЕДАКТИРОВАТЬ - Советы, полученные от 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.
Надеюсь, это поможет другим.
Общий подход, используемый с nhibernate, состоит в том, чтобы создать ваш сеанс (ObjectContext) в begin_request (или каком-либо другом подобном событии жизненного цикла) и затем поместить его в end_request. Вы можете поместить этот код в HttpModule.
Вам нужно изменить свой репозиторий, чтобы в него вставлялся ObjectContext. Ваш репозиторий должен выйти из бизнеса управления жизненным циклом ObjectContext.
Я бы сказал, что ты определенно должен. Если Entity Framework не обрабатывает соединения совсем иначе, чем LinqToSql (что я и использовал), вы должны реализовать IDisposable
всякий раз, когда вы работаете с соединениями. Возможно, это правда, что соединение автоматически закрывается после успешного завершения транзакции. Но что произойдет, если он не завершится успешно? Внедрение IDisposable
является хорошей гарантией того, что после того, как вы покончили с ними, у вас не останется открытых соединений. Более простая причина в том, что это лучшая практика для реализации IDisposable
,
Реализация может быть такой же простой, как помещение этого в ваш класс репозитория:
public void Dispose()
{
SqlDataContext.Dispose();
}
Затем, когда вы что-либо делаете со своим репозиторием (например, со своим сервисным уровнем), вам просто нужно обернуть все в using
пункт. Вы можете сделать несколько операций "CRUD" в течение одного using
пункт, так что вы можете распоряжаться только тогда, когда вы все сделали.
Обновить
В моем слое обслуживания (который я разработал для работы с LinqToSql, но, надеюсь, это применимо к вашей ситуации), я каждый раз создаю новый репозиторий. Чтобы обеспечить возможность тестирования, у меня есть проход инжектора зависимостей в провайдере репозитория (вместо экземпляра репозитория). Каждый раз, когда мне нужен новый репозиторий, я помещаю вызов в 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()
Когда все это будет сделано, ваш репозиторий будет расположен правильно.