具有多个有界DbContexts的简单注入器 - 异常"IDbContext已经注册"

AFD*_*AFD 5 entity-framework dependency-injection simple-injector

我正在尝试切换到Simple Injector Dependency Injection框架,因为我对它的速度印象深刻.

 private static void RegisterServices(Container container)
 {
     container.RegisterPerWebRequest<IDbContext, DbContext1>();
     ////container.RegisterPerWebRequest<IDbContext, DbContext2>();    
     container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>();
     container.RegisterPerWebRequest<IColourRepository, ColourRepository>();
Run Code Online (Sandbox Code Playgroud)

其中DbContext1和DbContext2继承自BaseDbContext类

public class BaseDbContext<TContext> : DbContext, IDbContext where TContext : DbContext
Run Code Online (Sandbox Code Playgroud)

它实现了一个相当简单的IDbContext接口(就像在SO上提供的许多接口),例如:

public interface IDbContext
{
    IQueryable<TEntity> Find<TEntity>() where TEntity : class;
    DbSet<TEntity> Set<TEntity>() where TEntity : class;
    int SaveChanges();
    void Dispose();
}
Run Code Online (Sandbox Code Playgroud)

如果我只使用一个DbContext类,它可以正常工作 - 存储库被注入,数据被拉出等.

但是,我还想在每个中使用带有少量DbSet的有界上下文(使用DDD有界上下文缩小EF模型),因为我的Code-First DbContext将包含数百个类

private static void RegisterServices(Container container)
 {
     container.RegisterPerWebRequest<IDbContext, DbContext1>();
     container.RegisterPerWebRequest<IDbContext, DbContext2>();

     container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>();
     container.RegisterPerWebRequest<IColourRepository, ColourRepository>();
Run Code Online (Sandbox Code Playgroud)

然后我得到一个例外:

System.InvalidOperationException未由用户代码处理HResult = -2146233079消息=类型IDbContext已经注册,并且容器当前未配置为允许覆盖注册.要允许覆盖当前注册,请将Container.Options.AllowOverridingRegistrations设置为true.Source = SimpleInjector StackTrace:SimpleInjector.Container.ThrowWhenTypeAlreadyRegistered(Type type),位于SimpleInjector.Container.AddRegistration(类型serviceType,注册注册),位于SimpleInjector.Container.Register [TService,TImplementation](Lifestyle lifestyle,String serviceTypeParamName,String implementationTypeParamName)at at SimpleInjector中的SimpleInjector.Container.Register [TService,TImplementation](生活方式生活方式).

如果我遵循这个建议:

container.Options.AllowOverridingRegistrations = true;
Run Code Online (Sandbox Code Playgroud)

然后DbContext2似乎覆盖DbContext1,例如DbSet"Color"在DbContext1中,它不再可访问:

Additional information: The entity type Colour is not part of the model for the current context.
Run Code Online (Sandbox Code Playgroud)

我应该如何使用Simple Injector和有界的DbContexts?

[UPDATE]

DbContexts没有直接在控制器中使用,它们是存储库的依赖关系,Simple Injector应该能够在构造函数中初始化

 public class ColoursController : ApiController
{
    private readonly IColourRepository _repository;
    private readonly ModelFactory _modelFactory;

    public ColoursController(IColourRepository repository)
    {
        _repository = repository;
        _modelFactory = new ModelFactory();
    }
Run Code Online (Sandbox Code Playgroud)

哪里

 public class ColourRepository : Repository<Colour>, IColourRepository
{
    public ColourRepository(IDbContext context) : base(context) { }
Run Code Online (Sandbox Code Playgroud)

ColourRepository期望DbContext1的具体实现,但是其他一些存储库需要DbContext2(具有不同的实体集)

我没有看到为什么不能为DbContext1和DbContext2使用IDbContext接口(或基类型)的原因.

Unity可以做到:

container.RegisterType<IDbContext, NorthwindContext>(new PerRequestLifetimeManager(), "NorthwindContext");
container.RegisterType<IDbContext, NorthwindCustomerContext>(new PerRequestLifetimeManager(), "NorthwindCustomerContext");
Run Code Online (Sandbox Code Playgroud)

Ninject可以做到.

Simple Injector提到CompositeLogger - 也许那个人可以做到这一点?

https://simpleinjector.org/

Ste*_*ven 6

ColourRepository期望DbContext1的具体实现,但是其他一些存储库需要DbContext2(具有不同的实体集)

您的设计目前含糊不清.虽然你的设计讲的是一个IDbContext,看起来如果只有一个抽象有两个实现,但这些实现是不可互换的(Liskov替换原则违规),这表明实际上应该有两个不同的接口.此外,拥有一个单一的界面会使您的DI配置更复杂,更难维护(这与您选择的框架无关).

因此,解决方案是通过为每个上下文提供自己的界面来消除设计中的歧义.这允许您的存储库依赖于它们所需的抽象:

public class ColourRepository : Repository<Colour>, IColourRepository
{
    public ColourRepository(ICustomerDbContext context) : base(context) { }
}
Run Code Online (Sandbox Code Playgroud)

这样可以简化注册:

container.Register<IDbContext, NorthwindContext>(Lifestyle.Scoped);
container.Register<ICustomerDbContext, NorthwindCustomerContext>(Lifestyle.Scoped);
Run Code Online (Sandbox Code Playgroud)

请注意,使用键控注册不能解决核心问题; 您仍然会被迫明确说明应该在哪个存储库中注入哪个键控版本,这将使您的DI配置成为维护噩梦并且非常容易出错.