5

如果我有以下存储库:

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 Code Metrics 当然认为我应该这样做。

我使用 IQueryable 是因为我将查询控制权交给了我的服务层(过滤器、分页等),所以请不要就我使用它的事实进行架构讨论。

顺便说一句 - SqlDataContext是我的自定义类,它扩展了实体框架的 ObjectContext 类(所以我可以有 POCO 方)。

所以问题 - 我真的必须实施 IDisposable 吗?

如果是这样,我不知道这是怎么可能的,因为每个方法共享相同的存储库实例。

编辑

我正在使用依赖注入(StructureMap)将具体存储库注入服务层。这种模式沿应用程序堆栈向下 - 我正在使用 ASP.NET MVC,并将具体服务注入到控制器中。

换句话说:

  1. 用户请求网址
  2. 控制器实例被创建,它接收一个新的 ServiceLayer 实例,该实例是用一个新的 Repository 实例创建的。
  3. 控制器调用服务上的方法(所有调用都使用相同的存储库实例)
  4. 一旦请求得到服务,控制器就消失了。

我正在使用混合模式将依赖项注入到我的控制器中,根据 StructureMap 文档,这会导致实例存储在 HttpContext.Current.Items 中。

所以,我不能这样做:

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

因为这破坏了 DI 的全部意义。

4

3 回答 3

3

我会说你绝对应该。除非 Entity Framework 处理连接的方式与 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(),当这一切都完成后,您的存储库将被妥善处理。

于 2010-09-08T07:16:51.970 回答
3

与 nhibernate 一起使用的一种常见方法是在 begin_request(或其他类似的生命周期事件)中创建会话 (ObjectContext),然后在 end_request 中将其处理掉。您可以将该代码放在 HttpModule 中。

您需要更改您的存储库,以便它注入 ObjectContext。您的存储库应该脱离管理 ObjectContext 生命周期的业务。

于 2010-09-08T12:56:51.940 回答
2

编辑 - 从 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.

希望能帮助别人。

于 2010-09-09T02:06:28.253 回答