C# CommandHandler装饰程序依赖项
我有一个问题,我希望我的处理程序使用从处理程序生成的数据:C# CommandHandler装饰程序依赖项,c#,.net,dependency-injection,decorator,cqrs,C#,.net,Dependency Injection,Decorator,Cqrs,我有一个问题,我希望我的处理程序使用从处理程序生成的数据: UpdateUserProfileImageCommandHandlerAuthorizeDecorator UpdateUserProfileImageCommandHandlerUploadDecorator UpdateUserProfileImageCommandHandler 我的问题是架构和性能 UpdateUserCommandHandlerAuthorizeDecorator调用存储库(entityframework)以
UpdateUserCommandHandlerAuthorizeDecorator
调用存储库(entityframework)以授权用户。我还有其他类似的装饰程序,它们应该使用和修改实体,并将其发送到链的上游
UpdateUserCommandHandler
只需将用户保存到数据库。我目前必须进行另一个存储库调用并更新实体,而我本可以从以前的decorator处理该实体
我的问题是,该命令只接受用户Id和一些要更新的属性。在我从Authorize decorator获取用户实体的情况下,我如何仍然处理链上的该实体?将User
属性添加到命令并处理该属性可以吗
代码:
public类UpdateUserProfileImageCommand:Command
{
public UpdateUserProfileImageCommand(Guid id,流图像)
{
这个.Id=Id;
这个。图像=图像;
}
公共流映像{get;set;}
公共Uri ImageUri{get;set;}
}
公共类UpdateUserProfileImageCommandHandlerAuthorizeDecorator:ICommandHandler
{
公共无效句柄(UpdateUserProfileImageCommand命令)
{
//我想在`UpdateUserProfileImageCommandHandlerUploadDecorator'中使用此实体`
var user=userRespository.Find(u=>u.UserId==command.Id);
if(userCanModify(user,currentPrincipal))
{
decoratedHandler(命令);
}
}
}
公共类UpdateUserProfileImageCommandHandlerUploadDecorator:ICommandHandler
{
公共无效句柄(UpdateUserProfileImageCommand命令)
{
//我不想再次从存储库中请求这个,而是希望重用来自上一个decorator的实体
var user=userRespository.Find(u=>u.UserId==command.Id);
fileService.DeleteFile(user.ProfileImageUri);
var command.ImageUri=fileService.Upload(generatedUri,command.Image);
decoratedHandler(命令);
}
}
公共类UpdateUserProfileImageCommandHandler:ICommandHandler
{
公共无效句柄(UpdateUserProfileImageCommand命令)
{
//我再次询问用户。。。
var user=userRespository.Find(u=>u.UserId==command.Id);
user.ProfileImageUri=command.ImageUri;
//事实上,我在一家邮政装潢店有这个。
unitOfWork.Save();
}
}
为什么首先要通过装饰器实现这一点
验证
通常的方法是让客户机在提交命令之前进行所有必要的验证。创建/发布/执行的任何命令在提交之前都应执行所有(合理的)验证。我之所以选择“合理”,是因为有些东西,比如独特性,是无法事先100%验证的。当然,执行命令的授权可以在提交命令之前完成
拆分命令处理程序
拥有一个只处理命令处理逻辑的一部分,然后丰富命令对象的装饰器对我来说似乎过于工程化了。首先,应该使用decorator来扩展给定的操作,并提供额外的功能,例如日志记录、事务或身份验证(尽管如我所说,我认为这不适用于修饰命令处理程序)
似乎上传图像,然后在数据库中分配新的图像URL是一个命令处理程序的责任。如果您希望抽象这两个不同操作的细节,那么就向处理程序中注入这样做的类,比如IUserimageUploader
通常地
通常,命令被认为是不可变的,并且在创建后不应更改。这有助于强制要求命令应预先包含完成操作所需的所有信息。您不应仅为了性能而传递任何额外数据。此外,usng装饰师,你不能更改合同。相反,您应该允许缓存该用户实体,这通常应由存储库实现负责。对于实体框架,这实际上相当简单。您可以调用
DbSet.Find(id)
,EF将首先在缓存中查找实体。这可以防止不必要的数据库往返。我总是这样做
因此,您只需将Find(key)
或GetById
方法添加到您的存储库中,该方法映射到EF的Find(key)
方法,就完成了
此外,我同意皮特的观点。装饰器应该主要用于交叉关注点。在decorators中添加其他东西有时可以,但是您似乎在处理程序及其decorator上拆分了核心业务逻辑。将文件写入磁盘需要很长的时间才能到达核心逻辑。你可能会考虑坚持单一责任,但在我看来,你将单一责任分解为多个类。这并不意味着您的命令处理程序应该很大。正如Pete所说,您可能希望将其提取到一个服务中,并将该服务注入到处理程序中
验证授权是一个贯穿各领域的问题,因此在decorator中使用它似乎是可以的,但是您当前的实现存在一些问题。首先,这样做会导致有许多非通用的装饰器,这会导致大量的维护。此外,如果用户未经授权(通常不是您想要的),您会自动跳过执行
<>不要静静地跳过,考虑抛出一个异常并防止
public class UpdateUserProfileImageCommand : Command
{
public UpdateUserProfileImageCommand(Guid id, Stream image)
{
this.Id = id;
this.Image = image;
}
public Stream Image { get; set; }
public Uri ImageUri { get; set; }
}
public class UpdateUserProfileImageCommandHandlerAuthorizeDecorator : ICommandHandler<UpdateUserProfileImageCommand>
{
public void Handle(UpdateUserProfileImageCommand command)
{
// I would like to use this entity in `UpdateUserProfileImageCommandHandlerUploadDecorator`
var user = userRespository.Find(u => u.UserId == command.Id);
if(userCanModify(user, currentPrincipal))
{
decoratedHandler(command);
}
}
}
public class UpdateUserProfileImageCommandHandlerUploadDecorator : ICommandHandler<UpdateUserProfileImageCommand>
{
public void Handle(UpdateUserProfileImageCommand command)
{
// Instead of asking for this from the repository again, I'd like to reuse the entity from the previous decorator
var user = userRespository.Find(u => u.UserId == command.Id);
fileService.DeleteFile(user.ProfileImageUri);
var command.ImageUri = fileService.Upload(generatedUri, command.Image);
decoratedHandler(command);
}
}
public class UpdateUserProfileImageCommandHandler : ICommandHandler<UpdateUserProfileImageCommand>
{
public void Handle(UpdateUserProfileImageCommand command)
{
// Again I'm asking for the user...
var user = userRespository.Find(u => u.UserId == command.Id);
user.ProfileImageUri = command.ImageUri;
// I actually have this in a PostCommit Decorator.
unitOfWork.Save();
}
}
[PermittedRole(Role.LabManagement)]
public class UpdateUserProfileImageCommandHandler
: ICommandHandler<UpdateUserProfileImageCommand>
{
private readonly IFileService fileService;
public UpdateUserProfileImageCommandHandler(IFileService fileService)
{
this.fileService = fileService;
}
public void Handle(UpdateUserProfileImageCommand command)
{
var user = userRespository.GetById(command.Id);
this.fileService.DeleteFile(user.ProfileImageUri);
command.ImageUri = this.fileService.Upload(generatedUri, command.Image);
user.ProfileImageUri = command.ImageUri;
}
}