Search code examples
c#dependency-injectionninjectproviderninject-extensions

Implementing ninject provider for a generic type


Using ninject, I want to create a provider for MyRepository class which has dependency on ApplicationDbContext:

public class MyRepository<TEntity> : IMyRepository<TEntity>
    where TEntity : MyBaseEntity
{
    private ApplicationDbContext _dbContext;

    public MyRepository(ApplicationDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    // ...
}

I have seen this document which explains how the providers should be created, but I am not sure:

  1. How to pass ApplicationDbConext argument to the provider
  2. How to instantiate a generic type

Here is my attempt:

public class MyRepositoryProvider : Provider<MyRepository> 
{
    protected override MyRepository CreateInstance(IContext context)
    {
        // how to create a generic instance of type T?
        MyRepository myRepository = new MyRepository<T>(/*need ApplicationDbContext*/);
        return myRepository;
    }
}

I am not certain if it is possible to create a provider for a generic type. If not, can someone show how this can be done using Factory interface?


Note: I have created this code review explaining why I need a provider.


Solution

  • Since in this case the target implementation type is known to the provider.

    Then you can get the generic type from the type being requested and use that to construct the desired implementation.

    public class MyRepositoryProvider : IProvider {
        private ApplicationDbContext _applicationDbContext;
    
        public MyRepositoryProvider(ApplicationDbContext applicationDbContext) {
            _applicationDbContext = applicationDbContext;
        }
    
        Type Type => typeof(MyRepository<>);
    
        public object Create(IContext context) {
            var genericArguments = context.GenericArguments; //TEntity
            var genericType = this.Type.MakeGenericType(genericArguments); //MyRepository<TEntity>
    
            //using reflection to do new MyRepository<TEntity>(_applicationDbContext)
            return Activator.CreateInstance(genericType, _applicationDbContext);
        }
    }
    

    Activator is used here under the assumption that the implementation has a public constructor as implied by the code in the original example. If not public then reflection can be used to find the construct and invoke it.

    The provider is registered with the kernel

    kernel.Bind<ApplicationDbContext>().ToSelf().InRequestScope();
    kernel.Bind(typeof(IMyRepository<>)).ToProvider(typeof(MyRepositoryProvider)).InRequestScope();
    

    Which tells the kernel to use the provider when resolving the abstractions

    IMyRepository<MyEntity> repository = kernel.Get<IMyRepository<MyEntity>>();