首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实体对象验证和业务规则验证

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

Stack Overflow用户
提问于 2010-10-26 17:57:45
回答 2查看 1.9K关注 0票数 1

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

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

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

我想把我的验证分成两个子部分。1)将由验证块5.0处理的标量属性验证,以及2)将在业务层组件中处理的业务流程规则验证。然而,我在集中报告违规规则(标量属性验证和业务流程规则验证)方面遇到了困难。我不想按照映射的业务流程在违反中的业务规则的地方注入异常。

一个例子如下:当创建一个新的商家时,需要验证类别。因为如果某个类型类别与这个新商家相关联,则业务规则规定该商家不能在系统中出现两次。

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

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

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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2010-10-26 19:14:31

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

您可以做的是创建一个自定义异常(即命名为ValidationException),当您的验证应用程序块(VAB)报告错误或业务规则报告错误时,将抛出该异常。在表示层中,您必须捕获此ValidationException,并以用户友好的方式向最终用户报告此确切类型的异常。

对两个验证子系统使用单一的异常类型,允许您以一致的方式报告错误,并确保当您(或其他开发人员)忘记处理验证错误时,验证错误不会被忽视。处理错误应该是非常明确的IMO。当您让方法返回一个错误列表时,很容易忘记处理它们。创建自定义异常时,很容易向该异常类型添加一个包含验证错误列表的属性。这样的错误列表很容易从VAB中提取出来。我不知道您使用哪种验证系统进行业务规则验证,但从中提取错误代码列表并不难。

当然,在UI中处理此问题的最简单方法是使用try-catch

代码语言:javascript
复制
var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc

try
{
    businessCommand.Execute();
}
catch (ValidationException ex)
{
   UIValidationHelper.ReportValidationErrors(ex.Errors);
}

当然,到处都是这些try-catch语句是丑陋的,但至少这段代码很容易理解。根据您构建业务层的方式和所使用的UI技术,您可以使用更漂亮的解决方案。例如,您可以使用以下操作包装可能失败的实际操作:

代码语言:javascript
复制
var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc

UIValidationHelper.ExecuteOrDisplayErrors(() => 
{
    businessCommand.Execute();
});

UIValidationHelper将如下所示:

代码语言:javascript
复制
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可能如下所示:

代码语言:javascript
复制
var businessCommand = new CreateNewMerchantCommand();
businessCommand.Name = "Wikki";
// etc

businessCommand.ValidationErrorOccurred +=
    UIValidationHelper.DisplayValidationErrors;

businessCommand.Execute();

此示例将静态方法挂钩到命令实例的ValidationErrorOccurred事件。这里的技巧是让该命令的Execute方法捕获ValidationException,并在注册时将它们路由到ValidationErrorOccurred。当没有注册任何方法时,这个异常应该在调用堆栈中冒泡,因为一个未处理的验证异常当然不应该被忽视。

当然,可以直接从您的业务层执行此操作,但这会使您的业务层依赖于特定的验证技术。除此之外,这种方法允许客户选择以任何他们想要的方式处理验证错误,或者决定根本不处理它们(例如,当您不希望在特定用例中发生任何验证错误时)。

使用ASP.NET Web表单时,UIValidationHelperDisplayValidationErrors方法可能如下所示:

代码语言:javascript
复制
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会是什么样子。这些命令的基类可能如下所示:

代码语言:javascript
复制
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如下所示:

代码语言:javascript
复制
public class ValidationErrorEventArgs : EventArgs
{
    public ValidationErrorEventArgs(ValidationException error)
    {
        this.Error = error;
    }

    public ValidationException Error { get; private set; }
}

我希望这一切都是有意义的,并为我冗长的回答道歉:-)

祝好运。

票数 1
EN

Stack Overflow用户

发布于 2010-10-26 18:17:51

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

至于规则部分,我将查看您试图实现一些规则检查的每个对象图的访问者模式。根据发现的结果,我可能会报告这可能是一个新的嵌套对象。我个人倾向于使用访问者模式来生成XML文档或其他一些自定义嵌套类,这取决于您的效率需求。访问者模式中的实际规则可以在访问者模式之外声明,最好是以声明性方法声明。例如CheckDuplicateRecord。这将允许重用。

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

我个人使用EF的方法是使用POCO对象,因为它们允许可伸缩性。然后在UI上执行一些验证,然后在传输到业务对象层时执行一些验证,然后在EF DAL层中再次执行相同的操作。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4022555

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档