2014-07-25 3 views
0

C#, .NET Framework 4.0, Entity Framework Code First 6.0 및 Ninject를 사용하여 ASP.NET MVC 4 웹 API를 개발 중입니다.Ninject IGenericRepository와의 명명 된 바인딩

서로 다른 두 개의 데이터베이스로 연결하기 위해 서로 다른 두 개의 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.. 
} 

내가 여기 public GenericRepository(IUnitOfWork unitOfWork)[Named("TRZIC")]을 사용하는 방법을 모르거나 어쩌면 내가 다른 곳을 사용해야합니다.

여기서 IUnitOfWork 구현은 TEntity에 따라 다릅니다.

어떤 조언이 필요합니까?

+0

이전 질문에서 제안한 인터페이스를 사용해보십시오. –

+0

@Alex 두 데이터베이스 모두에 동일한 'IGenericRepository'를 사용하고 있기 때문에 여기서 인터페이스를 사용할 수 없습니다. 여기서, IUnitOfWork 구현은 TEntity에 의존합니다. – VansFannel

답변

0

또 다른 해결책 : 나는 INICDbContext을 사용할 것을 나타 내기 위해서 WithConstructorArgument을 사용했다

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.. 
} 

.

이 정보가 정확한지 나는 알 수 없습니다.

0

IUnitOfWorkTEntity에 달려있는 이유는 무엇입니까 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

기본 사항부터 살펴 보겠습니다.

마찬가지로 이름이 지정된 바인딩은 [Named("foo")] 속성과 같은 코드에서 비롯된 상수 값으로 만 작동하거나 IResolutionRoot.Get<T>(string name)과 같은 "서비스 위치"를 사용하여 작동합니다. 시나리오 중 하나가 작동하지 않으므로 명명 된 바인딩이 문제가되지 않습니다. 그러면 조건부 바인딩이 생깁니다 (.When(...) 메쏘드).


각각 n 개의 엔티티가있는 2 개의 데이터베이스가 있습니다. 2 데이터베이스는 두 개의 구성이 두 개의 다른 의미 인 IUnitOfWork 구성을 의미합니다. 그러나 "사용자"는 특정 데이터베이스가 아니라 특정 엔터티를 요청하고 있습니다. 따라서지도 entity-->database (사전)이 필요합니다. 나는 그걸 해결할 방법이 없다고 생각하지만, 컨벤션을 통해 구현하는 일종의 컨벤션을 고안 할 수도 있습니다. 그래서 많은 코드를 타이핑하고 유지할 필요가 없습니다.

해결 방법 1 : 상자 Ninject에 기능을 밖으로 .WhenInjectedInto<>

, 그리고 육체 노동을 많이 :

Bind<IUnitOfWork>().To<UnitOfWorkOfDatabaseA>() 
    .WhenInjectedInto<IRepository<SomeEntityOfDatabaseA>>(); 

Bind<IUnitOfWork>().To<UnitOfWorkOfDatabaseA>() 
    .WhenInjectedInto<IRepository<SomeOtherEntityOfDatabaseA>>(); 

Bind<IUnitOfWork>().To<UnitOfWorkOfDatabaseB>() 
    .WhenInjectedInto<IRepository<SomeEntityOfDatabaseB>>(); 

당신이 바로 .. 드리프트를 얻을?


솔루션 2.1 : 사용자 정의 When(..) 구현

더 이상 육체 노동 및 유지 보수 순전히 없음. 내가 당신에 코드를 덤프하자, 아래 참조 :

공용 인터페이스 IRepository { IUnitOfWork의 UnitOfWork {얻을; } }

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; 
    } 
} 

솔루션 2.2 사용자 정의 규칙

When(...)이의 작은 규칙을 소개하겠습니다 기반. TRZIC 데이터베이스의 엔터티 이름은 TRZIC으로 시작합니다 (예 : TRZIC_Foo). 데이터베이스 INIC의 엔터티 이름은 과 같이 INIC으로 시작됩니다. 우리가 명시 적으로 매핑 (EntityA, EntityB, EntityC) => DatabaseA, (EntityD, EntityE, EntityF) => DatabaseB) 필요하지 않습니다

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; 
    } 
} 

이 방법 : 우리는 지금 이전 솔루션을 적용 할 수 있습니다.

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>가 aswell를 구현하자를 추가

.

관련 문제