IValidatableObject를 어떻게 사용합니까?
나는 이해 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.
*/
}
}
}
우선, 올바른 리소스를 알려주는 @ 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;
}
}
Validator.TryValidateProperty()
유효성 검사에 실패하면를 사용 하면 결과 컬렉션에 추가됩니다. 실패한 유효성 검사가 없으면 성공을 나타내는 결과 컬렉션에 추가되지 않습니다.
유효성 검사하기 :
public void DoValidation()
{
var toValidate = new ValidateMe()
{
Enable = true,
Prop1 = 1,
Prop2 = 2
};
bool validateAllProperties = false;
var results = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(
toValidate,
new ValidationContext(toValidate, null, null),
results,
validateAllProperties);
}
validateAllProperties
이 방법이 작동하려면 false 로 설정 해야합니다. 때 validateAllProperties
인과 거짓 전용 속성 [Required]
속성이 확인됩니다. 이를 통해 IValidatableObject.Validate()
메소드가 조건부 유효성 검증을 처리 할 수 있습니다 .
유효성 검사기를 사용하여 유효성 검사 개체 및 속성에 대한 Jeff Handley의 블로그 게시물에서 인용 :
객체의 유효성을 검사 할 때 Validator.ValidateObject에 다음 프로세스가 적용됩니다.
- 특성 레벨 속성 검증
- 유효성 검사기가 유효하지 않은 경우 유효성 검사를 중단하여 실패를 반환합니다.
- 객체 레벨 속성 검증
- 유효성 검사기가 유효하지 않은 경우 유효성 검사를 중단하여 실패를 반환합니다.
- 데스크탑 프레임 워크에서 오브젝트가 IValidatableObject를 구현하는 경우 Validate 메소드를 호출하고 실패를 리턴하십시오.
이는 2 단계에서 유효성 검사가 중단되므로 시도하려는 작업이 기본적으로 작동하지 않음을 나타냅니다. 기본 유효성 검사를 수행하기 전에 기본 제공 속성에서 상속되는 속성을 만들고 인터페이스를 통해 활성화 된 속성이 있는지 확인하십시오. 또는 Validate
메소드 에서 엔티티 유효성 검증을위한 모든 로직을 넣을 수 있습니다.
몇 가지 사항을 추가하면됩니다.
때문에 Validate()
메서드 서명 반환 IEnumerable<>
, yield return
유유히 결과를 생성하는 데 사용할 수 있습니다 -이 도움이되는 유효성 검사 중 일부는 IO 또는 CPU를 많이가있는 경우.
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (this.Enable)
{
// ...
if (this.Prop1 > this.Prop2)
{
yield return new ValidationResult("Prop1 must be larger than Prop2");
}
Also, if you are using MVC ModelState
, you can convert the validation result failures to ModelState
entries as follows (this might be useful if you are doing the validation in a custom model binder):
var resultsGroupedByMembers = validationResults
.SelectMany(vr => vr.MemberNames
.Select(mn => new { MemberName = mn ?? "",
Error = vr.ErrorMessage }))
.GroupBy(x => x.MemberName);
foreach (var member in resultsGroupedByMembers)
{
ModelState.AddModelError(
member.Key,
string.Join(". ", member.Select(m => m.Error)));
}
I implemented a general usage abstract class for validation
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;
}
}
}
The problem with the accepted answer is that it now depends on the caller for the object to be properly validated. I would either remove the RangeAttribute and do the range validation inside the Validate method or I would create a custom attribute subclassing RangeAttribute that takes the name of the required property as an argument on the constructor.
For example:
[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;
}
}
I liked cocogza's answer except that calling base.IsValid resulted in a stack overflow exception as it would re-enter the IsValid method again and again. So I modified it to be for a specific type of validation, in my case it was for an e-mail address.
[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;
}
}
This works much better! It doesn't crash and produces a nice error message. Hope this helps someone!
참고URL : https://stackoverflow.com/questions/3400542/how-do-i-use-ivalidatableobject
'IT박스' 카테고리의 다른 글
라텍스에서 목록을 여러 열로 나누기 (0) | 2020.06.02 |
---|---|
소수 열에 돈 저장-정밀도와 스케일은? (0) | 2020.06.02 |
MongoDB 데이터베이스 파일 크기 줄이기 (0) | 2020.06.02 |
디자인 패턴 : 팩토리 vs 팩토리 메소드 vs 추상 팩토리 (0) | 2020.06.02 |
'fs : 기본 모듈 소스 재평가는 지원되지 않습니다'를 수정하는 방법-graceful-fs (0) | 2020.06.02 |