我正在开发具有从EF 4.0创建的业务对象的应用程序。应用程序由具有类存储库的数据层分层。上面是一个业务层,其中根据需求映射业务流程。业务层调用数据层。
业务对象具有复杂的关联。一种场景:商家有多个地址商家属于一个类别商家有一个账户账户有一个钱包
用户将创建一个新的商家与上述业务对象(图)。在创建和更新商家时,有各种业务规则。
我想把我的验证分成两个子部分。1)将由验证块5.0处理的标量属性验证,以及2)将在业务层组件中处理的业务流程规则验证。然而,我在集中报告违规规则(标量属性验证和业务流程规则验证)方面遇到了困难。我不想按照映射的业务流程在违反中的业务规则的地方注入异常。
一个例子如下:当创建一个新的商家时,需要验证类别。因为如果某个类型类别与这个新商家相关联,则业务规则规定该商家不能在系统中出现两次。
现在,当UI将新的商家图传递给业务层组件时,我首先验证BO标量属性验证(通过验证块进行验证),然后将此BO传递到业务流程规则验证方法中,以检查各种规则。我想报告一些可能的违规点,不允许持久化商家和图形对象。
请分享任何有价值的设计方法,以管理集中验证规则日志和报告到UI层。
编辑:我不想在EF SaveChanges,ChangeAssociation等事件处理器上加入验证。
发布于 2010-10-26 19:14:31
虽然你说你不想抛出异常,但我发现抛出异常非常有效。特别是当您有多个子系统验证系统时,抛出一种类型的异常作为外观将非常有效。
您可以做的是创建一个自定义异常(即命名为ValidationException),当您的验证应用程序块(VAB)报告错误或业务规则报告错误时,将抛出该异常。在表示层中,您必须捕获此ValidationException,并以用户友好的方式向最终用户报告此确切类型的异常。
对两个验证子系统使用单一的异常类型,允许您以一致的方式报告错误,并确保当您(或其他开发人员)忘记处理验证错误时,验证错误不会被忽视。处理错误应该是非常明确的IMO。当您让方法返回一个错误列表时,很容易忘记处理它们。创建自定义异常时,很容易向该异常类型添加一个包含验证错误列表的属性。这样的错误列表很容易从VAB中提取出来。我不知道您使用哪种验证系统进行业务规则验证,但从中提取错误代码列表并不难。
当然,在UI中处理此问题的最简单方法是使用try-catch
var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc
try
{
businessCommand.Execute();
}
catch (ValidationException ex)
{
UIValidationHelper.ReportValidationErrors(ex.Errors);
}当然,到处都是这些try-catch语句是丑陋的,但至少这段代码很容易理解。根据您构建业务层的方式和所使用的UI技术,您可以使用更漂亮的解决方案。例如,您可以使用以下操作包装可能失败的实际操作:
var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc
UIValidationHelper.ExecuteOrDisplayErrors(() =>
{
businessCommand.Execute();
});UIValidationHelper将如下所示:
public static class UIValidationHelper
{
public static void ExecuteOrDisplayErrors (Action action)
{
try
{
action();
}
catch (ValidationException ex)
{
// Show the errors in your UI technology
ShowErrorMessage(ex.Errors);
}
}
}我过去使用过的另一种选择是通过使用事件扩展业务层。要实现这一点,您需要一个诸如命令之类的结构,就像我在示例中使用的那样。使用事件时,UI可能如下所示:
var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc
businessCommand.ValidationErrorOccurred +=
UIValidationHelper.DisplayValidationErrors;
businessCommand.Execute();此示例将静态方法挂钩到命令实例的ValidationErrorOccurred事件。这里的技巧是让该命令的Execute方法捕获ValidationException,并在注册时将它们路由到ValidationErrorOccurred。当没有注册任何方法时,这个异常应该在调用堆栈中冒泡,因为一个未处理的验证异常当然不应该被忽视。
当然,可以直接从您的业务层执行此操作,但这会使您的业务层依赖于特定的验证技术。除此之外,这种方法允许客户选择以任何他们想要的方式处理验证错误,或者决定根本不处理它们(例如,当您不希望在特定用例中发生任何验证错误时)。
使用ASP.NET Web表单时,UIValidationHelper的DisplayValidationErrors方法可能如下所示:
public static class UIValidationHelper
{
public static void DisplayValidationErrors(
object sender, ValidationErrorEventArgs e)
{
Page page = GetValidPageFromCurrentHttpContext();
var summary = GetValidationSummaryFromPage()
foreach (var result in e.Error.Results)
{
summary.Controls.Add(new CustomValidator
{
ErrorMessage = result.Message,
IsValid = false
});
}
}
private static Page GetValidPageFromCurrentHttpContext()
{
return (Page)HttpContext.Current.CurrentHandler;
}
private ValidationSummary GetValidationSummaryFromPage(Page page)
{
return page.Controls.OfType<ValidationSummary>().First();
}
}此静态帮助器方法将报告的错误的消息注入到页上的ValidationSummary控件中。它期望页面在页面的根级别包含一个ValidationSummary控件。可以很容易地创建更灵活的解决方案。
我想向您展示的最后一件事是,采用此解决方案时BusinessCommand会是什么样子。这些命令的基类可能如下所示:
public abstract class BusinessCommand
{
public event EventHandler<ValidationErrorEventArgs>
ValidationErrorOccurred;
public void Execute()
{
try
{
this.ExecuteInternal();
}
catch (ValidationException ex)
{
if (this.ValidationErrorOccurred != null)
{
var e = new ValidationErrorEventArgs(ex);
this.ValidationErrorOccurred(this, e);
}
else
{
// Not rethrowing here would be a bad thing!
throw;
}
}
}
protected abstract void ExecuteInternal();
}ValidationErrorEventArgs如下所示:
public class ValidationErrorEventArgs : EventArgs
{
public ValidationErrorEventArgs(ValidationException error)
{
this.Error = error;
}
public ValidationException Error { get; private set; }
}我希望这一切都是有意义的,并为我冗长的回答道歉:-)
祝好运。
发布于 2010-10-26 18:17:51
对象成员验证可以在封装的业务对象中完成。是在setter属性中执行,还是作为单独的方法调用执行,由您决定。如果对象被设置为无效值,应用程序是否允许错误类型的数据进入系统,范围检查等。
至于规则部分,我将查看您试图实现一些规则检查的每个对象图的访问者模式。根据发现的结果,我可能会报告这可能是一个新的嵌套对象。我个人倾向于使用访问者模式来生成XML文档或其他一些自定义嵌套类,这取决于您的效率需求。访问者模式中的实际规则可以在访问者模式之外声明,最好是以声明性方法声明。例如CheckDuplicateRecord。这将允许重用。
将所有这些保持在与业务层相同的层中,但进一步将业务层细分为规则验证层和业务对象。
我个人使用EF的方法是使用POCO对象,因为它们允许可伸缩性。然后在UI上执行一些验证,然后在传输到业务对象层时执行一些验证,然后在EF DAL层中再次执行相同的操作。
https://stackoverflow.com/questions/4022555
复制相似问题