简体   繁体   中英

Dependency Injection with Ninject share same objects with different instances

I've been gifted having had to work with an already set up Ninject DI based application which I have grown and added to considerably over the development of an application I'm working on.

I now find a problem that I would like to correct. I've managed to work around it using inheritance but would like a more cleaner solution.

I have two connections required to be injected into different services and repositories. I then need the repositories to also be correctly linked to the correct service having the same UnitOfWork .

I think I might be asking something that is not possible without inheritance and specialisation but that is why I am asking.

I managed to resolve this by creating a sub class of the main Repository and UnitOfWork classes but does nothing apart from implementing the base class. I just don't like the idea of a sub class that is fully dependant on the super class functionality with basically empty braces apart from constructor, to me this doesn't seem true OOP just to resolve this problem. So I sought for a better solution utilising a one class solution if possible in DI.

So if you can ignore the solution I have spoken about because I completely reverted the change this is what I am left with:

Looking at the code below you can see what is the objective.

...

public class UnitOfWork : IUnitOfWork
{
    private static readonly log4net.ILog log = log4net.LogManager.GetLogger("UnitOfWork");
    public DbContext DataContext { get; set; }

    public UnitOfWork(string connectionString)
    {
        DataContext = new DbContext(connectionString);
    }

    public void Commit()
    {
        ...
    }
}

...

public class Repository<T> : IRepository<T> where T : class
{
    public IUnitOfWork unitOfWork { get; set; }
    private readonly IDbSet<T> dbSet;

    //private static readonly log4net.ILog log = log4net.LogManager.GetLogger("Repository");

    public Repository(IUnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;
        dbSet = this.unitOfWork.DataContext.Set<T>();
    }
    ...
}
...

public class IPOPDataModules : NinjectModule
{
    public override void Load()
    {
    Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope().WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BE_TESTEntities"].ConnectionString);
    Bind<IRepository<tOrder>>().To<Repository<tOrder>>().InRequestScope();
    }
}

...

public class DataModules : NinjectModule
{
    public override void Load()
    {
    Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope().WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BAPSEntities"].ConnectionString);
    Bind<IRepository<Data.Quote>>().To<Repository<Data.Quote>>().InRequestScope();
    }
}

...

public class QuoteService : IQuoteService
{
    private IUnitOfWork unitOfWork;
    private IRepository<Data.Quote> quoteRepository;
    public QuoteService(IUnitOfWork unitOfWork, IRepository<Data.Quote> quoteRepository)
    {
        ...
    }
}

...

public class IPOPService : IIPOPService
{
    private IUnitOfWork unitOfWork;
    private IRepository<Data.tOrder> tOrderRepository;

    public IPOPService(IUnitOfWork unitOfWork, IRepository<Data.tOrder>)
    {
        ...
    }
}

What I want to know is, is it possible to share the same UnitOfWork and Repository objects by two different connections and have them injected as different instances to the respective services ( IPOPService for IPOP_BE_TEST connection, QuoteService for IPOP_BAP connection)

Again the code above doesn't achieve want I want but this is the sort of architecture I would like to play around to get this to work.

Your question is not completely clear for me. But check the documentation for the following two scopes, which might be interesting for your scenario.

  • InCallScope will result that only one instance will be created per resolution tree. I usually use this scope on desktop applications for a unit of work. See the documentation here . You'll need the Ninject.Extensions.NamedScope extension for this.
  • InRequestScope will result that in a web application, only one instance will be created per HTTP request. I usually use this scope for a unit of work. See the documentation here . You'll need the Ninject.Web.Common package for this.

What you're looking for are Ninject binding scopes. Whenever you declare a binding Ninject will provide a delegate to that binding that the activation process uses to determine if it should create a new instance of that service, or if it should return a previously constructed instance.

So, if you want to implement a singleton in Ninject, you simply declare a binding that looks like this:

Bind<IRepository<Data.Quote>>().To<Repository<Data.Quote>>().InSingletonScope();

InSingletonScope() and InRequestScope() are simply sugar (or in the case of InRequestScope an extension method) on IBindingInSyntax<T> for the InScope(Func<Ninject.Activation.IContext, object> scope) method though. Any time you want to ensure that Ninject returns the same instance of a service in a given situation, all you need to do is implement a custom scope.

If I understand your question correctly, you want to ensure that when a request hits your application the same instances of Repository<T> and IUnitOfWork will be injected into all the services in your application. In this case you would simply have to write bindings like this:

Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope().WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BE_TESTEntities"].ConnectionString);
Bind<IRepository<tOrder>>().To<Repository<tOrder>>().InRequestScope();

However, your problem appears to be that you have two separate modules, with two separate bindings. I would suggest that you need to use a single module with contextual binding to determine which connection string should be provided to which part of the system. So your one module might look like this:

Bind<IUnitOfWork>()
    .To<UnitOfWork>()
    .WhenInjectedInto<IIPOPService>()
    .InRequestScope()
    .WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BE_TESTEntities"].ConnectionString);
Bind<IUnitOfWork>()
    .To<UnitOfWork>()
    .WhenInjectedInto<IQuoteService>()
    .InRequestScope()
    .WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BAPSEntities"].ConnectionString);

Bind<IRepository<tOrder>>().To<Repository<tOrder>>().InRequestScope();

This way you can be sure that when Ninject is resolving IIPOPService it will create an instance of UnitOfWork initialized with the "IPOP_BE_TESTEntities" connection string, and when resolving IQuoteService , it will use the "IPOP_BAPSEntities" connection string, but otherwise, across that request scope, only a single instance will be constructed by Ninject.

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM