我的问题是关于在 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
方法吗?
我会对此进行一些解释,提前谢谢!
属性本身只是属性的"装饰器"。如果没有调用它,它将不会自动执行或使用。
但是,在您的情况下,当您可以使用属性本身时,我看不出使用属性的意义:
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
。这样,代码变得更加面向未来,因为您可以随时针对接口进行编程并添加新的验证属性。
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();
}