2014-06-24 7 views
2

내 최근 프로젝트에 CQRS 패턴을 사용하고, 및 사용 EF 코드를 사용하여 일반 CommandHandler 각 모듈마다 다른 UnitOfWorks 등록, 그래서 삽입/업데이트 할 몇 가지 일반적인 CommandHandlers 정의/삭제 :내 DAL 먼저 structuremap

를 보시다시피
public class InsertCommandHandler<TEntity> : ICommandHandler<InsertCommandParameter<TEntity>> 
    where TEntity : BaseEntity, IAggregateRoot<TEntity>, new() 
{ 
    private readonly IUnitOfWork _uow; 

    public InsertCommandHandler(IUnitOfWork uow) 
    { 
     _uow = uow; 
    } 

    public void Handle(InsertCommandParameter<TEntity> parameter) 
    { 
     var entity = parameter.Entity; 
     _uow.Repository<TEntity>().Add(entity); 
    } 
} 

public interface ICommandParameter 
{ 
} 

public abstract class BaseEntityCommandParameter<T> : ICommandParameter 
    where T : BaseEntity, new() 
{ 
    public T Entity { get; set; } 

    protected BaseEntityCommandParameter() 
    { 
     Entity = new T(); 
    } 
} 
public class InsertCommandParameter<T> : BaseEntityCommandParameter<T> where T : class, new() 
{ 
} 

은 내가 InsertCommandHandler 생성자에 IUnitOfWork을 주입.

public interface IUnitOfWork : IDisposable 
{ 
    IRepository<T> Repository<T>() where T : BaseEntity, IAggregateRoot<T>,new(); 
    void Commit(); 
} 

나는 나의 IoC ContainerStructuremap 3을 사용, 그래서 정의 된 변환 다음 (custom registration conventions for partially closed types 사용) 각 BaseEntity 유형 ICommandHandler의 해결을 위해 :

public class CRUDCommandRegistrationConvention : StructureMap.Graph.IRegistrationConvention 
{ 
    private static readonly 
    Type _openHandlerInterfaceType = typeof(ICommandHandler<>); 
    private static readonly 
    Type _openInsertCommandType = typeof(InsertCommandParameter<>); 
    private static readonly 
    Type _openInsertCommandHandlerType = typeof(InsertCommandHandler<>); 
    private static readonly 
    Type _openUpdateCommandType = typeof(UpdateCommandParameter<>); 
    private static readonly 
    Type _openUpdateCommandHandlerType = typeof(UpdateCommandHandler<>); 
    private static readonly 
    Type _openDeleteCommandType = typeof(DeleteCommandParameter<>); 
    private static readonly 
    Type _openDeleteCommandHandlerType = typeof(DeleteCommandHandler<>); 



    public void Process(Type type, Registry registry) 
    { 
     if (!type.IsAbstract && typeof(BaseEntity).IsAssignableFrom(type)) 
      if (type.GetInterfaces() 
       .Any(x => x.IsGenericType && x.GetGenericTypeDefinition() 
        == typeof(IAggregateRoot<>))) 
      { 
       Type closedInsertCommandType = _openInsertCommandType.MakeGenericType(type); 
       Type closedInsertCommandHandlerType = _openInsertCommandHandlerType.MakeGenericType(type); 
       Type closedUpdateCommandType = _openUpdateCommandType.MakeGenericType(type); 
       Type closedUpdateCommandHandlerType = _openUpdateCommandHandlerType.MakeGenericType(type); 
       Type closedDeleteCommandType = _openDeleteCommandType.MakeGenericType(type); 
       Type closedDeleteCommandHandlerType = _openDeleteCommandHandlerType.MakeGenericType(type); 

       Type insertclosedHandlerInterfaceType = _openHandlerInterfaceType.MakeGenericType(closedInsertCommandType); 
       Type updateclosedHandlerInterfaceType = _openHandlerInterfaceType.MakeGenericType(closedUpdateCommandType); 
       Type deleteclosedHandlerInterfaceType = _openHandlerInterfaceType.MakeGenericType(closedDeleteCommandType); 

       registry.For(insertclosedHandlerInterfaceType).Use(closedInsertCommandHandlerType); 
        registry.For(updateclosedHandlerInterfaceType).Use(closedUpdateCommandHandlerType); 
        registry.For(deleteclosedHandlerInterfaceType).Use(closedDeleteCommandHandlerType); 
      } 
    } 
} 

