Ninject命名与IGenericRepository的绑定

时间:2014-07-25 11:07:55

标签: c# entity-framework ninject

我正在使用C#,.NET Framework 4.0,Entity Framework Code First 6.0和Ninject开发ASP.NET MVC 4 Web Api。

我有两个不同的DbContext自定义实现来连接两个不同的数据库。

这是我的NinjectConfigurator课程(部分):

private void AddBindings(IKernel container)
{
    container.Bind<IUnitOfWork>().
       To<TRZICDbContext>().InRequestScope().Named("TRZIC");
    container.Bind<IUnitOfWork>().
       To<INICDbContext>().InRequestScope().Named("INIC");

    container.Bind<IGenericRepository<CONFIGURATIONS>>().
       To<GenericRepository<CONFIGURATIONS>>();
    container.Bind<IGenericRepository<INCREMENTAL_TABLE>>().
    To<GenericRepository<INCREMENTAL_TABLE>>();

    // More implementation...
}

CONFIGURATIONSTRZIC表,INCREMENTAL_TABLEINIC表。

我正在使用IGenericRepository,在这里我遇到了问题:

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
    protected DbSet<TEntity> DbSet;

    private readonly DbContext dbContext;

    public GenericRepository(IUnitOfWork unitOfWork)
    {
        dbContext = (DbContext)unitOfWork;
        DbSet = dbContext.Set<TEntity>();
    }

    // Hidden implementation..
}

我不知道如何使用[Named("TRZIC")] public GenericRepository(IUnitOfWork unitOfWork)或者我需要在其他地方使用它。

此处IUnitOfWork实施取决于TEntity。

有什么建议吗?

4 个答案:

答案 0 :(得分:2)

让我们从基础开始。

据我所知,命名绑定仅适用于代码中的常量值,例如[Named("foo")]属性,或者使用“{1}}之类的”服务位置“。这两种情况都不适用于您的场景,因此命名绑定是不可能的。 这使您得到条件绑定(IResolutionRoot.Get<T>(string name)方法)。


你有2个数据库,每个数据库都有n个实体。 2数据库意味着两个配置意味着2个不同的.When(...)配置。 但是,“用户”不是请求特定数据库,而是请求特定实体。 因此,您需要一张地图IUnitOfWork(字典)。我认为没有办法解决这个问题,但你可能会设计出某种惯例。按惯例实现它,因此您不必键入和维护大量代码。

解决方案1:entity-->database

具有开箱即用的ninject功能,以及大量的手工劳动:

.WhenInjectedInto<>

你得到漂移,对吧?


解决方案2.1:自定义Bind<IUnitOfWork>().To<UnitOfWorkOfDatabaseA>() .WhenInjectedInto<IRepository<SomeEntityOfDatabaseA>>(); Bind<IUnitOfWork>().To<UnitOfWorkOfDatabaseA>() .WhenInjectedInto<IRepository<SomeOtherEntityOfDatabaseA>>(); Bind<IUnitOfWork>().To<UnitOfWorkOfDatabaseB>() .WhenInjectedInto<IRepository<SomeEntityOfDatabaseB>>(); 实施

不再需要手工劳动和维护了。 让我把代码转储给你,见下文:

公共接口IRepository     {         IUnitOfWork UnitOfWork {get; }     }

When(..)

