- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我在最近的项目中使用了 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)了 IUnitOfWork
到 InsertCommandHandler
构造函数。
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
因为它是IUnitOfWork
和 InsertCommandHandler<Employee>
使用 HRDbContext
因为它是IUnitOfWork
.
[已更新]
这是 IoC containar 应该提供的客户代码示例 SaleDbContext
对于 Consumer1
和 HRDbContext
对于 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/
我有一个问题,我希望我的处理程序使用从处理程序生成的数据: UpdateUserProfileImageCommandHandlerAuthorizeDecorator UpdateUserProfi
我有一个问题,我希望我的处理程序使用从处理程序生成的数据: UpdateUserProfileImageCommandHandlerAuthorizeDecorator UpdateUserProfi
我尝试使用命令 DTO,但无法识别他的处理程序。当我记录 DTO 时,它是一个没有 CreateUserCommand 签名的简单对象 {...}。 这是我的 Controller : async i
我是 C# 的(非常)新手,我正在尝试用这种语言实现现有的应用程序元模型。在此元模型中,“菜单”表包含表单和操作的组合,指示对于每个表单,哪些操作应该可用。 然后我可以使用这个“菜单”数据源在运行时构
我想在命令处理程序中使用命令列表,但永远不会调用相应的函数。 getabList = ["getab"] for x in range(0, 31): getabList.append("ge
在我的开发团队中,我们正在使用 this article 中描述的 CQRS 模式并使用推荐的 DI 容器:Simple Injector (双关语)。 这是我们当前的项目结构: 01 WebApp
我在最近的项目中使用了 CQRS 模式,并在我的 DAL 中首先使用了 EF 代码,所以我定义了一些通用的 CommandHandlers插入/更新/删除: public class InsertCo
我有以下测试用例: [Test] public void MarkAsSuccessfulTest() { //setup data var p
我一直在阅读有关使用 Command 对象来表示我们的域公开的用例,以及使用 Command Handler 对象来处理这些命令的内容。 例如: 注册用户命令 RegisterUserCommandH
我目前正在使用 Axon 4.2,并且我有一个聚合(Customer),它在其 @CommandHandlers 中使用注入(inject)的服务(CustomerService) 方法。 下面显示了
所以我试图了解 axon 3.4 中的分布式命令总线。我有一个用例,当发送某个命令时,聚合会发送一个启动传奇的事件,该传奇会发送 2 个命令,以保持发送到 2 个不同服务的数据处于一致状态。 现在棘手
我是一名优秀的程序员,十分优秀!