그리고 내 CompositionRoot에서 그것을 사용 :

public static class ApplicationConfiguration 
{ 
    public static IContainer Initialize() 
    { 
     ObjectFactory.Initialize(x => 
     { 
      x.Scan(s => 
      { 
       s.AssemblyContainingType(typeof(ICommandHandler<>)); 
       s.AssemblyContainingType(typeof(Order)); 
       s.AssemblyContainingType(typeof(FindOrderByIdQueryHandler)); 
       s.WithDefaultConventions(); 
       x.For(typeof(IUnitOfWork)) 
        .Use(typeof(EfUnitOfWork<SaleDBContext>)) 
        .Named("SaleDBContext") 
        .SetLifecycleTo((Lifecycles.Singleton)); 
       s.Convention<CRUDCommandRegistrationConvention>(); 

      }); 

     }); 

     return ObjectFactory.Container; 
    } 
    public static T Resolve<T>() 
    { 
     return ObjectFactory.GetInstance<T>(); 
    } 
} 

IUnitOfWork에 대해 EfUnitOfWork<SaleDBContext>을 등록했지만 내 솔루션 (Bounded context)에서 각 모듈 당 DbContext을 분리하여 사용하십시오. 예를 들어 내 판매 모듈에는 DbContext, HR 모듈에는 DbContext 등이 있으며 위 등록 변환에는 으로 등록 EfUnitOfWork<SaleDBContext> 만 등록하면됩니다.

내가 내 솔루션에서 일부 모듈 (Visual Studio에서 솔루션 폴더)가 각각의 모듈은 3 계층 (3 개 클래스 라이브러리 프로젝트)이 있습니다

: 내 모듈 구조를 다음있다 (예를 들어, 각 모듈은 3 어셈블리가 있습니다)
SaleModule: 
----Application 
----Domain (Entities , ...) //Order, Customer,... 
----DAL (DbContext ,...) //SaleDbContext 

HRModule: 
----Application 
----Domain (Entities , ...) // Employee, OrganizationUnit, ... 
----DAL (DbContext ,...)//HRDbContext 

InfrastructureModule: 
----Application (ICommandHandler,IQueryHandler,...) 
----Domain 
----DAL 

InsertCommandHandler<T>은 인프라 모듈에 넣습니다.

내가 InsertCommanHandler<T>을 사용할 때 해당 모듈의 DbContextIUnitOfWork으로 사용하고 싶습니다. 예를 들어 InsertCommandHandler<Order>은 이고, IUnitOfWork이고 InsertCommandHandler<Employee>IUnitOfWork이므로 HRDbContext을 사용하고 싶습니다.

[업데이트]]

이의 IoC containar이 Consumer2에 대한 Consumer1HRDbContext에 대한 SaleDbContext을 제공해야 cunsumers 코드의 샘플입니다 : 나는 내 조성 루트에 사용하는 것을 할 수있는 방법

public class Consumer1 
{ 
    ICommandHandler<InsertCommandParameter<Order>> _insertCommandHandler; 
    public Consumer1(ICommandHandler<InsertCommandParameter<Order>> insertCommandHandler) 
    { 
     _insertCommandHandler = insertCommandHandler; 
    } 
    public void DoInsert() 
    { 
     var command = new InsertCommandParameter<Order>(); 
     command.Entity = new Order(){ 
             Number = 'ord-01', 
             // other properties 
            }; 
     insertCommandHandler.Handle(command); //this query handler should use SaleDbContext 

    } 
} 

