c# - Unity Container - 具有通用工作统一性的多个数据库

标签 c# dependency-injection solid-principles

我在这里使用 EF6 的通用统一工作: https://genericunitofworkandrepositories.codeplex.com/

我有一个使用两个数据库的应用程序。我创建了额外的 UnitOfWork 接口(interface)和实现原始工作单元接口(interface)的类:

namespace Repository.Pattern.UnitOfWork
{
    public interface ILotteryBackOfficeUnitOfWorkAsync : IUnitOfWorkAsync
    {
    }
}

第二个数据库初始化的第二个工作单元类型:

namespace Repository.Pattern.Ef6
{
    public class LotteryBackOfficeUnitOfWork : UnitOfWork, ILotteryBackOfficeUnitOfWorkAsync
    {
        public LotteryBackOfficeUnitOfWork(IDataContextAsync dataContext)
            : base(dataContext)
        { }
    }
}

在统一中,我为不同的数据上下文注册了两个工作单元:

public static void RegisterTypes(IUnityContainer container)
{
    // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
    // container.LoadConfiguration();

    // TODO: Register your types here
    // container.RegisterType<IProductRepository, ProductRepository>();

    var purusLotteryConnectionString = WebConfigurationManager.ConnectionStrings["PurusLotteryContext"].ConnectionString;
    var purusLotteryBackOfficeConnectionString = WebConfigurationManager.ConnectionStrings["PurusLotteryBackOfficeContext"].ConnectionString;

    container.RegisterType<IDataContextAsync, PurusLotteryContext>(new InjectionConstructor(purusLotteryConnectionString));
    container.RegisterType<IUnitOfWorkAsync, UnitOfWork>(new HierarchicalLifetimeManager());

    container.RegisterType<IDataContextAsync, PurusLotteryBackOfficeContext>("LotteryBackOfficeContext", new InjectionConstructor(purusLotteryBackOfficeConnectionString));
    container.RegisterType<ILotteryBackOfficeUnitOfWorkAsync, LotteryBackOfficeUnitOfWork>(new HierarchicalLifetimeManager(),
        new InjectionConstructor(container.Resolve<IDataContextAsync>("LotteryBackOfficeContext")));

    container.RegisterType<IHomeService, HomeService>();
}

它有效,但这是正确的程序吗?

最佳答案

我看到的一个错误是您在注册阶段解决了。这不仅是危险的(因为是 explained 在不同的 DI 库的文档中),在你的情况下它会导致 PurusLotteryBackOfficeContext用作常量,因此作为 Singleton 注入(inject)到 LotteryBackOfficeUnitOfWork 中.换句话说,虽然这在开发过程中似乎可行,但实际上行不通,因为 DbContext can't be used as singleton .

相反,您应该尽可能多地使用 Unity 的 Autowiring 功能,否则 DI 库与构建对象图相比没有优势(只有劣势)by hand .

除此之外,您还违反了 Liskov Substitution Principle (LSP) 在您的设计中,这会给您的 DI 配置带来麻烦。您违反了 LSP,因为您对同一抽象有两个不兼容的实现。两者 PurusLotteryContextPurusLotteryBackOfficeContext实现 IDataContextAsync ,但它们是不兼容的,因为它们不能互换,因为它们都在完全不同的数据库模式上工作。尽管它们可能看起来共享相同的接口(interface),但它们并不共享相同的契约。看看当你注入(inject) PurusLotteryContext 时会发生什么进入一些需要与后台一起工作的类(class)。应用程序将中断,这意味着您违反了 LSP。

解决方案是为它们提供独立的抽象。乍一看,这似乎是一件很奇怪的事情,因为它们都有相同的方法。但是请记住,接口(interface)不仅仅是一组方法签名;一个接口(interface)描述了一个契约和行为,并且由于两个实现都在一个完全不同的数据库模式上工作,所以它们有一个完全不同的契约。当您将其分开时,您的代码将如下所示:

public class PurusLotteryContext : IPurusLotteryDataContextAsync { 
    public PurusLotteryContext(string conString) : base(conString) { }
}

public class LotteryUnitOfWork : ILotteryUnitOfWorkAsync {
    public LotteryUnitOfWork(IPurusLotteryDataContextAsync dc) { }
}

public class PurusLotteryBackOfficeContext : IPurusLotteryBackOfficeDataContextAsync { 
    public PurusLotteryBackOfficeContext(string conString) : base(conString) { }
}

public class LotteryBackOfficeUnitOfWork : ILotteryBackOfficeUnitOfWorkAsync {
    public LotteryBackOfficeUnitOfWork(IPurusLotteryBackOfficeDataContextAsync dc) { }
}

这允许您进行以下注册:

container.Register<IPurusLotteryDataContextAsync>(new HierarchicalLifetimeManager(),
    new InjectionFactory(c => new PurusLotteryContext(purusLotteryConnectionString)));

container.Register<IPurusLotteryBackOfficeDataContextAsync>(
    new HierarchicalLifetimeManager(),
    new InjectionFactory(c => new PurusLotteryBackOfficeContext(
        purusLotteryBackOfficeConnectionString)));

container.RegisterType<ILotteryUnitOfWorkAsync, LotteryUnitOfWork>(
    new HierarchicalLifetimeManager());
container.RegisterType<ILotteryBackOfficeUnitOfWorkAsync, LotteryBackOfficeUnitOfWork>(
    new HierarchicalLifetimeManager());

注意关于这个注册的一些事情:

  1. 数据上下文实现被注册为分层​​的,因为您通常需要 Entity Framework DbContext to have a 'per request' lifestyle .
  2. 与使用容器的 Autowiring 功能相比,数据上下文实现是使用工厂注册的。这是因为 Autowiring 对这些类没有帮助,工厂委托(delegate)不仅会使注册更简单,而且类型更安全(如果我们出错,编译器会帮助我们)。

关于c# - Unity Container - 具有通用工作统一性的多个数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33981713/

相关文章:

c# - 发布 ASP.NET 网站是否会带来任何额外的安全优势?

c# - 如何在输入键按下事件时将焦点移到数据 GridView 中的下一个单元格

c# - Entity Framework 和类

java - Jersey 2.0 的依赖注入(inject)

asp.net-mvc - Fluent 验证和 IoC(独特字段)

design-principles - 里氏替换原则 - 没有重写/虚拟方法?

java - 让 Java 代码更加通用

c# - 获取正则表达式中第一次出现的匹配项

unit-testing - 测试驱动开发和开放/封闭原则如何协同工作?

java - 依赖注入(inject) - Eclipse e4 应用程序到 eclipse 插件