gpt4 book ai didi

c# - 使用结构图为通用 CommandHandler 每个模块注册不同的 UnitOfWorks

转载 作者:太空宇宙 更新时间:2023-11-03 10:43:29 26 4
gpt4 key购买 nike

我在最近的项目中使用了 CQRS 模式,并在我的 DAL 中首先使用了 EF 代码,所以我定义了一些通用的 CommandHandlers插入/更新/删除:

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()
{
}

如你所见,我注入(inject)了 IUnitOfWorkInsertCommandHandler构造函数。

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

我用了Structuremap 3作为我的 IoC Container , 所以我定义了以下转换来解决 ICommandHandler每个BaseEntity类型(使用 custom registration conventions for partially closed types ):

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

我注册了EfUnitOfWork<SaleDBContext>对于 IUnitOfWork , 但我想单独使用 DbContext每个模块在我的解决方案(Bounded context)。例如我的销售模块有自己的 DbContext , HR模块有自己的DbContext等等,以上注册转换,只注册EfUnitOfWork<SaleDBContext>作为我的 IUnitOfWork .

我的解决方案中有一些模块(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>我希望它使用相应模块的 DbContext作为IUnitOfWork .例如,我想要 InsertCommandHandler<Order>使用 SaleDbContext因为它是IUnitOfWorkInsertCommandHandler<Employee>使用 HRDbContext因为它是IUnitOfWork .

[已更新]

这是 IoC containar 应该提供的客户代码示例 SaleDbContext对于 Consumer1HRDbContext对于 Consumer2 :

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 在我的组合根中执行此操作? ?

最佳答案

你可以制作IUnitOfWork通用如 IUnitOfWork<TConnection> .这允许每个 Repository规定哪个UnitOfWork它需要,理想情况下使用构造函数注入(inject),例如

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)
{
// ...
}
}

更新

对于通用处理程序来说,合乎逻辑的做法是每个逻辑域都有一个(即每个 DbContext ),例如 SalesInsertCommandHandler , HRInsertCommandHandler

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

}
}

这符合 separation of concerns原则,并在您用不同方面(跟踪、重试逻辑等)装饰您的关注点时为您提供额外的灵 active

当然,所有命令处理程序都可以继承自单个通用(抽象)命令处理程序。

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

关于c# - 使用结构图为通用 CommandHandler 每个模块注册不同的 UnitOfWorks,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24382654/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com