Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/257.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用IValidatableObject?_C#_Asp.net_Ivalidatableobject - Fatal编程技术网

C# 如何使用IValidatableObject?

C# 如何使用IValidatableObject?,c#,asp.net,ivalidatableobject,C#,Asp.net,Ivalidatableobject,我知道,IValidatableObject用于验证对象,使人们能够相互比较属性 我仍然希望使用属性来验证单个属性,但我希望在某些情况下忽略某些属性的失败 我是否试图在下面的案例中错误地使用它?如果没有,我该如何实现这一点 public class ValidateMe : IValidatableObject { [Required] public bool Enable { get; set; } [Range(1, 5)] public int Prop1

我知道,
IValidatableObject
用于验证对象,使人们能够相互比较属性

我仍然希望使用属性来验证单个属性,但我希望在某些情况下忽略某些属性的失败

我是否试图在下面的案例中错误地使用它?如果没有,我该如何实现这一点

public class ValidateMe : IValidatableObject
{
    [Required]
    public bool Enable { get; set; }

    [Range(1, 5)]
    public int Prop1 { get; set; }

    [Range(1, 5)]
    public int Prop2 { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (!this.Enable)
        {
            /* Return valid result here.
             * I don't care if Prop1 and Prop2 are out of range
             * if the whole object is not "enabled"
             */
        }
        else
        {
            /* Check if Prop1 and Prop2 meet their range requirements here
             * and return accordingly.
             */ 
        }
    }
}
public类ValidateMe:IValidatableObject
{
[必需]
公共bool启用{get;set;}
[范围(1,5)]
公共int Prop1{get;set;}
[范围(1,5)]
公共int Prop2{get;set;}
公共IEnumerable验证(ValidationContext ValidationContext)
{
如果(!this.Enable)
{
/*在此处返回有效结果。
*我不在乎Prop1和Prop2是否超出范围
*如果整个对象未“启用”
*/
}
其他的
{
/*在此检查Prop1和Prop2是否满足其范围要求
*并相应地返回。
*/ 
}
}
}
引用自:

