【问题标题】:Disposal of dependencies when using DI/abstract factory pattern使用 DI/抽象工厂模式时的依赖处理
【发布时间】:2012-06-28 09:57:29
【问题描述】:

在下面的简化示例中,我有一个我认为以相当合理的方式定义的 DataContext 和 Repository:

public interface IUnitOfWork 
{
   int SaveChanges();
}

public class DataContext : DbContext, IUnitOfWork 
{
    public DbSet<Car> Cars { get ; set; }
}

public interface ICarsRepository 
{
    Car Find(int id);
    void Add(Car car);
}

public class SqlCarsRepository : ICarsRepository 
{
    private DataContext  _context;
    public SqlCarsRepository(DataContext context)
    {
       _context = context;
    }

    public Car Find(int id) 
    {
        return _context.Cars.Find(id);
    }

    //etc
}

我正在努力研究如何使用 DI 和抽象工厂模式来实现我想要的。在 MVC 应用程序中,这很容易设置 - 控制器需要在其构造函数中实现 IUnitOfWork 和 ICarsRepository 的实例。我可以配置容器以使用不同的控制器工厂为每个 Http 请求提供相同的 DataContext 实例。不知何故,这里的一次性依赖项似乎被正确处理了。

但是,我想在 Windows 服务中使用相同的存储库。这是多线程的,每个线程在启动时都需要访问自己的存储库,并且每个线程都应该有自己的 DataContext / UnitOfWork。但我不知道该怎么做:

  • 应用程序的复合根是服务启动时,因此无法为每个线程解析依赖关系(线程按需启动)。
  • 我不确定如何使用抽象工厂模式。线程需要 IUnitOfWork 和 ICarsRepository 的实例,但共享相同的 DataContext。我可以创建一个抽象工厂来在一次调用中创建它们,并将其传递到线程中,但是我不知道如何处理 DataContext。我不希望线程必须关心对 ICarsRepository 实现的依赖项是一次性的。我绝对不希望线程知道 ICarsRepository 依赖于 DataContext,因为那样拥有接口似乎毫无意义——线程可能只依赖于 SqlCarsRespository。
  • 我不想让 SqlCarsRepository 成为一次性的并让它处置 DataContext,因为可能有其他人在使用 DataContext,而它一开始并没有创建它。
  • 我认为我可以创建一个 CarsService 来隐藏 IUnitOfWork 和 ICarsRepository(并使用抽象工厂获取其实例),但我仍然不知道如何处理 DataContext 依赖项

做我正在尝试的最好方法是什么?

【问题讨论】:

  • 您使用的是什么 DI 容器,还是您手动操作?
  • @Steven:我正在使用温莎城堡

标签: c# entity-framework dependency-injection repository-pattern abstract-factory


【解决方案1】:

因此无法为每个线程解决依赖关系

他们可以,而且事实上,他们应该这样做。您应该在每个线程的开头解析一个新的对象图。不这样做,意味着您只能使用线程安全的依赖项,而您的情况并非如此。

我不确定如何使用抽象工厂模式

我认为,首先,尝试将您的 DataContext 定义为 MVC 应用程序中的 Per Web Request 和 Windows 服务中的 Per Lifetime Scope(或您使用的容器中可用的任何内容)。在您的 Windows 服务中,每个线程都将获得自己的生命周期范围。定义范围通常允许您在范围结束时释放实例。

我不希望线程必须关心 ICarsRepository 实现的依赖项是一次性的

你的线程应该关心这个,但你的业务逻辑不应该。启动新线程时,您必须拥有一些允许启动和结束范围以及解析和使用图的根类型的基础架构代码。此代码应该是您的组合根的一部分,因此应用程序的其余部分应该忽略这一点。如果您使用 Per Lifetime Scope(或其他显式生命周期)注册了某些类型,您的容器将知道何时处置这些实例。基础设施代码只需要告诉容器,作用域已经结束。

我不想让 SqlCarsRepository 一次性

SqlCarsRepository 应该依赖于一个没有实现IDisposable 的接口,在这种情况下没有什么可以处理的。它应该是负责处理DataContext 的容器,并且通过正确的注册您可以做到这一点。

做我正在尝试的最好方法是什么?

你的设计听起来很合理,但这里有一些其他的 SO 问题,可能会给你更多的工作:

【讨论】:

  • 谢谢,这真的很有帮助。所以我可以在每个线程启动时为它创建一个完整的隔离对象图。但是主线程上的依赖关系规则将非常不同,因为它们在这个基于请求的模型之外。拥有两个 IoC 容器实例并为主线程的图形配置一个,然后为线程配置另一个实例是不是很糟糕?
  • Windows 服务将是与您的 MVC 网站不同的应用程序。这将是一个单独的进程,并且(当然)将拥有自己的容器实例。但不仅如此,由于技术不同,这个容器会有不同的配置。例如,在您的 Windows 服务中基于每个 Web 请求注册实例完全没有意义。但是,在同一个应用程序域中拥有多个容器实例是不好的。那会使一切变得非常复杂。使用该单个容器在线程/请求的开头请求一个新的对象图。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多