Aru*_*K V 2 c# asp.net-mvc dependency-injection simple-injector
在我的项目中,我有多个数据库上下文,因此我创建了一个提供程序,用于根据需要获取上下文对象。我的提供者看起来像这样。
public class DbContextProvider
{
public Func<AccountingContext> AccountingDbContextResolver { get; set; }
public Func<ActiveDirectryContext> ActiveDirectryDbContextResolver { get; set; }
public AccountingContext GetAccountingDbContext() =>
this.AccountingDbContextResolver();
public ActiveDirectryContext GetActiveDirectryDbContext() =>
this.ActiveDirectryDbContextResolver();
}
Run Code Online (Sandbox Code Playgroud)
ServiceBase我为获取提供者而创建的一类
public class ServiceBase
{
public ServiceBase(DbContextProvider contextProvider)
{
this.ContextProvider = contextProvider;
}
protected DbContextProvider ContextProvider { get; }
public AccountingContext AccountingDbContext =>
this.ContextProvider.GetAccountingDbContext();
public ActiveDirectryContext ActiveDirectryDbContext =>
this.ContextProvider.GetActiveDirectryDbContext();
}
Run Code Online (Sandbox Code Playgroud)
我正在使用简单的注入器,我需要创建两个数据库上下文的实例。
为了获取实例,我使用委托创建了两个静态方法
private static DbContextProvider CreateActiveDirectryDbContextProvider(Container container)
{
return new DbContextProvider
{
ActiveDirectryDbContextResolver =
() => container.GetInstance<ActiveDirectryContext>();
};
}
private static DbContextProvider CreateAccountingDbContextProvider(Container container)
{
return new DbContextProvider
{
AccountingDbContextResolver = () => container.GetInstance<AccountingContext>();
};
}
Run Code Online (Sandbox Code Playgroud)
对于注册,我使用了以下代码
var accountingProvider = CreateAccountingDbContextProvider(container);
container.RegisterInstance(accountingProvider);
var activeDirectryProvider = CreateAccountingDbContextProvider(container);
container.RegisterInstance(activeDirectryProvider);
Run Code Online (Sandbox Code Playgroud)
如果我运行代码,则会收到如下错误
System.InvalidOperationException: '类型 DbContextProvider 已经注册。如果您打算解析 DbContextProvider 实现的集合,请使用 Container.Collection.Register 重载。有关更多信息,请参阅 https://simpleinjector.org/coll1。如果您打算用此新注册替换现有注册,您可以通过将 Container.Options.AllowOverridingRegistrations 设置为 true 来允许覆盖当前注册。有关更多信息,请参阅https://simpleinjector.org/ovrrd。
但是当我只尝试使用一种上下文时一切正常,
var accountingProvider = CreateAccountingDbContextProvider(container);
container.RegisterInstance(accountingProvider);
Run Code Online (Sandbox Code Playgroud)
我尝试使用 注册它container.Collection.Register,没有出现错误,但我没有在服务层中获取实例,我在那里收到空引用异常。
有人可以帮我解决这个问题吗?
您只有一种类型 ( DbContextProvider) 负责构造AccountingContext和ActiveDirectryContext。从这个角度来看,创建两个DbContextProvider每个都部分初始化的实例真的很奇怪。消费者不会期望GetAccountingDbContext()返回 null 或抛出NullReferenceException。因此,您应该创建一个可用于两种情况的实例:
container.Register<ActiveDirectryContext>(Lifestyle.Scoped);
container.Register<AccountingContext>(Lifestyle.Scoped);
container.RegisterInstance<DbContextProvider>(new DbContextProvider
{
ActiveDirectryDbContextResolver = () => container.GetInstance<ActiveDirectryContext>(),
AccountingDbContextResolver = () => container.GetInstance<AccountingContext>()
});
Run Code Online (Sandbox Code Playgroud)
或者更好,使DbContextProvider不可变:
container.RegisterInstance<DbContextProvider>(new DbContextProvider(
activeDirectryDbContextResolver: () => container.GetInstance<ActiveDirectryContext>(),
accountingDbContextResolver: () => container.GetInstance<AccountingContext>()));
Run Code Online (Sandbox Code Playgroud)
这解决了这个问题,因为DbContextProvider. 这消除了歧义,防止了可能的错误,并且是一个更简单的解决方案。
但是,虽然这可行,但我想建议对您的设计进行一些更改。
首先,你应该摆脱ServiceBase基类。尽管基类看起来还不错,但当它们开始获得自己的依赖项时,它们可能会开始违反单一职责原则及其派生的依赖倒置原则和开闭原则:
相反,不要使用基类,而是执行以下操作:
当您遵循此建议时,您最终会得到一组(派生的)服务类,它们依赖于一个只不过是一个空壳的基类。这是您可以完全删除基类的时候。您现在实现的是Composition over Inheritance。与继承相比,组合通常会导致更易于维护的系统。
正如 JoostK 提到的,您还可以将DbContext类型直接注入消费者。但是,您是否要这样做取决于您决定使用的合成模型的类型。这意味着,当您选择应用闭包组合模型时,您通常应该将DbContext实现直接注入您的使用者。
container.Register<ActiveDirectryContext>(Lifestyle.Scoped);
container.Register<AccountingContext>(Lifestyle.Scoped);
container.RegisterInstance<DbContextProvider>(new DbContextProvider
{
ActiveDirectryDbContextResolver = () => container.GetInstance<ActiveDirectryContext>(),
AccountingDbContextResolver = () => container.GetInstance<AccountingContext>()
});
Run Code Online (Sandbox Code Playgroud)
这简化了系统的注册,因为现在您只需注册DbContext实现即可:
container.RegisterInstance<DbContextProvider>(new DbContextProvider(
activeDirectryDbContextResolver: () => container.GetInstance<ActiveDirectryContext>(),
accountingDbContextResolver: () => container.GetInstance<AccountingContext>()));
Run Code Online (Sandbox Code Playgroud)
你的电流DbContextProvider似乎是围绕Ambient Composition Model设计的。两种合成模型各有优势,您可能特意选择了环境合成模型。
然而,仍然DbContextProvider公开了许多 (10) 个属性——每个DbContext. 具有许多方法的类和抽象可能会导致许多有关可维护性的问题。这源于规定了狭义抽象的接口隔离原则。因此,不要注入一个可以访问单一DbContext类型的广泛提供程序实现。实现通常只需要访问单一DbContext类型。如果他们需要多个班级,几乎肯定应该将班级分成更小、更集中的班级。
所以你可以做的是创建一个允许访问单一DbContext类型的通用抽象:
public class ProduceIncomeTaxService : IHandler<ProduceIncomeTax>
{
private readonly AccountingContext context;
// Inject stateful AccountingContext directly into constructor
public ProduceIncomeTaxService(AccountingContext context) => this.context = context;
public void Handle(ProduceIncomeTax command)
{
var record = this.context.AccountingRecords
.Single(r => r.Id == command.Id);
var tax = CalculateIncomeTax(record);
FaxIncomeTax(tax);
this.context.SaveChanges();
}
...
}
Run Code Online (Sandbox Code Playgroud)
在消费者中使用时,这将如下所示:
container.Register<ActiveDirectryContext>(Lifestyle.Scoped);
container.Register<AccountingContext>(Lifestyle.Scoped);
// Of course don't forget to register your service classes.
Run Code Online (Sandbox Code Playgroud)
有多种实现方式IDbContextProvider<T>,但例如,您可以创建一个直接依赖于 Simple Injector 的实现:
public interface IDbContextProvider<T> where T : DbContext
{
T Context { get; }
}
Run Code Online (Sandbox Code Playgroud)
这个类使用注入Container来InstanceProducer为给定DbContext类型提取正确的注册。如果未注册,则会引发异常。该InstanceProducer则用来获取DbContext时Context被调用。
由于此类依赖于 Simple Injector,因此它应该是您的Composition Root 的一部分。
您可以按如下方式注册:
public class ProduceIncomeTaxService : IHandler<ProduceIncomeTax>
{
private readonly IDbContextProvider<AccountingContext> provider;
// Inject an ambient context provider into the constructor
public ProduceIncomeTaxService(IDbContextProvider<AccountingContext> provider)
=> this.provider = provider;
public void Handle(ProduceIncomeTax command)
{
var record = this.provider.Context.AccountingRecords
.Single(r => r.Id == command.Id);
var tax = CalculateIncomeTax(record);
FaxIncomeTax(tax);
this.provider.Context.SaveChanges();
}
...
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
957 次 |
| 最近记录: |