【问题标题】:C# custom attribute validation in console environment控制台环境中的 C# 自定义属性验证
【发布时间】:2018-10-14 03:30:11
【问题描述】:

我的问题是关于在 C# 中使用自定义属性进行验证。

我不太明白验证的工作原理。我已经声明了一个包含验证规则的属性,但是当错误应该被抛出时它不是。

属性:

[AttributeUsage(AttributeTargets.Property)]
public class NotNullAttribute : Attribute
{
    public bool IsValid(object value)
    {
        if (value is string && (string)value != "")
        {
            return false;
        }
        return true;
    }

}

在属性内部,我检查属性是否为字符串类型以及其值是否为空字符串,因为这是我必须检查的内容。

任务是检查属性是否为string,如果是空字符串则无效,否则无效。

我的Person班级:

class Person
{
    [NotNull]
    public string Name { get; set; }
}

我在这里应用自定义属性。

主要方法:

class Program
{
    static void Main(string[] args)
    {
        Person p1 = new Person();
        p1.Name = "";

        Console.WriteLine("Validation done");
        Console.ReadKey();
    }
}

这是我实例化Person 类并将空字符串分配给Name 属性的地方。我猜这就是应该抛出错误的地方。

所以我的问题是为什么不应用验证?我应该以某种方式从它自身的属性中调用IsValid 方法吗?

我会对此进行一些解释,提前谢谢!

【问题讨论】:

    标签: c# validation attributes


    【解决方案1】:

    属性本身只是属性的“装饰器”。如果没有调用它,它不会被自动执行或使用。

    但是,在您的情况下,当您可以使用属性本身时,我看不到使用属性的意义:

    private string _name = "";
    
    public string Name
    {
       get
       {
          return _name;
       }
       set
       {
          if ( string.IsNullOrEmpty(value) )
          {
              //throw or fallback
          }
          else
          {
              _name = value;
          }
       }
    }
    

    进行基本的值验证正是属性设置器非常适合的工作。如果有人使用了无效值,您可以抛出异常,或者设置一个备用值。

    如果您仍然更喜欢使用属性,您仍然需要一些代码来执行验证本身。而且,除非执行验证,否则任何人都可以为该属性分配任何有效值。

    例如ASP.NET MVC 在模型绑定期间使用属性验证 - 它检查绑定模型类上的验证属性并在操作方法开始执行之前对其进行验证。

    属性验证示例

    下面是一个简单示例,说明如何使您的代码与反射一起工作。

    首先是验证属性的略微更新版本:

    [AttributeUsage(AttributeTargets.Property)]
    public class NotNullAttribute : Attribute
    {
        public bool IsValid(object value)
        {
            if (!string.IsNullOrEmpty(value as string))
            {
                return false;
            }
            return true;
        }
    }
    

    您的代码实际上只允许 null"" 值,我猜这与您想要的相反。此版本仅在字符串不为null 且不为空时有效。

    现在在 Program 类中创建一个 Validate 方法:

    private static bool Validate(object model)
    {
        foreach (var propertyInfo in model.GetType().GetProperties())
        {                
            foreach (var attribute in propertyInfo.GetCustomAttributes(true))
            {
                var notNullAttribute = attribute as NotNullAttribute;
                if (notNullAttribute != null)
                {
                    if (!notNullAttribute.IsValid(propertyInfo.GetValue(model)))
                    {
                        return false;
                    }
                }
            }
        }
        return true;
    }
    

    这基本上收集了传入参数类型的所有属性,检查NotNullAttribute的所有属性,然后针对model中的当前值执行属性的IsValid方法。

    最后,您可以通过Main 调用它:

    static void Main(string[] args)
    {
        Person p1 = new Person();
        p1.Name = "d";
    
        if (Validate(p1))
        {
            Console.WriteLine("Valid");
        }
        else
        {
            Console.WriteLine("Invalid");
        }
    
        Console.WriteLine("Validation done");
        Console.ReadKey();
    }
    

    现在,如果您打算添加更多验证属性,我会先创建一个接口:

    public interface IValidationAttribute
    {
        bool IsValid(object value);
    }
    

    然后从IValidationAttribute 派生所有验证属性,并在Validate 方法中使用IValidationAttribute 代替NotNullAttribute。通过这种方式,代码变得更加面向未来,因为您可以针对接口进行编程并随时添加新的验证属性。

    【讨论】:

    • 关键是我需要按照我在学校作业描述中描述的方式解决任务,这就是为什么我用我的方法寻求帮助
    • 我已经更新了我的答案,并举例说明了这如何与属性一起使用
    • 很高兴它有帮助,编码愉快:-)!
    【解决方案2】:
     public class BankAccount  
       {  
           public enum AccountType  
           {  
               Saving,  
               Current  
           }  
           [Required(ErrorMessage="First Name Required")]  
           [MaxLength(15,ErrorMessage="First Name should not more than 1`5 character")]  
           [MinLength(3,ErrorMessage="First Name should be more than 3 character")]  
           public string AccountHolderFirstName { get; set; }  
           [Required(ErrorMessage="Last Name Required")]  
           [MaxLength(15,ErrorMessage="Last Name should not more than 1`5 character")]  
           [MinLength(3,ErrorMessage="Last Name should be more than 3 character")]  
           public string AccountHolderLastName { get; set; }  
           [Required]  
    [RegularExpression("^[0-9]+$", ErrorMessage = "Only Number allowed in AccountNumber")]  
           public string AccountNumber { get; set; }  
    
           public AccountType AcType { get; set; }  
    
           [AccountBalaceCheckAttribute]  
           public double AccountBalance { get; set; }  
       }  
    

    如何验证

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

    例子

    static void Main(string[] args)  
    {  
        var bankAccount = new BankAccount();  
        ICollection<ValidationResult> lstvalidationResult;  
    
        bool valid = GenericValidator.TryValidate(bankAccount, out lstvalidationResult);  
        if (!valid)  
        {  
            foreach (ValidationResult res in lstvalidationResult)  
            {  
                Console.WriteLine(res.MemberNames +":"+ res.ErrorMessage);  
            }  
    
        }  
        Console.ReadLine();  
    }  
    

    【讨论】:

    • 您能否更详细地解释一下 TryValidate() 方法?我不明白 ValidationResult 和 ValidationContext 类型是什么,它们是预定义的还是来自框架?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-05
    相关资源
    最近更新 更多