解决方案2.2基于public class Repository<TEntity> : IRepository<TEntity> { public IUnitOfWork UnitOfWork { get; set; } public Repository(IUnitOfWork unitOfWork) { UnitOfWork = unitOfWork; } } public interface IUnitOfWork { } class UnitOfWorkA : IUnitOfWork { } class UnitOfWorkB : IUnitOfWork { } public class Test { [Fact] public void asdf() { var kernel = new StandardKernel(); kernel.Bind(typeof (IRepository<>)).To(typeof (Repository<>)); kernel.Bind<IUnitOfWork>().To<UnitOfWorkA>() .When(request => IsRepositoryFor(request, new[] { typeof(string), typeof(bool) })); // these are strange entity types, i know ;-) kernel.Bind<IUnitOfWork>().To<UnitOfWorkB>() .When(request => IsRepositoryFor(request, new[] { typeof(int), typeof(double) })); // assert kernel.Get<IRepository<string>>() .UnitOfWork.Should().BeOfType<UnitOfWorkA>(); kernel.Get<IRepository<double>>() .UnitOfWork.Should().BeOfType<UnitOfWorkB>(); } private bool IsRepositoryFor(IRequest request, IEnumerable<Type> entities) { if (request.ParentRequest != null) { Type injectInto = request.ParentRequest.Service; if (injectInto.IsGenericType && injectInto.GetGenericTypeDefinition() == typeof (IRepository<>)) { Type entityType = injectInto.GetGenericArguments().Single(); return entities.Contains(entityType); } } return false; } } 的自定义约定

让我们介绍一个小会议。数据库TRZIC的实体名称以When(...)开头,例如TRZIC。数据库INIC的实体名称以TRZIC_Foo开头,与INIC类似。我们现在可以调整以前的解决方案:

INIC_Bar

这样我们就不需要显式映射public class Test { [Fact] public void asdf() { var kernel = new StandardKernel(); kernel.Bind(typeof (IRepository<>)).To(typeof (Repository<>)); kernel.Bind<IUnitOfWork>().To<UnitOfWorkA>() .When(request => IsRepositoryFor(request, "TRZIC")); // these are strange entity types, i know ;-) kernel.Bind<IUnitOfWork>().To<UnitOfWorkB>() .When(request => IsRepositoryFor(request, "INIC")); // assert kernel.Get<IRepository<TRZIC_Foo>>() .UnitOfWork.Should().BeOfType<UnitOfWorkA>(); kernel.Get<IRepository<INIC_Bar>>() .UnitOfWork.Should().BeOfType<UnitOfWorkB>(); } private bool IsRepositoryFor(IRequest request, string entityNameStartsWith) { if (request.ParentRequest != null) { Type injectInto = request.ParentRequest.Service; if (injectInto.IsGenericType && injectInto.GetGenericTypeDefinition() == typeof (IRepository<>)) { Type entityType = injectInto.GetGenericArguments().Single(); return entityType.Name.StartsWith(entityNameStartsWith, StringComparison.OrdinalIgnoreCase); } } return false; } } (EntityA, EntityB, EntityC) => DatabaseA

答案 1 :(得分:0)

如果您说IUnitOfWork取决于TEntity,为什么不让IUnitOfWork通用?

public class TRZIC {}

public class INIC {}

public interface IUnitOfWork<TEntity> {}

public class TRZICDbContext : DbContext, IUnitOfWork<TRZIC> {}

public class INICDbContext : DbContext, IUnitOfWork<INIC> {}

public interface IGenericRepository<TEntity> {}

public class GenericRepository<TEntity> : IGenericRepository<TEntity>
    where TEntity : class
{
    public GenericRepository(IUnitOfWork<TEntity> unitOfWork)
    {
        var dbContext = (DbContext) unitOfWork;
    }
}

private static void AddBindings(IKernel container)
{
    container
        .Bind<IUnitOfWork<TRZIC>>()
        .To<TRZICDbContext>();
    container
        .Bind<IUnitOfWork<INIC>>()
        .To<INICDbContext>();

    container
        .Bind<IGenericRepository<TRZIC>>()
        .To<GenericRepository<TRZIC>>();
    container
        .Bind<IGenericRepository<INIC>>()
        .To<GenericRepository<INIC>>();
}

答案 2 :(得分:0)

另一种利用代码可读性的解决方案:

public interface IUnitOfWork {}

// both named A and B
public class UnitOfWorkA : IUnitOfWork {}

public class UnitOfWorkB : IUnitOfWork {}

public abstract class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
    protected DbSet<TEntity> DbSet;

    private readonly DbContext dbContext;

    public GenericRepository(IUnitOfWork unitOfWork)
    {
        dbContext = (DbContext)unitOfWork;
        DbSet = dbContext.Set<TEntity>();
    }

     // other IGenericRepository methods

}

public class GenericRepositoryForA<TEntity> : GenericRepository<TEntity>
{
    public GenericRepositoryForA([Named("A")]IUnitOfWork unitOfWork) 
        : base(unitOfWork)
    {

    }
}

public class GenericRepositoryForB<TEntity> : GenericRepository<TEntity>
{
    public GenericRepositoryForB([Named("B")]IUnitOfWork unitOfWork) 
        : base(unitOfWork)
    {

    }
}

这允许您将特定数据库上下文作为依赖项请求,或者在需要时获取它们。而且您只需要实施GenericRepository一次。

它极大地即兴创建了代码可见性,因为您实际上通过查看变量类型/名称来了解您正在使用的数据库上下文,而不是在没有任何关于其实际类型的视觉细节的情况下注入IUnitOfWork

我建议添加一些额外的接口,如果你想对它进行单元测试(你应该!)。

简单地添加

public interface IGenericRepositoryForA<TEntity> : IGenericRepository<TEntity>

并让GenericRepositoryForA<TEntity>实现它。

答案 3 :(得分:0)

另一种解决方案:

private void AddBindings(IKernel container)
{
    container.Bind<IUnitOfWork>().To<TRZICDbContext>().InRequestScope();

    container.Bind<IGenericRepository<CONFIGURATIONS>>().
        To<GenericRepository<CONFIGURATIONS>>();
    container.Bind<IGenericRepository<INCREMENTAL_TABLE>>().
        To<GenericRepository<INCREMENTAL_TABLE>>().WithConstructorArgument("unitOfWork", new INICDbContext());

    // More code..
}

我使用WithConstructorArgument表示我想使用INICDbContext

我不知道这是否正确。