Search code examples
ninjectninject-2

Using WithConstructorArgument and creating bound type


I have a binding that looks like this:

kernel.Bind<IRepository<Holiday>>().To<RepositoryBase<Holiday>>();

The problem is that RepositoryBase takes a contructor paramter of UnitOfWork called context. This is not, in and of itself a problem. Ninject should resolve it. Except for the fact that I have two UnitOfWork implementations, both bound using an attribute discriminator.

kernel.Bind<IUnitOfWork>().To<MS_DevEntities>().WhenTargetHas<MsDataAttribute>()
    .InRequestScope();

How can specify that when an IRepository is created, it should be created with MS_DevEntities?

Certainly, i could do something like this:

kernel.Bind<IRepository<Holiday>>().To<RepositoryBase<Holiday>>()
    .WithConstructorArgument("context", new MS_DevEntities());

However, I would prefer to have Ninject create the instance, particularly because i'm using the .InRequestScope() lifecycle.

Any suggestions?

EDIT:

Previously, I was using a class that looked like this:

public class HolidayRepository : RepositoryBase<Holiday>, IHolidayRepository
{
    public HolidayRepository([MsData]IUnitOfWork context) : base(context){}
}

However, I now find myself with several dozen of these and they don't do much but add extra work when I need to create a new repository. I'd like to just map these directly in the bindings.

EDIT2:

I suppose I could do something like this, but it seems kind of hackish. Is there a better way?

kernel.Bind<MS_DevEntities>().ToSelf().InRequestScope();
kernel.Bind<IRepository<Holiday>>().To<RepositoryBase<Holiday>>()
    .WithConstructorArgument("context", 
        (context) => context.Kernel.Get<MS_DevEntities>());

Solution

  • E.g. Put the attribute to the entity and use something like this:

    kernel.Bind(typeof(IRepository<>)).To(typeof(RepositoryBase<>));
    kernel.Bind<IUnitOfWork>().To<MS_DevEntities>()
          .When(r => EntityHas<MsData>(r));
    kernel.Bind<IUnitOfWork>().To<TheOtherOne_DevEntities>()
          .When(r => EntityHas<TheOtherData>(r));
    
    bool EntityHas<TAttribute>(IRequest r)
    {
        return r.Target.Member.ReflectedType.IsGenericType &&
               r.Target.Member.ReflectedType.GetGenericArguments()[0]
                   .GetCustomAttributes(typeof(TAttribute), false).Any();
    }
    

    You can do everything using this When condition and take the entity type from r.Target.Member.ReflectedType.GetGenericArguments()[0] to lookup somewhere which UoW that you have to use for this entity type.

    2nd approch using config

    kernel.Bind<IUnitOfWork>().To<TheOtherOne_DevEntities>()
          .When(r => EntityNeedsUoW(r, 1));
    
    bool EntityNeedsUoW(IRequest r, int id)
    {
        return UoWConfig.GetDbIdForEntity(
             r.Target.Member.ReflectedType.GetGenericArguments()[0]) == id;
    }