Question

In my application, I use repositories for CRUD operations. Out of all entities, some should not be deletable at all, some entities (that implement ISoftDeletable) are only soft deletable and some entities (that implement IPermanentDeletable) can be permanently deleted. (I'm following the answer on this post Generic Repository with Soft Delete Feature)

I have 3 Repositories that Inherit from one another as follows

BaseRepository : IRepository

SoftDeleteRepository : BaseRepository, ISoftDeleteRepository

PermanentDeleteRepository : BaseRepository, IPermanentDeleteRepository

My problem is, I don't want to do individual binding for each of the entities of different types like

kernel.Bind(typeof(IRepository<Company>)).To(typeof(BaseRepository<Company>));
kernel.Bind(typeof(IRepository<Country>)).To(typeof(PermanentDeleteRepository<Country>));
kernel.Bind(typeof(IRepository<Contact>)).To(typeof(SoftDeleteRepository<Contact>));

But instead, I want to somehow make it so that all the bindings for IRepository go to a factory and instantiate the repository depending on the type of the entity that its generic parameter has taken, and inject it to the controller.

Is it possible to achieve this?

Was it helpful?

Solution

So this is how you could achieve it while still injecting IRepository<XYZ> into the consumer:

public interface ISoftDeletable
{
}

public interface IPermanentDeleteable
{
}

public interface IRepository<TEntity>
{
}

public class PermanentDeletableRepository<TEntity> : IRepository<TEntity>
    where TEntity : IPermanentDeleteable
{
}

public class SoftDeletableRepository<TEntity> : IRepository<TEntity>
    where TEntity : ISoftDeletable
{
}

public class RepositoryModule : NinjectModule
{
    public override void Load()
    {
        this.Bind(typeof(IRepository<>))
            .To(typeof(PermanentDeletableRepository<>))
            .When(IsRequestForRepositoryOfEntityImplementing<IPermanentDeleteable>);

        this.Bind(typeof(IRepository<>))
            .To(typeof(SoftDeletableRepository<>))
            .When(IsRequestForRepositoryOfEntityImplementing<ISoftDeletable>);
    }

    public static bool IsRequestForRepositoryOfEntityImplementing<TInterface>(IRequest request)
    {
        Type entityType = GetEntityTypeOfRepository(request.Service);
        return ImplementsInterface<TInterface>(entityType);
    }

    public static Type GetEntityTypeOfRepository(Type repositoryType)
    {
        // target.Type must be IRepository<"AnyEntity">
        return repositoryType.GetGenericArguments().Single();
    }

    public static bool ImplementsInterface<TInterface>(Type type)
    {
        return typeof(TInterface).IsAssignableFrom(type);
    }
}

I verified with a unit test that it works:

    public class SomeSoftDeletableEntity : ISoftDeletable
    {
    }

    [Fact]
    public void RepositoryBinding()
    {
        var kernel = new StandardKernel();
        kernel.Load<RepositoryModule>();

        IRepository<SomeSoftDeletableEntity> repository = kernel.Get<IRepository<SomeSoftDeletableEntity>>();

        repository.Should().BeOfType<SoftDeletableRepository<SomeSoftDeletableEntity>>();
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top