如果我有以下存储库:
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??)
}
}
如果是这种情况,我还需要使我的Repository实现IDisposable吗?
Visual Studio Code Metrics当然认为我应该这样做。
我正在使用IQueryable因为我将查询控制到我的服务层(过滤器,分页等),所以请不要就我使用它的事实进行架构讨论。
BTW - SqlDataContext 是我的自定义类,它扩展了Entity Framework的ObjectContext类(所以我可以拥有POCO派对)。
所以问题 - 我真的必须实现IDisposable吗?
如果是这样,我不知道这是怎么可能的,因为每个方法共享相同的存储库实例。
修改
我正在使用Depedency Injection(StructureMap)将具体的存储库注入服务层。这个模式跟随app堆栈 - 我正在使用ASP.NET MVC,并且具体服务被注入到控制器中。
换句话说:
我正在使用混合模式将依赖项注入到我的控制器中,根据StructureMap文档,这些实例会将实例存储在HttpContext.Current.Items中。
所以,我不能这样做:
using (var repo = new Repository())
{
return repo.Users().ToList();
}
因为这违背了DI的全部要点。
答案 0 :(得分:3)
我会说你绝对应该。除非实体框架处理连接的方式与LinqToSql(我一直在使用的方式)完全不同,否则在使用连接时应该实现IDisposable
。在事务成功完成后,连接可能会自动关闭。但如果没有成功完成会发生什么?实施IDisposable
是一个很好的保障,确保您在完成任务后没有任何连接。一个更简单的原因是,实施IDisposable
是最佳做法。
实现可以像在存储库类中一样简单:
public void Dispose()
{
SqlDataContext.Dispose();
}
然后,无论何时对存储库执行任何操作(例如,使用服务层),只需将所有内容都包含在using
子句中。您也可以在单个using
子句中执行多个“CRUD”操作,因此只有在完成所有操作后才能进行处理。
<强>更新强>
在我的服务层(我设计用于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()
,当它完成后,你的存储库将被正确处理。
答案 1 :(得分:3)
与nhibernate一起使用的常见方法是在begin_request(或其他类似的生命周期事件)中创建会话(ObjectContext),然后将其置于end_request中。您可以将该代码放在HttpModule中。
您需要更改存储库,以便注入ObjectContext。您的存储库应该不再管理ObjectContext生命周期。
答案 2 :(得分:2)
编辑 - 从Ayende Rahien收到的建议
收到Ayende Rahien(Rhino Mocks,Raven,Hibernating Rhinos成名)的电子邮件回复。
这就是他所说的:
你的问题是你初始化 你的上下文是这样的: _genericSqlServerContext = new GenericSqlServerContext(new EntityConnection( “名称= EFProfDemoEntities”));
这意味着上下文没有 拥有实体连接,这意味着 它没有处置它。在 一般来说,它是非常可取的 有上下文创建 连接。你可以通过使用: _genericSqlServerContext = new GenericSqlServerContext(“name = EFProfDemoEntities”);
哪个定义有意义 - 但是我会认为Disposing of 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.
希望能帮助他人。