Hooman Bahreini
Hooman Bahreini

Reputation: 15559

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.

Upvotes: 2

Views: 644

Answers (2)

Nkosi
Nkosi

Reputation: 247008

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>>();

Upvotes: 2

Hooman Bahreini
Hooman Bahreini

Reputation: 15559

I managed to create a provider for my generic type:

public class MyRepositoryProvider<TEntity> : Provider<IMyRepository<TEntity>>
    where TEntity : MyBaseEntity
{
    private ApplicationDbContext _applicationDbContext;

    public MyRepositoryProvider(ApplicationDbContext applicationDbContext)
    {
        _applicationDbContext = applicationDbContext;
    }

    protected override IMyRepository<TEntity> CreateInstance(IContext context)
    {
        return new MyRepository<TEntity>(_applicationDbContext);
    }
}

And this is how the binding looks like:

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

Upvotes: 1

Related Questions