验证对象时 以下过程应用于 Validator.ValidateObject:

  • 验证属性级别属性
  • 如果任何验证器无效,则中止验证并返回 失败
  • 验证对象级属性
  • 如果任何验证器无效,则中止验证并返回 失败
  • 如果在桌面框架上,对象实现 IValidatableObject,然后调用其 验证方法并返回任何 失败
  • 这表明您尝试执行的操作无法立即执行,因为验证将在步骤2中止。您可以尝试创建从内置属性继承的属性,并在执行其正常验证之前(通过接口)专门检查是否存在已启用的属性。或者,您可以将验证实体的所有逻辑放在
    Validate
    方法中


    您还可以先看一下
    验证程序

    的具体实现,感谢@paper1337为我指出了正确的资源…我没有注册,所以我不能投票支持他,如果其他人读到了这篇文章,请这样做

    下面是如何完成我想做的事情

    可验证类:

    public class ValidateMe : IValidatableObject
    {
        [Required]
        public bool Enable { get; set; }
    
        [Range(1, 5)]
        public int Prop1 { get; set; }
    
        [Range(1, 5)]
        public int Prop2 { get; set; }
    
        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var results = new List<ValidationResult>();
            if (this.Enable)
            {
                Validator.TryValidateProperty(this.Prop1,
                    new ValidationContext(this, null, null) { MemberName = "Prop1" },
                    results);
                Validator.TryValidateProperty(this.Prop2,
                    new ValidationContext(this, null, null) { MemberName = "Prop2" },
                    results);
    
                // some other random test
                if (this.Prop1 > this.Prop2)
                {
                    results.Add(new ValidationResult("Prop1 must be larger than Prop2"));
                }
            }
            return results;
        }
    }
    

    validateAllProperties
    设置为false对于此方法的工作非常重要。当
    validateAllProperties
    为false时,仅检查具有
    [必需]
    属性的属性。这允许
    IValidatableObject.Validate()
    方法处理条件验证。

    只需添加几点:

    因为
    Validate()

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (this.Enable)
        {
            // ...
            if (this.Prop1 > this.Prop2)
            {
                yield return new ValidationResult("Prop1 must be larger than Prop2");
            }
    

    我实现了一个用于验证的通用抽象类

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    
    namespace App.Abstractions
    {
        [Serializable]
        abstract public class AEntity
        {
            public int Id { get; set; }
    
            public IEnumerable<ValidationResult> Validate()
            {
                var vResults = new List<ValidationResult>();
    
                var vc = new ValidationContext(
                    instance: this,
                    serviceProvider: null,
                    items: null);
    
                var isValid = Validator.TryValidateObject(
                    instance: vc.ObjectInstance,
                    validationContext: vc,
                    validationResults: vResults,
                    validateAllProperties: true);
    
                /*
                if (true)
                {
                    yield return new ValidationResult("Custom Validation","A Property Name string (optional)");
                }
                */
    
                if (!isValid)
                {
                    foreach (var validationResult in vResults)
                    {
                        yield return validationResult;
                    }
                }
    
                yield break;
            }
    
    
        }
    }
    
    使用系统;
    使用System.Collections.Generic;
    使用System.ComponentModel.DataAnnotations;
    名称空间App.Abstractions
    {
    [可序列化]
    抽象公共类实体
    {
    公共int Id{get;set;}
    公共IEnumerable验证()
    {
    var vResults=新列表();
    var vc=新的ValidationContext(
    实例:这个,,
    服务提供者:null,
    项目:空);
    var isValid=Validator.TryValidateObject(
    实例:vc.ObjectInstance,
    validationContext:vc,
    验证结果:vResults,
    validateAllProperties:true);
    /*
    如果(真)
    {
    返回新的ValidationResult(“自定义验证”,“属性名称字符串(可选)”);
    }
    */
    如果(!isValid)
    {
    foreach(vResults中的var validationResult)
    {
    收益率验证结果;
    }
    }
    屈服断裂;
    }
    }
    }
    
    接受答案的问题是,现在要正确验证对象,它取决于调用者。我要么删除RangeAttribute并在Validate方法中进行范围验证,要么创建一个自定义属性子类RangeAttribute,该子类将所需属性的名称作为构造函数的参数

    例如:

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    class RangeIfTrueAttribute : RangeAttribute
    {
        private readonly string _NameOfBoolProp;
    
        public RangeIfTrueAttribute(string nameOfBoolProp, int min, int max) : base(min, max)
        {
            _NameOfBoolProp = nameOfBoolProp;
        }
    
        public RangeIfTrueAttribute(string nameOfBoolProp, double min, double max) : base(min, max)
        {
            _NameOfBoolProp = nameOfBoolProp;
        }
    
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var property = validationContext.ObjectType.GetProperty(_NameOfBoolProp);
            if (property == null)
                return new ValidationResult($"{_NameOfBoolProp} not found");
    
            var boolVal = property.GetValue(validationContext.ObjectInstance, null);
    
            if (boolVal == null || boolVal.GetType() != typeof(bool))
                return new ValidationResult($"{_NameOfBoolProp} not boolean");
    
            if ((bool)boolVal)
            {
                return base.IsValid(value, validationContext);
            }
            return null;
        }
    }
    
    除了调用base.IsValid会导致堆栈溢出异常,因为它会一次又一次地重新输入IsValid方法之外,我很喜欢它。因此,我将其修改为用于特定类型的验证,在我的示例中,它用于电子邮件地址

    [AttributeUsage(AttributeTargets.Property)]
    class ValidEmailAddressIfTrueAttribute : ValidationAttribute
    {
        private readonly string _nameOfBoolProp;
    
        public ValidEmailAddressIfTrueAttribute(string nameOfBoolProp)
        {
            _nameOfBoolProp = nameOfBoolProp;
        }
    
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (validationContext == null)
            {
                return null;
            }
    
            var property = validationContext.ObjectType.GetProperty(_nameOfBoolProp);
            if (property == null)
            {
                return new ValidationResult($"{_nameOfBoolProp} not found");
            }
    
            var boolVal = property.GetValue(validationContext.ObjectInstance, null);
    
            if (boolVal == null || boolVal.GetType() != typeof(bool))
            {
                return new ValidationResult($"{_nameOfBoolProp} not boolean");
            }
    
            if ((bool)boolVal)
            {
                var attribute = new EmailAddressAttribute {ErrorMessage = $"{value} is not a valid e-mail address."};
                return attribute.GetValidationResult(value, validationContext);
            }
            return null;
        }
    }
    

    这个效果更好!它不会崩溃,并生成一条漂亮的错误消息。希望这对别人有帮助

    我不喜欢iValidate的一点是它似乎只在所有其他验证之后运行。
    此外,至少在我们的站点中,它会在尝试保存时再次运行。我建议您只需创建一个函数并将所有验证代码放在其中。或者,对于网站,您可以在创建模型后在控制器中进行“特殊”验证。例如:

     public ActionResult Update([DataSourceRequest] DataSourceRequest request, [Bind(Exclude = "Terminal")] Driver driver)
        {
    
            if (db.Drivers.Where(m => m.IDNumber == driver.IDNumber && m.ID != driver.ID).Any())
            {
                ModelState.AddModelError("Update", string.Format("ID # '{0}' is already in use", driver.IDNumber));
            }
            if (db.Drivers.Where(d => d.CarrierID == driver.CarrierID
                                    && d.FirstName.Equals(driver.FirstName, StringComparison.CurrentCultureIgnoreCase)
                                    && d.LastName.Equals(driver.LastName, StringComparison.CurrentCultureIgnoreCase)
                                    && (driver.ID == 0 || d.ID != driver.ID)).Any())
            {
                ModelState.AddModelError("Update", "Driver already exists for this carrier");
            }
    
            if (ModelState.IsValid)
            {
                try
                {
    

    不错!在Validate方法中使用属性和反射值得吗?我想不出会在哪种情况下使用它。如果表中有跟踪列(例如创建跟踪列的用户),您可以给我一个使用此功能的示例。它在数据库中是必需的,但是您可以在上下文中插入SaveChanges来填充它(无需开发人员记住显式设置它)。当然,您需要在保存之前进行验证。因此,您没有按要求标记“creator”列,而是根据所有其他列/属性进行验证。此解决方案的问题是,现在您依赖调用者来正确验证对象。要增强此功能,请
    [AttributeUsage(AttributeTargets.Property)]
    class ValidEmailAddressIfTrueAttribute : ValidationAttribute
    {
        private readonly string _nameOfBoolProp;
    
        public ValidEmailAddressIfTrueAttribute(string nameOfBoolProp)
        {
            _nameOfBoolProp = nameOfBoolProp;
        }
    
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (validationContext == null)
            {
                return null;
            }
    
            var property = validationContext.ObjectType.GetProperty(_nameOfBoolProp);
            if (property == null)
            {
                return new ValidationResult($"{_nameOfBoolProp} not found");
            }
    
            var boolVal = property.GetValue(validationContext.ObjectInstance, null);
    
            if (boolVal == null || boolVal.GetType() != typeof(bool))
            {
                return new ValidationResult($"{_nameOfBoolProp} not boolean");
            }
    
            if ((bool)boolVal)
            {
                var attribute = new EmailAddressAttribute {ErrorMessage = $"{value} is not a valid e-mail address."};
                return attribute.GetValidationResult(value, validationContext);
            }
            return null;
        }
    }
    
     public ActionResult Update([DataSourceRequest] DataSourceRequest request, [Bind(Exclude = "Terminal")] Driver driver)
        {
    
            if (db.Drivers.Where(m => m.IDNumber == driver.IDNumber && m.ID != driver.ID).Any())
            {
                ModelState.AddModelError("Update", string.Format("ID # '{0}' is already in use", driver.IDNumber));
            }
            if (db.Drivers.Where(d => d.CarrierID == driver.CarrierID
                                    && d.FirstName.Equals(driver.FirstName, StringComparison.CurrentCultureIgnoreCase)
                                    && d.LastName.Equals(driver.LastName, StringComparison.CurrentCultureIgnoreCase)
                                    && (driver.ID == 0 || d.ID != driver.ID)).Any())
            {
                ModelState.AddModelError("Update", "Driver already exists for this carrier");
            }
    
            if (ModelState.IsValid)
            {
                try
                {