일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- identityserver3
- IdentityServer4
- MSSQL
- async
- task
- english
- 영어공부
- 느린 저장프로시저
- C#
- 저장프로시저
- query
- await
- slow in the application
- fast in ssms
- SSMS
- execution plan
- oauth2
- SQLServer
- 실행계획 원리
- .net
- TPL
- 쿼리 최적화
- esl
- Dataannotation
- ThreadPool
- validation
- async await
- SQL Server Optimizer
- identityserver
- stored procedure
- Today
- Total
Genius DM
DataAnnotation > DataAnnotation Validation and IValidatableObject 본문
DataAnnotation > DataAnnotation Validation and IValidatableObject
Damon Jung 2017. 12. 18. 08:55
DataAnnotation Validation and IValidatableObject
DataAnnotation?
[Required] 와 같이 클래스에 선언된 멤버 변수의 유효성을 검사하고자 할 때 사용되는 Annotation 을 의미한다. 데이터 유형이 명확한 데이터 타입에 대한 유효성을 검증하기 위해 만들어졌다. 그렇기 때문에 아래와 같은 유효성 검증 객체가 이미 포함되어 있고, 주요 Attribute 만 정리하면 아래와 같다.
- CreditCardAttribute
- EmailAddressAttribute
- MaxLengthAttribute
- MinLengthAttribute
- StringLengthAttribute
- UrlAttribute
- RequiredAttribute
- PhoneAttribute
IValidatableObject?
어떻게 동작할까?
샘플 모델
public Company { [Required] public string CompanyName { get; set; } [Range(1, int.MaxValue)] public int Employees { get; set; } public List<Employee> EmployeeList { get; set; } public List<Department> DepartmentList { get; set; } }
어플리케이션 코드에서 벨리데이션 수행 방법
public void SomeValidationLogic() { Company company = new Company(); List<ValidationResult> results = new List<ValidationResult>(); ValidationContext context = new ValidationContext(company, null, null); if(Validator.TryValidateObject(company, context, results, true)) { // Validation passed ! } else { // Validation failed ! } }
위 코드에서 볼 수 있듯, 모델에 대한 DataAnnotation Validation 을 수행을 수동으로 하려면 3개의 준비물이 필요하다.
-
ValidationResult
Validator 를 호출할 때 List 로 넘겨주면, 에러 발생시 해당 리스트에 ValidationResult 객체가 추가된다.
기본적으로 ErrorMessage 와 MemberNames 를 string 리스트를 멤버 변수로 지니고 있어, 여기에 에러 데이터가 바인딩된다.
-
ValidationContext
벨리데이션 과정으로 생각하면 된다. IServiceProvider 라는 인터페이스를 Dependency Injection 으로 활용할 수 있는데, 여기선 제외한다.
-
Validator
Static Helper 클래스이다. 사용자는 TryValidateObject 메서드 정도만 알아도 된다.
- [Required] 결과 >> CompanyName 이 필요합니다.
- [Range] 결과 >> Employees 는 1 과 2,147,483,647 사이에 값이여야 합니다.
public void SomeValidationLogic() { Company company = new Company(); List<ValidationResult> results = new List<ValidationResult>(); ValidationContext context = new ValidationContext(company, null, null); if(Validator.TryValidateObject(company, context, results, true)) { // Validation passed ! } else { foreach(var e in results) { string ErrorMessage = e.ErrorMessage; // 한 개 라면.. MemberNames 는 여러 개 일 수도 있다. string PropertyName = e.MemberNames.FirstOrDefault(); } } }
더 복잡한 Validation 을 수행하려면? IValidatableObject
public Company : IValidatableObject { [Required] public string CompanyName { get; set; } [Range(1, int.MaxValue)] public int Employees { get; set; } public List<Employee> EmployeeList { get; set; } public List<Department> DepartmentList { get; set; } public IEnumerable<ValidationResult> Validate(object value) { Company company = value as company; if(company != null) { if(company.EmployeeList.Count <= 0 && company.DepartmentList.Count <= 0) { // ValidationResult(" Error Message ", " MemberNames " ) yield return new ValidationResult("부서 정보와 직원 정보가 없습니다", new string[] { "DepartmentList & EmployeeList" }); } if(company.EmployeeList.Count > 10) { yield return new ValidationResult("이 회사에는 부서가 10개 이상 존재하지 않습니다.", new string[] { "DepartmentList" }); } } }
IValidatableObject 주의사항
- [Required]
- 기타 Attribute
- IValidatableObject Implementation
if(Validator.TryValidateObject(company, context, results, true)) // << 이 마지막 녀석. { // Validation passed ! } else { // Validation failed ! }
저 true 의 의미는 validateAllProperties 이다. 문자 그대로, 모든 프로퍼티를 검사 할 것이냐, 말 것이냐를 의미한다. true 로 해주면, Company 의 대한 검사 결과에 Range Attribute 결과까지 포함될 것이다. False 인 경우는 [ Required ] 만 검사한다. 다른 Attribute 는 무시된다. 어플리케이션 개발시 1차 벨리데이션 결과만 갖고 판단해도 충분한 상황이라면 괜찮지만, 모든 에러 결과를 1차적으로 알고 난 이후에 판단해야 하는 경우도 존재한다.
DataAnnotation Validation 에서는 결국 정석만 가지고는 해당 요구사항을 만족시킬 수 없다. 어떻게 해야 될까? 다음 포스트에서 커스텀 Attribute 만드는 방법과, IValidatableObject 를 Property 레벨에서 에러가 발생했어도 호출을 보장할 수 있는 방법에 대하여 다뤄보도록 하겠다.
DataAnnotation Validation and IValidatableObject
DataAnnotation?
Means annotations that validate member variables in a class like [Required]. The main purpose is to validate data types that are explicit and have a clear format. So .NET already has a series of classes for certain data validation, see the list below.
- CreditCardAttribute
- EmailAddressAttribute
- MaxLengthAttribute
- MinLengthAttribute
- StringLengthAttribute
- UrlAttribute
- RequiredAttribute
- PhoneAttribute
IValidatableObject?
How it works?
Sample Model
public Company { [Required] public string CompanyName { get; set; } [Range(1, int.MaxValue)] public int Employees { get; set; } public List<Employee> EmployeeList { get; set; } public List<Department> DepartmentList { get; set; } }
Perform validation in application code
public void SomeValidationLogic() { Company company = new Company(); List<ValidationResult> results = new List<ValidationResult>(); ValidationContext context = new ValidationContext(company, null, null); if(Validator.TryValidateObject(company, context, results, true)) { // Validation passed ! } else { // Validation failed ! } }
As you can see above, you will need three prerequisites to manually perform validation on a model.
-
ValidationResult
It has ErrorMessage and MemberNames as its member variable. The error message and the property name will be bound automatically while the validation process. And you just toss the list of ValidationResult into TryValidateObject method, and it internally adds the result. -
ValidationContext
It's literally a context of validation process. It provides IServiceProvider for you to perform DI ( Dependency Injection ) later, but I'm not going to skip it.
-
Validator
It's a static helper class. All you have to remember is TryValidatObject.
- [Required] validation result >> CompanyName is required.
- [Range] validation result >> Employees value should be in range between 1 and 2,147,483,647.
public void SomeValidationLogic() { Company company = new Company(); List<ValidationResult> results = new List<ValidationResult>(); ValidationContext context = new ValidationContext(company, null, null); if(Validator.TryValidateObject(company, context, results, true)) { // Validation passed ! } else { foreach(var e in results) { string ErrorMessage = e.ErrorMessage; // Suitable if there is only one property name. It could have multiple names. string PropertyName = e.MemberNames.FirstOrDefault(); } } }
If you want more complex validation... use IValidatableObject
public Company : IValidatableObject { [Required] public string CompanyName { get; set; } [Range(1, int.MaxValue)] public int Employees { get; set; } public List<Employee> EmployeeList { get; set; } public List<Department> DepartmentList { get; set; } public IEnumerable<ValidationResult> Validate(object value) { Company company = value as company; if(company != null) { if(company.EmployeeList.Count <= 0 && company.DepartmentList.Count <= 0) { // ValidationResult(" Error Message ", " MemberNames " ) yield return new ValidationResult("No Department and Employees information", new string[] { "DepartmentList & EmployeeList" }); } if(company.EmployeeList.Count > 10) { yield return new ValidationResult("This company doesn't have more than 10 departments", new string[] { "DepartmentList" }); } } }
Caution on IValidatableObject
- [Required]
- Other attributes
- IValidatableObject Implementation
if(Validator.TryValidateObject(company, context, results, true)) // << That last parameter. { // Validation passed ! } else { // Validation failed ! }
It means validateAllProperties. It is literally a decision to check all the properties or not. If you set true, it will validate all the annotations in the Company model, including [Required] and [Range]. If you set false, it will validate [Required] property only. All the other annotations will be completely ignored. It's quite alright when validating [Required] will suffice for you, but there's a certain circumstances that you need a total set of validation results.
Seeing the limitations on data annotations and IValidatableObject implementation, it's impossible to make sure to validate all properties and invoke IValidatableObject implementation in a single validation context. How can I achieve that? I'm going to write another post about how to make a custom attribute and a way to guarantee to call the interface implemented method even if there's an prior error in the validation context.
'.NET' 카테고리의 다른 글
DataAnnotation > Recursive validation for collection items (0) | 2017.12.29 |
---|---|
DataAnnotation > Make a custom attribute (0) | 2017.12.22 |
No assembly found containing an OwinStartupAttribute. (0) | 2017.11.29 |
Windows 10 > 마우스 우클릭 화면 깜빡임 (1) | 2017.11.23 |
Azure > Azure Remote Debugging (0) | 2017.09.03 |