【问题标题】:ASP.NET MVC: How to execute Data Annotation validations in the service layer?ASP.NET MVC:如何在服务层执行数据注释验证?
【发布时间】:2009-10-13 20:25:56
【问题描述】:

在最近提出的一个问题中: ASP.NET MVC: Is Data Annotation Validation Enough?

...得出的结论是,依靠数据注释验证(由模型绑定器触发)不足以确保始终执行验证。我们仍然需要在服务层(或 ModelBinding 发生后的其他地方)添加相同的验证逻辑。不幸的是,我们将复制我们的验证代码(一次是使用数据注释,一次是在服务层)。服务层是否有一种简单的方法可以根据数据注释中定义的内容触发验证?如果这可以实现,那么我们将两全其美……我们不需要重复验证代码,但我们仍将确保始终执行验证。

【问题讨论】:

    标签: asp.net-mvc asp.net-mvc-2 data-annotations


    【解决方案1】:

    在此博客的帮助下:http://goneale.com/2009/03/04/using-metadatatype-attribute-with-aspnet-mvc-xval-validation-framework/ 我能够创建一个方法,该方法将根据数据注释定义的验​​证来测试我的对象。它将执行从 ValidateAttribute 派生的任何验证属性。我现在可以将我的对象从我的服务层(或 DomainModel)传递给这个方法,并且我的服务层不再依赖于控制器。这将确保在将数据持久化到数据库之前始终执行验证。我无法按原样使用博客上的代码,因为我似乎无法访问 Graham 使用的某些扩展方法,所以这是我的版本:

        public static IList<KeyValuePair<string, string>> GetErrors(object obj)
        {
            // get the name of the buddy class for obj
            MetadataTypeAttribute metadataAttrib = obj.GetType().GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault() as MetadataTypeAttribute;
    
            // if metadataAttrib is null, then obj doesn't have a buddy class, and in such a case, we'll work with the model class
            Type buddyClassOrModelClass = metadataAttrib != null ? metadataAttrib.MetadataClassType : obj.GetType();
    
            var buddyClassProperties = TypeDescriptor.GetProperties(buddyClassOrModelClass).Cast<PropertyDescriptor>();
            var modelClassProperties = TypeDescriptor.GetProperties(obj.GetType()).Cast<PropertyDescriptor>();
    
            var errors = from buddyProp in buddyClassProperties
                               join modelProp in modelClassProperties on buddyProp.Name equals modelProp.Name // as this is an inner join, it will return only the properties that are in both the buddy and model classes
                               from attribute in buddyProp.Attributes.OfType<ValidationAttribute>() // get only the attributes of type ValidationAttribute
                               where !attribute.IsValid(modelProp.GetValue(obj))
                               select new KeyValuePair<string, string>(buddyProp.Name, attribute.FormatErrorMessage(string.Empty));
    
            return errors.ToList();
        }
    

    此代码适用于有和没有伙伴类的两个类,但如果您不使用伙伴类,则可以稍微简化此代码。 我希望你觉得这很有用。

    【讨论】:

    • 好东西。我还看到 .Net 4.0 在 System.ComponentModel.DataAnnotations 中有 Validator 类。这也可能在未来的服务层中有用。
    • 您能否提供更多关于您的代码中的“伙伴”是什么的信息?
    • @user3625699 那是很久以前的事了,但我相信伙伴类是相关的部分类,它们为自动生成的类添加了额外的装饰(例如自定义属性)。这允许您在不更改自动生成的类本身的情况下扩展自动生成的类。您不会想要更改自动生成的类,因为它们将在下次运行类生成器时被覆盖。我希望这是有道理的。
    • @JohnyOshika 啊谢谢,所以“伙伴”是“装饰模式”的一种形式,也就是具有额外功能的包装器,同时实现了原始接口(“是”原始类),做我猜对了吗?
    • @user3625699 我认为不需要实现原始接口。自从我研究它已经有一段时间了,但我相信多个部分类在编译时会合并为一个类。不过,这不是 100% 确定的。
    【解决方案2】:

    您在上一个问题中没有检查my answer 吗?
    我提交了一些基于 DTO 的 DataAnnotation 属性进行自动验证的代码。只要您的 DTO 在您的控制器操作的参数中使用,它们就会被此属性拾取并无论如何都进行验证。

    唯一的问题是:如何生成 DTO?

    1. 是你自己写的吗?
    2. 您使用的是 EF 还是类似的东西?
    3. 您是否使用其他技术(如 T4)自动生成它们?

    如果你可以控制你的 DTO 类的生成,那么你也可以为它们添加额外的接口。我发布的代码使用 T4 over EF、xVal 和 DataAnnotation 以及声明在每个实体类中实现的 Validate() 方法的自定义接口。

    【讨论】:

    • 感谢您的回答,Robert,但您的解决方案仍然依赖于执行模型绑定然后调用 ModelState.IsValid 的控制器操作。它看起来是一个非常好的解决方案,我可以实施,但我也在寻找一种绝对确定的方式来执行验证,然后再持久化到数据库,并且必须在服务(或域)中完成模型)层。所以我仍然希望有一种简单的方法可以将数据注释验证插入我的服务层。
    【解决方案3】:

    我想达到同样的效果并尝试了约翰尼的回答。在您进行与其他属性相关的验证之前,它可以正常工作,例如使用 RequiredIf 属性。

    我最终在 System.ComponentModel.DataAnnotations 中使用了 Validator 类,这实际上是为此而设计的,并且应该应用与通常应用完全相同的完整逻辑。

    这是向您展示如何使用 Validator 类执行此操作的示例方法。

        public static bool TryValidate(object obj, List<ValidationResult> results = null)
        {
            var context = new ValidationContext(obj, serviceProvider: null, items: null);
            return Validator.TryValidateObject(obj, context, results, true);
        }
    

    【讨论】:

      猜你喜欢
      • 2012-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-06
      • 1970-01-01
      • 1970-01-01
      • 2012-07-13
      • 1970-01-01
      相关资源
      最近更新 更多