如何在非 ASP.net 上下文中使用 C# 中的数据验证属性?

发布于 2024-09-24 13:22:42 字数 79 浏览 0 评论 0原文

我想在库程序集中使用数据验证属性,以便数据的任何使用者都可以验证它,而无需使用 ModelBinder(例如在控制台应用程序中)。我该怎么做呢?

I'd like to use the data validation attributes in a library assembly, so that any consumer of the data can validate it without using a ModelBinder (in a console application, for instance). How can I do it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

猫性小仙女 2024-10-01 13:22:42

实际上这很酷。我最近在粮食署验证实施中使用了它。大多数人最终都会使用反射编写大量代码来迭代属性,但有一个内置函数可以实现此目的。

var vc = new ValidationContext(myObject, null, null);
return Validator.TryValidateObject(myObject, vc, null, true);

您还可以验证单个命名属性的属性。您还可以选择传入列表以访问错误消息:

var results = new List<ValidationResult>();
var vc = new ValidationContext(myObject, null, null) { MemberName = "UserName"};
var isValid = Validator.TryValidateProperty(value, vc, results);

// get all the errors
var errors = Array.ConvertAll(results.ToArray(), o => o.ErrorMessage);

Actually this is pretty cool. I used it in a WFP validation implementation recently. Most people end up writing lots of code using reflection to iterate the attributes, but there's a built in function for this.

var vc = new ValidationContext(myObject, null, null);
return Validator.TryValidateObject(myObject, vc, null, true);

You can also validate attributes on a single named property. You can also optionally pass in a list in order to access the error messages :

var results = new List<ValidationResult>();
var vc = new ValidationContext(myObject, null, null) { MemberName = "UserName"};
var isValid = Validator.TryValidateProperty(value, vc, results);

// get all the errors
var errors = Array.ConvertAll(results.ToArray(), o => o.ErrorMessage);
篱下浅笙歌 2024-10-01 13:22:42

System.ComponentModel.DataAnnotations.ValidationAttribute 类具有执行验证逻辑的 IsValid 方法。它们接受一个对象(它们修饰的字段的值)并返回 true 或 false。

您可以使用这些属性和一些反射来推出您自己的面向方面的验证器。向验证器传递一个对象,验证器将获取 PropertyInfoFieldInfo 的列表。对于其中的每一个,它都可以调用 GetCustomAttributes 来查找从 ValidationAttribute 继承的属性,并且对于其中的每一个,调用 IsValid,传递属性或字段的值。这可以完全动态地完成,无需知道设计时要验证的类的结构。

The System.ComponentModel.DataAnnotations.ValidationAttribute classes have IsValid methods that perform the validation logic. They take an Object (the value of the field they decorate) and return true or false.

You can use these attributes, and a little reflection, to roll your own aspect-oriented validator. Pass your validator an object, and the validator will get a list of PropertyInfos and FieldInfos. For each of these, it can call GetCustomAttributes to look for those that inherit from ValidationAttribute, and for each of these, call IsValid, passing the value of the property or field. This can be done totally dynamically without knowing the structure of the class to be validated at design-time.

木緿 2024-10-01 13:22:42

TryValidateProperty 写得很糟糕 - 你必须跳过重重困难才能让它在控制器之外工作,即使这样,如果你使用它两次,它最终会悄悄地将 ModelState 设置为有效/无效,并且停止改变该状态,并从那时起停止返回准确的结果。

我放弃了它,只是编写了自己的验证器。这将循环任何上下文中的任何对象集并告诉您它们是否有效:

bool isValid = true;
var invalidFields = new List<string>();

foreach (var o in viewModels)
{
    var properties = o.GetType()
        .GetProperties(BindingFlags.Public | BindingFlags.Instance);
    foreach(var prop in properties)
    {
        var attrs = prop.GetCustomAttributes(true);
        if (attrs != null)
        {
            var val = prop.GetValue(o);
            ValidationAttribute[] validatorAttrs = attrs
                .Where(a => a is ValidationAttribute)
                .Select(a => (ValidationAttribute)a).ToArray();

            foreach(var attr in validatorAttrs)
            {                       
                bool thisFieldIsValid = attr.IsValid(val);
                if (!thisFieldIsValid)
                    invalidFields.Add(prop.Name);

                isValid = isValid && thisFieldIsValid;
            }
        }
    }
}

TryValidateProperty is just badly written - you have to jump through hoops to get it to work outside a Controller, and even then, if you use it twice, it will end up quietly setting ModelState to valid/invalid and stop alterring that state, and stop returning accurate results from then on.

I gave up on it and just wrote my own validator. This'll loop over any set of objects in any context and tell you if they're valid:

bool isValid = true;
var invalidFields = new List<string>();

foreach (var o in viewModels)
{
    var properties = o.GetType()
        .GetProperties(BindingFlags.Public | BindingFlags.Instance);
    foreach(var prop in properties)
    {
        var attrs = prop.GetCustomAttributes(true);
        if (attrs != null)
        {
            var val = prop.GetValue(o);
            ValidationAttribute[] validatorAttrs = attrs
                .Where(a => a is ValidationAttribute)
                .Select(a => (ValidationAttribute)a).ToArray();

            foreach(var attr in validatorAttrs)
            {                       
                bool thisFieldIsValid = attr.IsValid(val);
                if (!thisFieldIsValid)
                    invalidFields.Add(prop.Name);

                isValid = isValid && thisFieldIsValid;
            }
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文