2

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.

Hooman Bahreini
  • 14,480
  • 11
  • 70
  • 137
  • `1)` What happens when you `bind` `IContext` to `ApplicationDbContext` and `2)` use `generic` `binding` like described here: https://stackoverflow.com/a/10243699/2877982 – Aage Nov 03 '19 at 08:45
  • @aage: thanks for your comment. `IContext` is ninject's context (as described [here](https://github.com/ninject/Ninject/wiki/Providers,-Factory-Methods-and-the-Activation-Context)). Regarding the other link, the answer is not using a Provider though... – Hooman Bahreini Nov 03 '19 at 09:02
  • I know it isn't a provider but why would it work different in anyway from the "regular" way of `binding` and `resolving` stuff in and from the `container`? About the `IContext`... maybe just bind `ApplicationDbContext` to itself or a `factory`? – Aage Nov 03 '19 at 09:15

2 Answers2

2

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>>();
Nkosi
  • 235,767
  • 35
  • 427
  • 472
1

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();
Hooman Bahreini
  • 14,480
  • 11
  • 70
  • 137