How to configure Ninject so that it would inject right instance depending on previously injected instance
Asked Answered
G

4

3

I can't find right words for my question so i will let my code speak instead.

I have Repository:

class Repository
{
    public Repository(DbContext ctx)
    {

    }
}

then i have this bindings:

Bind<IRepository>().To<Repository>();
Bind<DbContext>().To<UserStoreContext>().When...
Bind<DbContext>().To<CentralStoreContext>().When...

and then i have class that needs to access both db's

class Foo
{
    public Repository(IRepository userRepo, [CentralStoreAttribute]IRepository centralRepo)
    {

    }
}

How should i configure two DbContext bindings so that repositories with right contexts (based on CentralStoreAttribute) would be injected into Foo constructor?

Gilleod answered 16/6, 2011 at 13:59 Comment(1)
I realized that my approach wont work because when i request IRepository in request context, i get same one (first) no matter if I ask for one with different DbContext. So i revert all my code to use Paul Equis suggested way.Gilleod
B
0

Rather than relying on attributes in the right places, I usually create several types that are effectively just aliases. This is useful since with Ninject (and presumably other IoC containers) we're asking for dependencies by their type-name.

So if you need to be able to "request" a user repository vs a central one, I would create types that alias it like this:

interface IRepository {  /* methods and properties */ }
interface IUserRepository : IRepository {}
interface ICentralRepository : IRepository {}

class Foo
{
   public Foo(IUserRepository userRepo, ICentralRepository centralRepo)
   {
      // assign to fields
   }
}

I prefer this because then Ninject doesn't bleed into my app at all, it's more declarative, and I think it's simpler to remember than any convention based attribute approach like the one you're trying.

Brittani answered 16/6, 2011 at 15:29 Comment(7)
@Paul Equis thats good approach, but still if applied to my sample code, how to inject right DbContext ?Gilleod
@Paul Equis this is what you sugest if i understood correctly Bind<IRepository>().To<Repository>(); Bind<ICentralRepository>().To<Repository>(); Bind<DbContext>().To<UserStoreContext>().When... Bind<DbContext>().To<CentralStoreContext>().When... that still dosent solve problem of injecting right DbContextGilleod
Apply the same approach to the DbContext. Make one called IUserStoreContext and one called ICentralStoreContext and the repositories request the one they need.Brittani
@Paul Equis and how my repository class then should look? DbContext is not my type, it's framework's.Gilleod
Is DbContext sealed? If not, forget the interfaces and just inherit from it. Your repository constructor would look like this: UserRepository(UserStoreContext context) { /* assign stuff */ }Brittani
@Paul Equis i don't need 2 implementations of repositories for each context, i need only one, and if possible i don't want to couple it with any custom context derived from DbContext, i want "generic" repository implementation that coupled only with DbContextGilleod
@Paul Equis I was able to use you suggested concept. I created 2 interfaces as you suggested, also added conditions to DbContext bindings like .When(r => r.ParentRequest != null && r.ParentRequest.Service.Name == typeof(ICentralStoreRepository).Name)Gilleod
D
1

I tried this in a proof of concept but eventually went in a different direction.

Bind<IRepository>().ToMethod(x =>
{
  var repositoryType = x.Kernel
                .Get<IConfigObject>()
                .SomeStringPropertyDenotingTheRepository;

  switch (repositoryType )
  {
    case "1": return (IRepository)new Repository1();
    default: return (IRepository)new Repository2();
  }
}).InRequestScope();

While it worked, I never figured out if it was using my singleton instance of IObjectB or instantiating a new instance - should be pretty easy to figure out though. I figured it was calling ToMethod every time I used DI on IRepository - again not verified.

Denims answered 16/6, 2011 at 17:39 Comment(0)
G
1

Use the When(Func<IRequest, bool> condition) overload to check recursivly if r.Target.IsDefined(typeof(TAttribute), false) is true for the given request or one of its anchestors r.ParentRequest

Gage answered 16/6, 2011 at 19:0 Comment(3)
this works for constructor injections. But I also have places where i'm getting instance directly from kernel, like kernel.Get<IRepository>(...) what should i provide to Get method to get Repository with CentralStoreContext ?Gilleod
@qrow: The answer is pretty simple. Do not use Ninject as service locator and you do not have this problem.Gage
you mean stop getting instances directly from kernel? If so then i can't, i'm using framework that creates my types instead of me, so it needs parameter-less constructor. I don't know any other way then to get instances from kernel in that parameter-less constructor.Gilleod
H
0
Bind<IRepository>().To<Repository>();
Bind<DbContext>().To<CentralStoreContext>()
    .When( context => context.Target != null 
        && context.Target.GetCustomAttributes( typeof( CentralStoreAttribute ) ) != null );

// make the general binding after the more specific one
Bind<DbContext>().To<UserStoreContext>();
Haircloth answered 16/6, 2011 at 14:57 Comment(3)
this seems like it wont work? first GetCustomAttributes takes 2 parameters, and returns object[], but event if i change it to context.Target.GetCustomAttributes(typeof(CentralStoreAttribute), false).Any() it still looks for attribute at target that is DbContext but I specified attribute on IRepositoryGilleod
your example probably doing same thing as .WhenTargetHas() what i need is something like .WhenParentTargetHas()Gilleod
sorry, I have an extension method that does exactly that, and was typing from memory. mine looks like: context.Target.HasAttribute<CentralStoreAttribute>()Haircloth
B
0

Rather than relying on attributes in the right places, I usually create several types that are effectively just aliases. This is useful since with Ninject (and presumably other IoC containers) we're asking for dependencies by their type-name.

So if you need to be able to "request" a user repository vs a central one, I would create types that alias it like this:

interface IRepository {  /* methods and properties */ }
interface IUserRepository : IRepository {}
interface ICentralRepository : IRepository {}

class Foo
{
   public Foo(IUserRepository userRepo, ICentralRepository centralRepo)
   {
      // assign to fields
   }
}

I prefer this because then Ninject doesn't bleed into my app at all, it's more declarative, and I think it's simpler to remember than any convention based attribute approach like the one you're trying.

Brittani answered 16/6, 2011 at 15:29 Comment(7)
@Paul Equis thats good approach, but still if applied to my sample code, how to inject right DbContext ?Gilleod
@Paul Equis this is what you sugest if i understood correctly Bind<IRepository>().To<Repository>(); Bind<ICentralRepository>().To<Repository>(); Bind<DbContext>().To<UserStoreContext>().When... Bind<DbContext>().To<CentralStoreContext>().When... that still dosent solve problem of injecting right DbContextGilleod
Apply the same approach to the DbContext. Make one called IUserStoreContext and one called ICentralStoreContext and the repositories request the one they need.Brittani
@Paul Equis and how my repository class then should look? DbContext is not my type, it's framework's.Gilleod
Is DbContext sealed? If not, forget the interfaces and just inherit from it. Your repository constructor would look like this: UserRepository(UserStoreContext context) { /* assign stuff */ }Brittani
@Paul Equis i don't need 2 implementations of repositories for each context, i need only one, and if possible i don't want to couple it with any custom context derived from DbContext, i want "generic" repository implementation that coupled only with DbContextGilleod
@Paul Equis I was able to use you suggested concept. I created 2 interfaces as you suggested, also added conditions to DbContext bindings like .When(r => r.ParentRequest != null && r.ParentRequest.Service.Name == typeof(ICentralStoreRepository).Name)Gilleod

© 2022 - 2024 — McMap. All rights reserved.