C# CQR中的验证和同步命令
我喜欢在没有事件来源的情况下尝试CQR的想法。 但我不知道如何解决我需要给用户即时反馈的问题 这就是我当前注册的方式(简化为理解) 我用简洁的方式读,用nHibernate写 注册控制器.csC# CQR中的验证和同步命令,c#,validation,nhibernate,cqrs,C#,Validation,Nhibernate,Cqrs,我喜欢在没有事件来源的情况下尝试CQR的想法。 但我不知道如何解决我需要给用户即时反馈的问题 这就是我当前注册的方式(简化为理解) 我用简洁的方式读,用nHibernate写 注册控制器.cs public ActionResult signup(UserCreateModel model) { // model validation (email, password strength etc) if (!ModelState.IsValid
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// use service layer to validate against database and create user
var registermodel = _userService.create_user_account(model.username, model.email, model.password);
// service returns and object with several states
if (registermodel.Status == UserRegistrationStatus.Ok)
{
// user registered ok, display thank you or whatever
}
if (registermodel.Status == UserRegistrationStatus.DuplicatedUsername)
{
// duplicated username found, back to form and show errors
}
if (registermodel.Status == UserRegistrationStatus.DuplicatedEmail)
{
// duplicated email found, back to form and show errors
}
// get back to form and show errors
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// validate duplicated email
bool is_email_duplicated = _read.user_email_exists(model.email);
// duplicated email found, back to form and show errors
// validate duplicated username
bool is_username_duplicated = _read.user_username_exists(model.username);
// duplicated username found, back to form and show errors
// assume all is perfect and dispatch
_commandDispatcher.Dispatch(new CreateUserCommand(model.username, model.email, model.password));
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// dispatch and validate inside the handler, abort execution if validation failed
var command = new CreateUserCommand(model.username, model.email, model.password)
// attached common feedback object to the command and deal with errors
if(command.status == UserRegistrationStatus.DuplicatedUsername)
{
// get back to form and show errors
}
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
var feedback = _userService.create_user(model.username, model.email, model.password);
// check for status and return feedback to the user
}
public Feedback create_user(string username, string email, string password)
{
// validate duplicated email
bool is_email_duplicated = _read.user_email_exists(email);
// duplicated email found, back to form and show errors
// validate duplicated username
bool is_username_duplicated = _read.user_username_exists(username);
// duplicated username found, back to form and show errors
// dispatch command
_commandDispatcher.Dispatch(new CreateUserCommand(username, email, password));
}
哪种方法对CQR最友好?
方法1
注册控制器.cs
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// use service layer to validate against database and create user
var registermodel = _userService.create_user_account(model.username, model.email, model.password);
// service returns and object with several states
if (registermodel.Status == UserRegistrationStatus.Ok)
{
// user registered ok, display thank you or whatever
}
if (registermodel.Status == UserRegistrationStatus.DuplicatedUsername)
{
// duplicated username found, back to form and show errors
}
if (registermodel.Status == UserRegistrationStatus.DuplicatedEmail)
{
// duplicated email found, back to form and show errors
}
// get back to form and show errors
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// validate duplicated email
bool is_email_duplicated = _read.user_email_exists(model.email);
// duplicated email found, back to form and show errors
// validate duplicated username
bool is_username_duplicated = _read.user_username_exists(model.username);
// duplicated username found, back to form and show errors
// assume all is perfect and dispatch
_commandDispatcher.Dispatch(new CreateUserCommand(model.username, model.email, model.password));
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// dispatch and validate inside the handler, abort execution if validation failed
var command = new CreateUserCommand(model.username, model.email, model.password)
// attached common feedback object to the command and deal with errors
if(command.status == UserRegistrationStatus.DuplicatedUsername)
{
// get back to form and show errors
}
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
var feedback = _userService.create_user(model.username, model.email, model.password);
// check for status and return feedback to the user
}
public Feedback create_user(string username, string email, string password)
{
// validate duplicated email
bool is_email_duplicated = _read.user_email_exists(email);
// duplicated email found, back to form and show errors
// validate duplicated username
bool is_username_duplicated = _read.user_username_exists(username);
// duplicated username found, back to form and show errors
// dispatch command
_commandDispatcher.Dispatch(new CreateUserCommand(username, email, password));
}
如果我需要在系统中的其他地方执行相同的验证(我会复制代码),该怎么办
我考虑过创建ValidationService
若命令因某种原因“爆炸”,用户将得到错误的反馈,该怎么办
方法2
注册控制器.cs
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// use service layer to validate against database and create user
var registermodel = _userService.create_user_account(model.username, model.email, model.password);
// service returns and object with several states
if (registermodel.Status == UserRegistrationStatus.Ok)
{
// user registered ok, display thank you or whatever
}
if (registermodel.Status == UserRegistrationStatus.DuplicatedUsername)
{
// duplicated username found, back to form and show errors
}
if (registermodel.Status == UserRegistrationStatus.DuplicatedEmail)
{
// duplicated email found, back to form and show errors
}
// get back to form and show errors
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// validate duplicated email
bool is_email_duplicated = _read.user_email_exists(model.email);
// duplicated email found, back to form and show errors
// validate duplicated username
bool is_username_duplicated = _read.user_username_exists(model.username);
// duplicated username found, back to form and show errors
// assume all is perfect and dispatch
_commandDispatcher.Dispatch(new CreateUserCommand(model.username, model.email, model.password));
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// dispatch and validate inside the handler, abort execution if validation failed
var command = new CreateUserCommand(model.username, model.email, model.password)
// attached common feedback object to the command and deal with errors
if(command.status == UserRegistrationStatus.DuplicatedUsername)
{
// get back to form and show errors
}
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
var feedback = _userService.create_user(model.username, model.email, model.password);
// check for status and return feedback to the user
}
public Feedback create_user(string username, string email, string password)
{
// validate duplicated email
bool is_email_duplicated = _read.user_email_exists(email);
// duplicated email found, back to form and show errors
// validate duplicated username
bool is_username_duplicated = _read.user_username_exists(username);
// duplicated username found, back to form and show errors
// dispatch command
_commandDispatcher.Dispatch(new CreateUserCommand(username, email, password));
}
基本上在处理程序内部,我欺骗和验证(向nHibernate repo添加额外的方法)
方法3
与第一种方法类似,但将验证和分派封装在UserService中
注册控制器.cs
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// use service layer to validate against database and create user
var registermodel = _userService.create_user_account(model.username, model.email, model.password);
// service returns and object with several states
if (registermodel.Status == UserRegistrationStatus.Ok)
{
// user registered ok, display thank you or whatever
}
if (registermodel.Status == UserRegistrationStatus.DuplicatedUsername)
{
// duplicated username found, back to form and show errors
}
if (registermodel.Status == UserRegistrationStatus.DuplicatedEmail)
{
// duplicated email found, back to form and show errors
}
// get back to form and show errors
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// validate duplicated email
bool is_email_duplicated = _read.user_email_exists(model.email);
// duplicated email found, back to form and show errors
// validate duplicated username
bool is_username_duplicated = _read.user_username_exists(model.username);
// duplicated username found, back to form and show errors
// assume all is perfect and dispatch
_commandDispatcher.Dispatch(new CreateUserCommand(model.username, model.email, model.password));
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// dispatch and validate inside the handler, abort execution if validation failed
var command = new CreateUserCommand(model.username, model.email, model.password)
// attached common feedback object to the command and deal with errors
if(command.status == UserRegistrationStatus.DuplicatedUsername)
{
// get back to form and show errors
}
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
var feedback = _userService.create_user(model.username, model.email, model.password);
// check for status and return feedback to the user
}
public Feedback create_user(string username, string email, string password)
{
// validate duplicated email
bool is_email_duplicated = _read.user_email_exists(email);
// duplicated email found, back to form and show errors
// validate duplicated username
bool is_username_duplicated = _read.user_username_exists(username);
// duplicated username found, back to form and show errors
// dispatch command
_commandDispatcher.Dispatch(new CreateUserCommand(username, email, password));
}
userservice.cs
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// use service layer to validate against database and create user
var registermodel = _userService.create_user_account(model.username, model.email, model.password);
// service returns and object with several states
if (registermodel.Status == UserRegistrationStatus.Ok)
{
// user registered ok, display thank you or whatever
}
if (registermodel.Status == UserRegistrationStatus.DuplicatedUsername)
{
// duplicated username found, back to form and show errors
}
if (registermodel.Status == UserRegistrationStatus.DuplicatedEmail)
{
// duplicated email found, back to form and show errors
}
// get back to form and show errors
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// validate duplicated email
bool is_email_duplicated = _read.user_email_exists(model.email);
// duplicated email found, back to form and show errors
// validate duplicated username
bool is_username_duplicated = _read.user_username_exists(model.username);
// duplicated username found, back to form and show errors
// assume all is perfect and dispatch
_commandDispatcher.Dispatch(new CreateUserCommand(model.username, model.email, model.password));
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
// dispatch and validate inside the handler, abort execution if validation failed
var command = new CreateUserCommand(model.username, model.email, model.password)
// attached common feedback object to the command and deal with errors
if(command.status == UserRegistrationStatus.DuplicatedUsername)
{
// get back to form and show errors
}
}
public ActionResult signup(UserCreateModel model)
{
// model validation (email, password strength etc)
if (!ModelState.IsValid)
{
// get back to form and show errors
}
var feedback = _userService.create_user(model.username, model.email, model.password);
// check for status and return feedback to the user
}
public Feedback create_user(string username, string email, string password)
{
// validate duplicated email
bool is_email_duplicated = _read.user_email_exists(email);
// duplicated email found, back to form and show errors
// validate duplicated username
bool is_username_duplicated = _read.user_username_exists(username);
// duplicated username found, back to form and show errors
// dispatch command
_commandDispatcher.Dispatch(new CreateUserCommand(username, email, password));
}
我喜欢这种方法,但我觉得它将成为一种Baklava代码。通常在使用CQR时,您希望使用乐观的方法 这个想法是在发出命令之前验证您的输入(可能是针对字符串格式或电子邮件的唯一性的简单验证) 显然,在实际生成
命令
时,您将仔细检查数据,以确保命令
处于有效和安全的状态(这同样适用于所有其他对象)
基于此,您可以假设您的命令将正确调度,并使用乐观的方法向用户提供积极的反馈
如果您的CommandHandler
无法处理您的命令
可能会引发异常
,您可以捕获并相应地通知用户
以Messenger/Facebook为例,当您键入并发送消息时,UI会让您认为一切都进行得很顺利,消息也已发送,但是,如果发生什么情况,您的UI将回滚。正如您所看到的,有多种可能的方法来处理命令的发送。有时,您会看到人们严格遵守从命令处理程序返回void,但也有ACK/NACK(已确认/未确认)响应的方法
我这样做的方式是,我的命令处理程序实现的Dispatch()方法将始终返回两种可能的状态之一,即ACK或NACK。这告诉我,我试图发送的命令是否被认为处于可以应用于系统的状态。然而,与ACK/NACK的简单枚举不同,每个命令都能够返回确认类。该类包含一个状态(ACK/NACK),以及一个或多个命令失败。这样,如果我有一个ACK,我知道该命令已收到,我可以假设它将被处理。另一方面,如果我得到一个NACK,我有一个失败,我可以格式化并呈现给用户。但是,我在任何时候都不会返回任何与发送后状态相关的信息。我报告的故障(同一个命令可能有多个故障)完全基于命令中的数据,与应用于系统的数据无关
这一切与你的第三个例子密切相关。上述方法的不同之处在于,封装故障和状态将允许您使用命令呈现/跟踪所有问题,而不是第一个故障
下面是我用来完成此任务的简单类/枚举
确认
namespace Commands
{
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Provides an ACK/NACK for an issued <see cref="Command" />. Can represent one of two states,
/// being either Acknowledged or Not Acknowledged, along with the ability to track unhandled faults.
/// </summary>
/// <remarks>
/// ACK/NACK implies a synchronous command execution. Asynchronous commands, while more rarely used,
/// should represent the concept of command acknowledgement through events.
/// </remarks>
public sealed class Acknowledgement
{
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="Acknowledgement"/> class.
/// </summary>
/// <remarks>
/// This is representative of an <see cref="AcknowledgementState.Acknowledged" /> state, with
/// no command failures nor faults.
/// </remarks>
public Acknowledgement()
{
this.State = AcknowledgementState.Acknowledged;
this.CommandFailures = new List<CommandValidationFailure>();
}
/// <summary>
/// Initializes a new instance of the <see cref="Acknowledgement"/> class.
/// </summary>
/// <param name="failures">The command validation failures that led to NACK.</param>
/// <remarks>
/// This is representative of a <see cref="AcknowledgementState.NotAcknowledged" /> state, with
/// at least one command validation failure and no fault.
/// </remarks>
public Acknowledgement(IEnumerable<CommandValidationFailure> failures)
{
this.State = AcknowledgementState.NotAcknowledged;
this.CommandFailures = failures;
}
/// <summary>
/// Initializes a new instance of the <see cref="Acknowledgement"/> class.
/// </summary>
/// <param name="fault">The fault that led to the NACK.</param>
/// <remarks>
/// This is representative of a <see cref="AcknowledgementState.NotAcknowledged" /> state, with
/// a fault and no command validation failures.
/// </remarks>
public Acknowledgement(Exception fault)
{
this.State = AcknowledgementState.NotAcknowledged;
this.Fault = fault;
}
#endregion
#region Public Properties
/// <summary>
/// Gets the command failures that led to a NACK, if any.
/// </summary>
/// <value>
/// The command failures, if present.
/// </value>
public IEnumerable<CommandValidationFailure> CommandFailures { get; }
/// <summary>
/// Gets the fault that led to a NACK, if present.
/// </summary>
/// <value>
/// The fault.
/// </value>
public Exception Fault { get; }
/// <summary>
/// Gets a value indicating whether this <see cref="Acknowledgement" /> is backed by a fault.
/// </summary>
/// <value>
/// <c>true</c> if this instance is reflective of a fault; otherwise, <c>false</c>.
/// </value>
public bool IsFaulted => this.Fault != null;
/// <summary>
/// Gets a value indicating whether this <see cref="Acknowledgement" /> is backed by command validation failures.
/// </summary>
/// <value>
/// <c>true</c> if this instance is reflective of command failures; otherwise, <c>false</c>.
/// </value>
public bool IsInvalid => this.CommandFailures != null && this.CommandFailures.Any();
/// <summary>
/// Gets the state of this instance, in terms of an ACK or NACK.
/// </summary>
/// <value>
/// The state representation.
/// </value>
public AcknowledgementState State { get; }
#endregion
}
}
名称空间命令
{
使用制度;
使用System.Collections.Generic;
使用System.Linq;
///
///为已发布状态提供ACK/NACK。可以表示两种状态之一,
///被确认或未被确认,以及跟踪未处理故障的能力。
///
///
///ACK/NACK意味着同步命令执行。异步命令虽然很少使用,
///应表示通过事件确认命令的概念。
///
公开密封类确认函
{
#区域构造函数
///
///初始化类的新实例。
///
///
///这是一个国家的代表,具有
///没有命令失败或错误。
///
公开承认
{
this.State=AcknowledgementState.Acknowledged;
this.CommandFailures=新列表();
}
///
///初始化类的新实例。
///
///导致NACK的命令验证失败。
///
///这是一个国家的代表,具有
///至少有一个命令验证失败且无故障。
///
公开确认(IEnumerable failures)
{
this.State=AcknowledgementState.NotAcknowledged;
this.CommandFailures=失败;
}
///
///初始化类的新实例。
///
///导致NACK的故障。
///
///这是一个国家的代表,具有
///出现故障且没有命令验证失败。
///
公开承认(例外错误)
{
this.State=AcknowledgementState.NotAcknowledged;
该故障=故障;
}
#端区
#区域公共财产
///
///获取导致NACK的命令失败(如果有)。
///
///
///命令失败(如果存在)。
///
公共IEnumerable命令失败{get;}
///
///获取导致NACK的故障(如果存在)。
///