背景
我正在构建一个双层C#.net应用程序:
如果您想了解我正在构建的应用程序的更多详细信息,我提供了一个可能过于彻底的解释here。
当前发展
目前,我正在Winforms客户端上工作。特别是,我试图在这个客户端中散列出适当的命令模式实现。我很幸运能够偶然发现this出色的博客文章,其中概述了一个坚实的命令架构。为了补充该帖子,作者followed up解释了他如何将查询与命令分开。在阅读这些博客之后,很明显我的第2层(web api服务)将从实现这两个方面大大受益。通用实现允许出色的灵活性,可测试性和可扩展性。
问题
对我来说不太清楚的是我如何在winforms客户端(第1层)上实现这些模式。查询和命令在这里继续被认为是独立的吗?考虑一个基本操作,例如登录尝试。这是查询还是命令?最终,您需要从Web服务返回数据(服务器上的用户信息),这样我才会认为这是一个查询。那么另一种情况呢,比如创建新用户的请求。我知道你会创建一个命令对象来存储用户信息并将其发送给服务。命令应该是火上浇油而忘记,但是你不希望从服务中获得命令成功的某种确认吗?此外,如果命令处理程序返回void,您如何告诉演示者用户创建请求是否成功?
在一天结束时,对于任何给定的UI任务(比如用户创建请求),最终是否最终得到基于winforms客户端的查询/命令,以及最终的web api服务版本命令/查询在那一端处理请求?
答案 0 :(得分:3)
此处的查询和命令是否继续被视为独立?
是的,通常您会触发一个命令,如果您需要在执行此操作后更新UI,您将执行查询以获取新信息。一个例子可以说明这一点。
我们假设您要为某个区域指定一名特定的后卫。该命令(仅为DTO)所需的唯一信息是守卫的Id
和该区域的Id
。关联的CommandHandler
将执行处理此操作的所有任务,例如从另一个区域移除该警卫,将他预订为不可用等。
现在您的UI想要显示更改。用户界面可能包含所有警卫及其指定区域的某种列表。此列表将由一个GetActiveGuardsAndAreaQuery
填充,该List<GuardWithAreaInformationDto>
将返回DTO
。此Active Directory
可以包含有关所有警卫的各种信息。从命令中返回此信息并不是关注点的清晰分离,因为原子命令处理可以从类似但稍微不同的UI中很好地使用,这将需要略微不同的UI信息更新。
例如登录尝试。这是查询还是命令?
IMO登录尝试都不是。这是一个跨领域的问题,一个实现细节,数据隐藏在安全连接后面。但是,应用程序不应该关注这个细节。考虑将该应用程序与您可以在Windows Authentication
域中托管WebApi服务的其他客户一起使用,您可以使用AuthenticateToWebApiServiceCommandHandlerDecorator
。在这种情况下,用户只需登录他的机器,安全性由客户端和服务器操作系统在通信时处理。
使用Query
可以很好地完成您所使用的模式。通过以模态形式询问用户,从中读取它,确保它们是服务于服务的登录凭据配置文件,或其他什么。
检查凭据是否有效可以通过执行您的应用程序始终需要的标准CheckIfUpdateIsAvailableQuery
来完成,例如void
。如果查询成功,则登录尝试成功,否则失败。
如果命令处理程序返回void,您如何告诉演示者用户创建请求是否成功?
虽然IPromptableCommandHandler
似乎没有返回任何内容,但事实并非如此。因为如果它没有因某些异常而失败(明确的消息出错了!)它必须成功。
在follow up提到的博客文章中,@ dotnetjunkie描述了一种从命令返回信息的方法,但注意到帖子顶部添加的评论。
总结一下,从失败的命令中抛出明确的异常。您可以添加一个额外的抽象客户端层来很好地处理这个问题。您可以注入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());
}
,而不是将命令处理程序直接注入到不同的演示者中,CommandHandlerProxy
在编译时只有一个开放的通用实现:
<Route path="/" component={App}>
<Route path="/" component={Auth}>
<Route path="login" component={Login} />
<Route path="register" component={Register}/>
</Route>
<Route path="about" component={About} />
<Route path="*" component={NoMatch}/>
</Route>
在一天结束时,对于任何给定的UI任务(比如用户创建请求),最终是否最终得到基于winforms客户端的查询/命令,以及最终的web api服务版本命令/查询在那一端处理请求?
没有!
客户端你应该创建一个开放的通用MATCH (n:User{user_id:304020})-[r:know]->(m:User) with m MATCH (m)-[s:like|create|share]->(o{is_active:1})
with m, s, o, (toInt(timestamp()/1000)-toInt(o.created_on))/86400 as days,
(toInt(timestamp()/1000)-toInt(o.created_on))/3600 as hours,
(1- round(o.impression_count_all/20)/50) as low_boost
with m,s,o,days,low_boost,hours,
CASE
WHEN days > 30 THEN 0.05
WHEN days >=20 AND days <=30 THEN 0.1
WHEN days >=10 AND days <=20 THEN 0.2
WHEN days >=5 AND days <=10 THEN 0.4
WHEN days >=2 AND days <=5 THEN 0.5
WHEN days =1 THEN 0.6
WHEN days < 1 THEN
CASE
WHEN hours <= 2 THEN 1
WHEN hours > 2 AND hours <= 8 THEN 0.9
WHEN hours > 8 AND hours <= 16 THEN 0.8
WHEN hours > 16 AND hours < 23 THEN 0.75
WHEN hours >= 23 AND hours <= 24 THEN 0.7
END
END as rs,
CASE
WHEN low_boost > 0 THEN low_boost
WHEN low_boost <= 0 THEN 0
END as lb
where has(o.trending_score_all) and has(o.impression_count_all) and not(o.is_featured=2)
RETURN distinct o.story_id as story_id,
(o.trending_score_all*4) as ts, (o.trending_score_all + rs + lb) as final_score,
count(s) as rel_count,max(s.activity_id) as id, toInt(o.created_on) as created_on
ORDER BY (CASE WHEN ts > 3 THEN final_score desc, rel_count desc ELSE ts) END) DESC
skip 0 limit 10;
,其任务是将命令dto传递给WebApi服务。
对于服务端体系结构,您应该阅读另一个后续内容:Writing Highly Maintainable WCF Services,它描述了一个服务器端体系结构,可以很好地处理这个问题。链接的项目还包含WebApi的实现!