Question

I am trying to switch to Simple Injector Dependency Injection framework as I am impressed with its speed.

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

where DbContext1 and DbContext2 inherit from a BaseDbContext class

public class BaseDbContext<TContext> : DbContext, IDbContext where TContext : DbContext

which implements a rather simple IDbContext interface (like the many offered on SO), e.g:

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

If I use just a single DbContext class, it works fine - repositories get injected, data pulled etc.

However, I'd also like to use bounded contexts with a smaller number of DbSets in each of them (Shrink EF Models with DDD Bounded Contexts) as my Code-First DbContext would otherwise include hundreds of classes

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

     container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>();
     container.RegisterPerWebRequest<IColourRepository, ColourRepository>();

Then I get an exception:

System.InvalidOperationException was unhandled by user code HResult=-2146233079 Message=Type IDbContext has already been registered and the container is currently not configured to allow overriding registrations. To allow overriding the current registration, please set the Container.Options.AllowOverridingRegistrations to true. Source=SimpleInjector StackTrace: at SimpleInjector.Container.ThrowWhenTypeAlreadyRegistered(Type type) at SimpleInjector.Container.AddRegistration(Type serviceType, Registration registration) at SimpleInjector.Container.Register[TService,TImplementation](Lifestyle lifestyle, String serviceTypeParamName, String implementationTypeParamName) at SimpleInjector.Container.Register[TService,TImplementation](Lifestyle lifestyle) at SimpleInjector.SimpleInjectorWebExtensions.RegisterPerWebRequest[TService,TImplementation](Container container)

If I follow the suggestion:

container.Options.AllowOverridingRegistrations = true;

then DbContext2 seems to override DbContext1, e.g. DbSet "Colour" is in DbContext1 and it is not accessible any more:

Additional information: The entity type Colour is not part of the model for the current context.

How should I use Simple Injector and bounded DbContexts together?

[UPDATE]

The DbContexts are no used in controllers directly, they are dependencies of repositories which Simple Injector should be able to initialise in constructors

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

    public ColoursController(IColourRepository repository)
    {
        _repository = repository;
        _modelFactory = new ModelFactory();
    }

where

 public class ColourRepository : Repository<Colour>, IColourRepository
{
    public ColourRepository(IDbContext context) : base(context) { }

ColourRepository expects a concrete implementation of DbContext1, but some other repository would need DbContext2 (with a different set of entities)

I do not see the reason why it is impossible to use IDbContext interface (or a base type) for both DbContext1 and DbContext2.

Unity can do it:

container.RegisterType<IDbContext, NorthwindContext>(new PerRequestLifetimeManager(), "NorthwindContext");
container.RegisterType<IDbContext, NorthwindCustomerContext>(new PerRequestLifetimeManager(), "NorthwindCustomerContext");

Ninject can do it.

Simple Injector mentions CompositeLogger - maybe that one could do the trick?

https://simpleinjector.org/

Was it helpful?

Solution

ColourRepository expects a concrete implementation of DbContext1, but some other repository would need DbContext2 (with a different set of entities)

Your design is currently ambiguous. Although your design speaks about an IDbContext and it looks like if there's just one abstraction with two implementations, but those implementations are not interchangeable (a Liskov Substitution principle violation) which is an indication that there should in fact be two different interfaces. Besides, having one single interface makes your DI configuration more complicated and harder to maintain (this is independent on the framework you pick).

So the solution is to remove the ambiguity from your design by giving each context its own interface. This allows your repositories to take the dependency on the abstraction they need:

public class ColourRepository : Repository<Colour>, IColourRepository
{
    public ColourRepository(ICustomerDbContext context) : base(context) { }
}

And this allows you to simplify the registration:

container.Register<IDbContext, NorthwindContext>(Lifestyle.Scoped);
container.Register<ICustomerDbContext, NorthwindCustomerContext>(Lifestyle.Scoped);

Note that using keyed registrations won't solve the core problem; you'll still forced to explicitly state which keyed version should be injected in which repository, which will make your DI configuration a maintenance nightmare and very error prone.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top