执行前验证命令

发布于 2024-12-23 10:30:20 字数 1737 浏览 4 评论 0原文

在我当前构建的系统中,我使用命令模式来执行所有可能的操作。我选择了 CommandMessage 和 CommandHandler 方法,将逻辑与数据分开。 目前这工作正常,但我遇到了一个问题 - 验证。

如何实际验证命令是否可以执行?

现在,我在每个命令上都有一个 CanExecute(ICommandExecutionContext context),使其负责确定它是否可以执行。然后在每个命令中对 ICommandExecutionContext 进行类型检查,以查看它是否具有正确的上下文类型,然后检查该信息是否使该命令在该上下文中可执行。

一切都包装在 ICommandService 中,它可以根据命令的名称、上下文和消息来验证和执行命令。除此之外,它还发布有关命令执行的事件,并执行权限检查。

该问题源于 UI(ASP.NET MVC 3 应用程序)。我想只在每个视图中显示有效的命令,但我未能找到我真正喜欢的解决方案。目前,我的控制器询问命令服务,在给定具体上下文的情况下,命令是否可以执行,如下所示:

var executionContext = new SystemCommandExecutionContext("SignInCommand", CurrentPrincial);
var canExecute = _commandService.CanExecute(executionContext);
/* Handle the result to enable or disable the action link */

对于适用于具体域对象的其他类型的命令,我使用相同的命令服务方法,但使用不同的上下文,我在其中传递域对象 id,如下所示:

[HttpPost, Authorize, ValidateAntiForgeryToken /* etc. */]
public ActionResult Delete(Guid id)
{
    /* Note the additional object id in the context */
    var executionContext = new EntityCommandExecutionContext("DeletePersonCommand", CurrentPrincipal, id);
    var canExecute = _commandService.CanExecute(executionContext);

    if(canExecute)
    {
        var message = new DeletePersonCommandMessage(id);
        var isValid = _commandService.IsValid(executionContext, message);
        if(isValid)
        {
            var result = _commandService.Execute(executionContext, message);
            /* More logic here... Not very DRY :( */
        }            
    }
}

我想上面的内容目前还可以,尽管不是很干燥。 但我想要完成的是根据 CanExecute 的结果禁用操作链接。

我怎样才能做到这一点?

我决定对每个视图上的所有命令链接进行“硬编码”,因此我不必传递命令名称等的集合 - 该路径太困难了(除非有人有一个聪明的主意;)

我当前的堆栈包括NHibernate、温莎城堡、ASP.NET MVC 3、AutoMapper。

In the system I'm currently building, I use the command pattern to carry out all operations possible. I have chosen the CommandMessage and CommandHandler approach, separating logic from data.
This works fine for now, but I've run into a problem - validation.

How do I actually validate whether a command can execute or not?

Right now I have a CanExecute(ICommandExecutionContext context) on every command, making it responsible for determining whether or not it can execute. A ICommandExecutionContext is then typechecked in each command, to see if it's of the correct context type, and afterwards if the information makes the command executable in that context.

Everything is wrapped in a ICommandService that can validate and execute commands, based on their names, context and message. Besides that, it also publishes events about command execution, and perform permission checks.

The problem originates from the UI (an ASP.NET MVC 3 application). I would like to only show valid commands in each view, but I have failed to come up with a solution I really like. Currently my controller asks the command service, whether a command can execute, given a concrete context like so:

var executionContext = new SystemCommandExecutionContext("SignInCommand", CurrentPrincial);
var canExecute = _commandService.CanExecute(executionContext);
/* Handle the result to enable or disable the action link */

For other types of commands, that works on concrete domain objects, I use the same command service method, but a different context, where I pass the domain object id, like this:

[HttpPost, Authorize, ValidateAntiForgeryToken /* etc. */]
public ActionResult Delete(Guid id)
{
    /* Note the additional object id in the context */
    var executionContext = new EntityCommandExecutionContext("DeletePersonCommand", CurrentPrincipal, id);
    var canExecute = _commandService.CanExecute(executionContext);

    if(canExecute)
    {
        var message = new DeletePersonCommandMessage(id);
        var isValid = _commandService.IsValid(executionContext, message);
        if(isValid)
        {
            var result = _commandService.Execute(executionContext, message);
            /* More logic here... Not very DRY :( */
        }            
    }
}

I guess the above is okay for now, though not very DRY.
But what I would like to accomplish is to disable the action links, based on the result from CanExecute.

How can I do that?

I've decided to "hardcode" all command links on each view, so I don't have to pass a collection of command names etc. - that path is too difficult (unless someone has a clever idea ;)

My current stack consist of NHibernate, Castle Windsor, ASP.NET MVC 3, AutoMapper.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

叹梦 2024-12-30 10:30:20

需要注意的是,您的命令对象是那些被序列化并发送到(可能是远程)服务以由命令处理程序处理的对象,那么这不是 GoF 意义上的命令模式的实例,因为命令对象本身并不提供执行方法。在这种情况下,命令消息对象的目的是表示一个操作以及所需的参数。有些来源将其称为可序列化方法调用。消息对象不应包含行为,而应仅包含数据,命令消息也不例外。这意味着确定给定命令是否可以在特定上下文中执行的过程应该由单独的服务来处理。该验证服务的具体实现取决于用于确定给定命令是否可以执行的标准。它可以根据某些上下文和命令类型来验证命令:

interface ICommandValidationService
{
  bool CanExecute(object context, Type commandType);
}

上下文对象应该独立于正在执行的实际命令。相反,它应该包含更多全局上下文值,例如用户、用户权限等。命令执行的能力将使用此上下文来决定。

根据命令的执行状态禁用或隐藏操作链接可以使用如下视图模型来实现:

class ActionLinkViewModel
{
        public string Name { get; set; }
        public string Url { get; set; }
        public bool Enabled { get; set; }
}

其中启用的值由 ICommandValidationService 在控制器中分配。此外,您可以扩展 MvcSiteMapProvider 来指定给定站点地图节点是否可见。

As small note, your command objects are those which are serialized and sent to a (possibly remote) service to be handled by a command handler then this is not an instance of the command pattern in the GoF sense, because the command object itself does not provide an execution method. The purpose of the command message object in this case is to represent an action together with the required parameters. Some sources call it a serializable method call. Message objects should not contain behavior, only data, and a command message is no exception. This means that the process of determining whether a given command can be executed in a particular context should be handled by a separate service. The specific implementation of this validation service depends on the criteria that are used in determining whether a given command can execute. It may be able to validate a command based on some context and the type of the command:

interface ICommandValidationService
{
  bool CanExecute(object context, Type commandType);
}

The context object should be independent of the actual command being executed. Instead it should contain more global contextual values such as the user, user permissions, etc. The ability of a command to execute would be decided using this context.

Disabling or hiding action links based on the execution status of a command can be implemented using a view model like this:

class ActionLinkViewModel
{
        public string Name { get; set; }
        public string Url { get; set; }
        public bool Enabled { get; set; }
}

Where the enabled value is assigned in the controller by the ICommandValidationService. Furthermore, you can extend the MvcSiteMapProvider to specify whether a given site map node is visible.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文