实体对象验证和业务规则验证

时间:2010-10-26 09:57:45

标签: validation object entity-framework-4

我正在开发具有从EF 4.0创建的业务对象的应用程序。该应用程序与具有类存储库的数据层分层。上面是业务流程,业务流程根据需求进行映射。业务层正在调用数据层。

业务对象具有复杂的关联。一种情况: 商家有多个地址 商家属于一个类别 商家有一个帐户 帐户有钱包

用户将与上述业务对象(图表)一起创建新商家。创建和更新商家时有各种业务规则。

我想将验证分为两个子部分。 1)由验证块5.0和2处理的标量属性验证业务流程规则要在业务层组件中处理的验证。但是,我在使用集中方式报告损坏的规则(标量属性验证和业务流程规则验证)时遇到了困难。我不希望在映射的业务流程中违反业务规则的地方注入异常。

一个例子如下: 创建新商家时,需要验证类别。因为如果某个类型类别与此新商家相关联,那么业务规则会说这样的商家在系统中不能存在两次。

现在,当UI将新商家图表传递给业务层组件时,我首先验证BO标量属性验证(验证过的验证块),然后将此BO传递到业务流程规则验证方法以检查各种规则。我想报告可能的违规点,并且不允许持久保存商家和图表对象。

请分享任何有价值的设计方法,以管理集中验证规则记录和向UI层报告。

编辑:我不想在EF SaveChanges,ChangeAssociation等事件处理程序上加入验证。

2 个答案:

答案 0 :(得分:1)

虽然你说你不想抛出异常,但我发现抛出异常非常有效。特别是当您有多个子系统验证系统时,将一种类型的异常作为外观抛出将非常有效。

您可以执行的操作是创建自定义异常(即名为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窗体时,DisplayValidationErrors的{​​{1}}方法可能如下所示:

UIValidationHelper

此静态帮助程序方法将报告的错误消息注入页面上的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

我希望这对我的长篇答案都有意义和抱歉: - )

祝你好运。

答案 1 :(得分:0)

对象成员验证可以在封装的业务对象中完成。无论是在setter属性中执行此操作还是作为单独的方法调用,都取决于您。如果对象设置为无效的值,应该是否应用程序允许数据进入错误类型的系统,范围检查等。

对于规则部分,我会查看您尝试实现某些规则检查的每个对象图的访问者模式。我可能会根据找到的内容将其报告为新的嵌套对象。我个人对此报告方面的偏好是使用访问者模式生成XML文档或其他一些自定义嵌套类,具体取决于您的效率需求。访问者模式中的实际规则可以在访问者模式之外声明,最好是以声明方式声明。例如CheckDuplicateRecord。这将允许重用。

将所有这些保留在与业务层相同的层中,但进一步将业务层细分为规则验证层和Business Objects。

我使用EF的个人方法是使用POCO对象,因为它们具有可扩展性。然后我会在UI上进行一些验证,然后在传输到Business Object层时进行一些验证,然后在EF DAL层再次执行相同的操作。