public class Consumer2 
{ 
    ICommandHandler<InsertCommandParameter<Employee>> _insertCommandHandler; 
    public Consumer2(ICommandHandler<InsertCommandParameter<Employee>> insertCommandHandler) 
    { 
     _insertCommandHandler = insertCommandHandler; 
    } 
    public void DoInsert() 
    { 
     var command = new InsertCommandParameter<Employee>(); 
     command.Entity = new Employee(){ 
             EmployeeNumber = 'Emp1', 
             // other properties 
            }; 
     insertCommandHandler.Handle(command); //this query handler should use HRDbContext 

    } 
} 

StructureMap?

답변

2

과 같이 IUnitOfWork을 일반적으로 만들 수 있습니다. 이를 통해 각 Repository은 생성자 주입을 이상적으로 필요로하는 것으로 규정합니다.

public class InsertCommandHandler : ICommandHandler<Order> 
{ 
    public InsertCommandHandler(IUnitOfWork<SalesDbContext> salesUnitOfWork) 
    { 
     // ... 
    } 
} 

그러나, 당신은 아마 당신이 그런 종속성을 피하기 위해 추상화를 정의해야합니다 있도록 각 처리기에서 DbContext을 참조하지 않습니다.

모든 DbContext 래퍼 클래스가 따라

public interface IConnection 
{ 
    DbContext Context { get; } 
} 

업데이트 IUnitOfWork 여기

public interface IUnitOfWork<TConnection> where TConnection : IConnection { } 

을 구현하는 간단한 인터페이스

시작 예 래퍼

public class SalesConnection : IConnection 
{ 
    private readonly DbContext context; 

    public SalesConnection() 
    { 
     this.context = new SalesDbContext(); 
    } 

    public DbContext Context { get { return this.context; } } 
} 

입니다 그리고 여기에 어떤 업데이트 된 명령입니다 핸들러

public class InsertCommandHandler : ICommandHandler<Order> 
{ 
    public InsertCommandHandler(IUnitOfWork<SalesConnection> salesUnitOfWork) 
    { 
     // ... 
    } 
} 

UPDATE

일반 핸들러를 위해 할 수있는 논리적 인 것처럼 보일 것이다 것은 논리적 도메인 당 하나를 가지고하는 것입니다 (즉, HRInsertCommandHandler

public class SalesInsertCommandHandler<TCommand> : ICommandHandler<TCommand> 
{ 
    public SalesInsertCommandHandler(IUnitOfWork<SalesConnection> unitOfWork) 
    { 

    } 
} 

이것은 separation of concerns 원칙을 준수하고)

모두 로직 등을 추적 다양한 측면 (과 우려를 장식 다시 시도 올 때 당신에게 여분의 유연성을 제공,) DbContext 당 예를 SalesInsertCommandHandler에 대한 물론 명령 핸들러는 하나의 공통 (추상) 명령 핸들러로부터 상속받을 수 있습니다.

public abstract class CommandHandler<TConnection, TCommand> : 
    ICommandHandler<TCommand> 
    where TConnection : IConnection 

{ 
    private readonly IUnitOfWork<TConnection> unitOfWork; 

    public CommandHandler(IUnitOfWork<TConnection> unitOfWork) 
    { 
     this.unitOfWork = unitOfWork; 
    } 
} 

public class SalesInsertCommandHandler<TCommand> : 
    CommandHandler<SalesConnection, TCommand> 
{ 
} 
+0

감사 qujck 난 당신의 솔루션을 사용하는 경우, 그러나, 나는 각 엔티티에 대해 개별적으로 OrderInsertCommandHandler, EmployeeInsertCommandHandler, ...를 정의해야합니다,하지만 난 내 질문에 업데이트 된 내 기관에 대한 일반 InsertCommandHandler을 <>를 사용하려면, PLZ 소비자 클래스를 참조하십시오. – Masoud

+0

@Masoud가 내 업데이트를보고 있습니다.이 기능이 작동합니까? – qujck

+0

나는 그것이 작동한다고 생각하지만 가능한 한 하나의'InsertCommandHandler '클래스로 해결책을 찾고있다. – Masoud