2016-07-30 46 views
1

背景如何在WinForms客户端应用程序中使用命令模式?

我建立一个两层的C#.net应用程序:

  1. 1级:使用MVP(模型 - 视图 - 演示)设计模式的WinForms客户端应用程序。
  2. 第2层:位于实体框架和SQL Server之上的WebAPI RESTful服务。

如果您想了解更多关于我正在构建的应用程序的详细信息,我提供了一个可能过于全面的解释here

发展现状

目前,我工作的WinForms客户端上。特别是,我试图在这个客户端中散列出足够的命令模式实现。我很幸运地发现了一篇优秀的博客文章,其中概述了一个可靠的命令体系结构。为了补充这篇文章,作者followed up解释了他如何从命令中分离查询。在阅读这些博客后,我很清楚,我的第2层(web api服务)将从实现这两个方面中获得巨大收益。通用实现允许出色的灵活性,可测试性和可扩展性。

问题

什么是对我不太清楚的是我如何去对事物(1级)中的WinForms客户端实现这些模式。查询和命令是否继续被认为是分开的?考虑一个基本操作,例如登录尝试。这是一个查询或命令?最终,您需要从Web服务中取回数据(服务器上的用户信息),以便让我认为这是一个查询。另一种情况如何,例如创建新用户的请求。我知道你会创建一个命令对象来存储用户信息并将其发送给服务。命令应该是火和遗忘,但你不想从服务的命令成功的某种确认?此外,如果命令处理程序返回无效,那么如何告诉主讲者用户创建请求是否成功?

在一天结束时,对于任何给定的UI任务(比如用户创建请求),是否最终会产生基于winforms客户端的查询/命令,以及web api服务版本处理这个请求的命令/查询?

+0

Ric的答案是现货。我没有什么补充。 – Steven

回答

2

查询和命令是否继续被认为是单独的?

是的,通常情况下,您会激发一个命令,如果您在执行此操作后需要更新UI,则会执行查询以获取新信息。一个例子会说明这一点。

假设你会为特定区域分配一个特定的警卫。该命令(它只是一个DTO)需要的唯一信息是护卫的Id和该区域的Id。关联的CommandHandler将执行处理这个的所有任务,例如,从另一个区域移除该警卫,将他预订为不可用等。

现在您的用户界面会想要显示更改。用户界面可能包含所有警卫及其指定区域的列表。此列表将由单个GetActiveGuardsAndAreaQuery填充,这将返回List<GuardWithAreaInformationDto>。这DTO可能包含所有警卫的各种信息。从命令中返回这些信息并不是一个完全分离的问题,因为原子命令处理可以很好地用于类似但略有不同的用户界面,这将需要对UI信息进行稍微不同的更新。

例如登录尝试。这是一个查询或命令?

IMO登录尝试既不是。这是一个横切关注点,数据隐藏在安全连接背后的实现细节。但是应用程序不应该关注这个细节。考虑将此应用程序与其他客户一起使用,您可以在其中承载WebApi服务,并在Active Directory域中使用Windows Authentication。在这种情况下,用户只需登录到他的机器,并在通信时由客户端和服务器OS处理安全性。

使用模式可以很好地完成使用AuthenticateToWebApiServiceCommandHandlerDecorator,确保他们是登录凭据服务通过以模态形式询问用户,从配置文件中读取或任何其他。

检查凭据是否可以通过执行一种标准来完成Query您的应用程序总是需要诸如CheckIfUpdateIsAvailableQuery。如果查询成功,则登录尝试成功,否则失败。

如果一个命令处理程序返回void,那么如何告诉主讲者用户创建请求是否成功?

虽然看起来void没有返回任何东西,但这不是真的。因为如果它没有失败并带有一些异常(带有明确的信息出了什么问题!)它一定成功了。

在上述博客文章的follow up中@dotnetjunkie描述了一种从命令返回信息的方式,但是会注意到帖子顶部添加的评论。

总之,从扔命令失败清晰异常。你可以添加一个额外的抽象层客户端来处理这个很好。而不是直接注入commandhandler到不同的主持人,你可以注入IPromptableCommandHandler其在编译的时候只有一个开放的通用实现:

public interface IPromptableCommandHandler<TCommand> 
{ 
    void Handle(TCommand command, Action succesAction); 
} 

public class PromptableCommandHandler<TCommand> : IPromptableCommandHandler<TCommand> 
{ 
    private readonly ICommandHandler<TCommand> commandHandler; 

    public PromptableCommandHandler(ICommandHandler<TCommand> commandHandler) 
    { 
     this.commandHandler = commandHandler; 
    } 

    public void Handle(TCommand command, Action succesAction) 
    { 
     try 
     { 
      this.commandHandler.Handle(command); 
      succesAction.Invoke(); 
     } 
     catch (Exception) 
     { 
      MessageBox.Show("An error occured, please try again."); 
      // possible other actions like logging 
     } 
    } 
} 
// use as: 
public void SetGuardActive(Guid guardId) 
{ 
    this.promptableCommandHandler.Handle(new SetGuardActiveCommand(guardId),() => 
       this.RefreshGuardsList()); 

} 

在一天结束的时候,对于任何给定UI任务(比如在用户创建请求),它结束了,你最终有一个WinForms基于客户端的查询/命令,以及命令/查询它处理上为此请求的网页API服务的版本?

不!

你应该建立一个单一的开放式泛型 CommandHandlerProxy这仅仅任务是通过命令DTO到的WebAPI服务

客户端。

对于服务端架构,你应该阅读其他跟进:描述一个服务器端架构非常漂亮,处理这个Writing Highly Maintainable WCF Services。链接的项目还包含WebApi的实现!

+0

再次感谢@Ric,感谢您的时间!为了确保我理解了所说的话,让我重温一下:对于一个命令,winforms客户端将创建一个命令对象(一个DTO)。该命令对象将作为句柄方法中的参数传递给命令处理程序。但是,winforms客户端中的命令处理程序只是一个通用代理,它将命令传递给实际命令所在的web api(即代理可以注入到PromptableComandHandler中)。这听起来正确吗?查询是否也通过代理以相同的方式处理? – Andrew

+1

@Andrew是的,你理解正确。查询遵循相同的模式。请注意,所提到的代理实现了ICommandHandler,但它是一个开放的通用实现,它以相同的方式处理所有命令。 –

+0

真棒,感谢@Ric的确认,我注意到了!这是非常强大的东西。另一个快速的问题是:命令和查询(DTO)通常应该是扁平的(只是属性),还是可以由其他对象组成? – Andrew

相关问题