注入存储库上的IDisposable

时间:2012-04-03 14:54:21

标签: c# ado.net dependency-injection idisposable

我有以下ADO .Net存储库

public class Repository : IRepository, IDisposable
{
   private readonly IUnitOfWork UnitOfWork;
   private SqlConnection Connection;

   public Repository(IUnitOfWork unitOfWork, connectionString)
   {
      UnitOfWork = unitOfWork;
      Connection = new SqlConnection(connectionString);
      Connection.Open();
   }

   public MyObject FindBy(string userName)
   {
      //...Ado .Net command.ExecuteReader, etc.
   }
}

此存储库向域服务注入IoC容器,并按如下方式使用:

public class UserDomainService : IUserDomainService
{
   private readonly IRepository Repository;

   public UserDomainService(IRepository repository)
   {
      Repository = repository;
   }

   public User CreateNewUser(User user)
   {
      using(Repository)
      {
         var user = Repository.FindBy(user.UserName);
         if(user != null)
            throw new Exception("User name already exists!");

         Repository.Add(user);
         Repository.Commit();
      }
   }
}

我的想法是我总是将Repository对象放在using语句中,所以当它完成时,连接被关闭并被处理掉,但是我认为它是一个问题,因为Domain Service类仍然存在并且如果有一个调用它,它将失败,因为存储库已被销毁。

现在我可以完全控制所有代码,我只想设计粗粒度服务调用,但是整个事情都有些不适合。

我这样做是为了避免域名服务知道存储库中的OpenConnection和CloseConnection方法。

这种设计本质上是不好的还是有更好的方法呢?

经过思考:当请求到达时,所有依赖关系树都在WCF级别生成,当然,您可以看到连接在此时打开,因为它发生在存储库的构造函数中所以我认为这并不是那么糟糕,因为它只在这个特定电话的持续时间内开放。我在这个假设上是正确的,还是我在这个过程中尽早打开数据库连接,做得非常糟糕?

2 个答案:

答案 0 :(得分:10)

注入一个工厂,创建您需要的实例,而不是实例本身。

选择IRepositoryFactory以便您可以创建IRepository并在每次使用时将其丢弃。这样,域服务或工厂都不需要是一次性的。此外,重要的是,通过仍然注入实现而不是硬编码来保持代码抽象。

public class UserDomainService : IUserDomainService
{
   private readonly IRepositoryFactory RepositoryFactory;

   public UserDomainService(IRepositoryFactory factory)
   {
      RepositoryFactory = factory;
   }

   public User CreateNewUser(User user)
   {
      using (IRepository repository = RepositoryFactory.Create())
      {
         var user = repository.FindBy(user.UserName);
         if(user != null)
            throw new Exception("User name already exists!");

         repository.Add(user);
         repository.Commit();
      }
   }
}

您并不总是需要注入所需的类型。阅读Castle Windsor(其心态是注册 - 解析 - 释放),您会发现如果您想在应用程序生命周期的不确定时间解析内容,建议使用Type Factories。

您知道您需要一个存储库,但时不知道。而不是要求存储库,请求创建它们的东西。因此保持了抽象级别,并且您没有泄露任何实现。

答案 1 :(得分:1)

您遇到的问题是所有权问题。 UserDomainService类不会创建IRepository,但它仍然需要该实例的所有权,因为它会处置它。

一般规则是创建对象的人应该破坏它。换句话说,创建对象的人是所有者,所有者应该销毁该对象。

您的问题有两种解决方案。

  1. 创建一个IRepositoryFactory,正如亚当清楚解释的那样。这样一个工厂的CreateNewRepository()方法会清楚地告知调用者获得所有权并应该处置创建的存储库。

  2. 让创建(并注入)该存储库的人处理该存储库的处理。您可以在WCF服务中手动执行此操作,也可以使用IoC / DI框架。如果您使用DI框架,您应该查看每Web请求生命周期或类似内容。

  3. 最后请注意,您的IRepository实施IDisposable。选择解决方案2时,您可以从IDisposable中删除IRepository接口,这会隐藏应用程序中涉及资源的事实。从应用程序中隐藏IDisposable是一件好事,因为该接口是一个漏洞的抽象。您已经遇到过此问题,因为从应用程序中调用Dispose会破坏整个应用程序。