일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- SQL Server Optimizer
- execution plan
- .net
- query
- SQLServer
- SSMS
- C#
- identityserver3
- esl
- 쿼리 최적화
- 실행계획 원리
- identityserver
- ThreadPool
- IdentityServer4
- async
- MSSQL
- 영어공부
- 느린 저장프로시저
- 저장프로시저
- Dataannotation
- task
- english
- stored procedure
- slow in the application
- oauth2
- TPL
- async await
- validation
- await
- fast in ssms
- Today
- Total
Genius DM
DataAnnotation > Make a custom attribute 본문
커스텀 Attribute 만들기
지난 포스트에서 DataAnnotation 에 Attribute 들이 어떻게 동작하는 지 알아보았다. 그리고 더 복잡한 Validation 수행을 위한 IValidatableObject 에 대해 소개를 했고, 그 한계에 대해서 알아보았다. 이 포스트에서는 그 한계를 뛰어넘기 위해 취할 수 있는 방법에 대해서 알아보겠다.
커스텀 Attribute
public class RequiredAttribute : ValidationAttribute
{
.
. // 기타 Member 변수들
.
public override IsValid(object value)
{
// Validation 내용
}
.
.
.
}
이것이 존재하기 때문에 멤버 변수 혹은 오브젝트 상위에 [Required] 로 선언해서 사용할 수 있는 것이다. 그러면 [GeniusDM] 처럼 내가 직접 Annotation Attribute 를 만들 수 없을까? 당연히 가능하다 !
Step1. 클래스 생성
public class GeniusDMAttribute : ValidationAttribute
{
}
Step2. Base 클래스 Override
ValidationAttribute Base 클래스는 Validation 호출 Method 로 두 가지를 지니고 있다.
- public virtual bool IsValue(object value)
true / false 와 에러메시지를 리턴하고 싶을 때. - protected virtual ValidationResult isValue(object value, ValidationContext validationContext)
ValidationResult 를 리턴하면서, 에러메시지 및 프로퍼티 정보까지 셋업하고 싶을 때.
두 가지가 같이 구현되어 있다면 이 메서드만 호출된다는 것을 기억하자.
public class GeniusDMAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if(value == null)
{
Message = "GeniusDM Validation Tutorial - Fail !";
return false;
}
else
{
Message = "GeniusDM Validation Tutorial - Success !";
return true;
}
}
}
Step3. Annotation 부여
public class TestModel
{
[GeniusDM]
public string Test { get; set; }
}
Name 모델에 [GeniusDM] Annotation 을 부여했다. 이제 잘 동작하는 지 실행해보자.
Step4. 실행 및 결과
public void SomeMethod
{
// 1. Model 가져오기
TestModel model = new TestModel { Name = "Test" };
// 2. Result 선언
List<ValidationResult> results = new List<ValidationResult>();
// 3. Context 선언
ValidationContext context = new ValidationContext(model, null, null);
// 4. Validator 호출
var result = Validator.TryValidateObject(model, context, results, true);
// 5. 최종 결과
// result -> true;
// results.Count == 1
// results[0].ErrorMessage = "GeniusDM Validation Tutorial - Success !";
}
에러가 발생한 Property 명칭과 함께 에러메시지를 만들고 싶을 때?
public class GeniusDMAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if(value == null) {
return new ValidationResult(String.Format("GeniusDM Validation Tutorial - Fail at {0}", validationContext.DisplayName));
} else {
return new ValidationResult(String.Format("GeniusDM Validation Tutorial - Success at {0}", validationContext.DisplayName));
}
}
}
public void SomeMethod
{
// 1. Model 가져오기
TestModel model = new TestModel { Name = "Test" };
// 2. Result 선언
List<ValidationResult> results = new List<ValidationResult>();
// 3. Context 선언
ValidationContext context = new ValidationContext(model, null, null);
// 4. Validator 호출
var result = Validator.TryValidateObject(model, context, results, true);
// 5. 최종 결과
// result -> true;
// results.Count == 1
// results[0].ErrorMessage = "GeniusDM Validation Tutorial - Success at Name!";
}
IValidatableObject 호출 보장하기
- 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" }); } } }
public void SomeMethod
{
Company company = new Company();
List<ValidationResult> results = new List<ValidationResult>();
ValidationContext context = new ValidationContext(company, null, null);
Validator.TryValidateObject(company, context, results, true);
// IValidatableObject 캐스팅
IValidatableObject validatable = (IValidatableObject)company;
validatable.Validate(context);
}
그냥 Interface 하나 만들자
public interface IGeniusDMValidatableObject { IEnumerable<ValidationResult> Validate(ValidationContext context); }
public Company : IGeniusDMValidatableObject { [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(ValidationContext context) { 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.DepartmentList.Count > 10) { yield return new ValidationResult("이 회사에는 부서가 10개 이상 존재하지 않습니다.", new string[] { "DepartmentList" }); } } }
public void SomeMethod
{
Company company = new Company();
List<ValidationResult> results = new List<ValidationResult>();
ValidationContext context = new ValidationContext(company, null, null);
Validator.TryValidateObject(company, context, results, true);
IGeniusDMValidatableObject validatable = (IGeniusDMValidatableObject)company;
validatable.Validate(context);
}
public void SomeMethod
{
Company company = new Company();
List<ValidationResult> results = new List<ValidationResult>();
ValidationContext context = new ValidationContext(company, null, null);
Validator.TryValidateObject(company, context, results, true);
IGeniusDMValidatableObject validatable = (IGeniusDMValidatableObject)company;
// IValidatableObject 결과 수집
List<ValidationResult> voResults = validatable.Validate(context).ToList();
foreach(var r in voResults)
{
results.Add(r);
}
}
public void SomeMethod
{
Company company = new Company();
List<ValidationResult> results = GeniusDMValidator.TryValidateObject(company);
}
public static class GeniusDMValidator
{
public static List<ValidationResult> TryValidateObject(object target)
{
List<ValidationResult> results = new List<ValidationResult>();
ValidationContext context = new ValidationContext(company, null, null);
Validator.TryValidateObject(company, context, results, true);
IGeniusDMValidatableObject validatable = (IGeniusDMValidatableObject)company;
// IValidatableObject 결과 수집
List<ValidationResult> voResults = validatable.Validate(context).ToList();
foreach(var r in voResults)
{
results.Add(r);
}
return results;
}
}
Make a custom attribute
We looked at how attributes in DataAnnotations work in the last post. And I talked about IValidatableObject to perform more complex validation and discussed its limitations. I'm going to look at some ways we can take to overcome those limitations.
Custom Attribute
public class RequiredAttribute : ValidationAttribute
{
.
. // Etc Member Variables
.
public override IsValid(object value)
{
// Validation Content
}
.
.
.
}
You can declare [Required] right above a property or an object because this class exists. Isn't it possible to make my own annotation attribute? yes it is possible.
Step1. Make a class
public class GeniusDMAttribute : ValidationAttribute
{
}
Step2. overriding base class
ValidationAttribute Base class has two method overloadings.
- public virtual bool IsValue(object value)
when you want to return true / false and error . - protected virtual ValidationResult isValue(object value, ValidationContext validationContext)
when you want to return ValidationResult, error message, and customized property information at the same time.
Remember if you implement all of them, this one is going to be invoked by the Validator.
public class GeniusDMAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if(value == null)
{
Message = "GeniusDM Validation Tutorial - Fail !";
return false;
}
else
{
Message = "GeniusDM Validation Tutorial - Success !";
return true;
}
}
}
Step3. assign an annotation
public class TestModel
{
[GeniusDM]
public string Test { get; set; }
}
Just assigned [GeniusDM] annotation on TestModel. Let's see if it works.
Step4. Run and the result
public void SomeMethod
{
// 1. Make a model
TestModel model = new TestModel { Name = "Test" };
// 2. Declare a result list
List<ValidationResult> results = new List<ValidationResult>();
// 3. Declare a context
ValidationContext context = new ValidationContext(model, null, null);
// 4. Call the Validator's method
var result = Validator.TryValidateObject(model, context, results, true);
// 5. Final result
// result -> true;
// results.Count == 1
// results[0].ErrorMessage = "GeniusDM Validation Tutorial - Success !";
}
If you want to get the originated property name and error message.
public class GeniusDMAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if(value == null) {
return new ValidationResult(String.Format("GeniusDM Validation Tutorial - Fail at {0}", validationContext.DisplayName));
} else {
return new ValidationResult(String.Format("GeniusDM Validation Tutorial - Success at {0}", validationContext.DisplayName));
}
}
}
public void SomeMethod
{
// 1. Make a model
TestModel model = new TestModel { Name = "Test" };
// 2. Declare a result list
List<ValidationResult> results = new List<ValidationResult>();
// 3. Declare a context
ValidationContext context = new ValidationContext(model, null, null);
// 4. Call the Validator's method
var result = Validator.TryValidateObject(model, context, results, true);
// 5. Final result.
// result -> true;
// results.Count == 1
// results[0].ErrorMessage = "GeniusDM Validation Tutorial - Success at Name!";
}
Make sure to invoke IValidatableObject implementation no matter what
- Force to invoke IValidatableObject implementation
- Make a new interface.
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 employee info", new string[] { "DepartmentList & EmployeeList" }); } if(company.DepartmentList.Count > 10) { yield return new ValidationResult("This company doesn't have more than 10 departments.", new string[] { "DepartmentList" }); } } }
public void SomeMethod
{
Company company = new Company();
List<ValidationResult> results = new List<ValidationResult>();
ValidationContext context = new ValidationContext(company, null, null);
Validator.TryValidateObject(company, context, results, true);
// IValidatableObject casting
IValidatableObject validatable = (IValidatableObject)company;
validatable.Validate(context);
}
Just making an interface will simply solve this problem.
public interface IGeniusDMValidatableObject { IEnumerable<ValidationResult> Validate(ValidationContext context); }
public Company : IGeniusDMValidatableObject { [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(ValidationContext context) { 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 employee info", new string[] { "DepartmentList & EmployeeList" }); } if(company.DepartmentList.Count > 10) { yield return new ValidationResult("This company doesn't have more than 10 departments.", new string[] { "DepartmentList" }); } } }
public void SomeMethod
{
Company company = new Company();
List<ValidationResult> results = new List<ValidationResult>();
ValidationContext context = new ValidationContext(company, null, null);
Validator.TryValidateObject(company, context, results, true);
IGeniusDMValidatableObject validatable = (IGeniusDMValidatableObject)company;
validatable.Validate(context);
}
public void SomeMethod
{
Company company = new Company();
List<ValidationResult> results = new List<ValidationResult>();
ValidationContext context = new ValidationContext(company, null, null);
Validator.TryValidateObject(company, context, results, true);
IGeniusDMValidatableObject validatable = (IGeniusDMValidatableObject)company;
// Collecting IValidatableObject results.
List<ValidationResult> voResults = validatable.Validate(context).ToList();
foreach(var r in voResults)
{
results.Add(r);
}
}
public void SomeMethod
{
Company company = new Company();
List<ValidationResult> results = GeniusDMValidator.TryValidateObject(company);
}
public static class GeniusDMValidator
{
public static List<ValidationResult> TryValidateObject(object target)
{
List<ValidationResult> results = new List<ValidationResult>();
ValidationContext context = new ValidationContext(company, null, null);
Validator.TryValidateObject(company, context, results, true);
IGeniusDMValidatableObject validatable = (IGeniusDMValidatableObject)company;
// Collecting IValidatableObject results.
List<ValidationResult> voResults = validatable.Validate(context).ToList();
foreach(var r in voResults)
{
results.Add(r);
}
return results;
}
}
'.NET' 카테고리의 다른 글
IdentityServer > Storing an access token in IdentityServer3 (0) | 2018.07.08 |
---|---|
DataAnnotation > Recursive validation for collection items (0) | 2017.12.29 |
DataAnnotation > DataAnnotation Validation and IValidatableObject (0) | 2017.12.18 |
No assembly found containing an OwinStartupAttribute. (0) | 2017.11.29 |
Windows 10 > 마우스 우클릭 화면 깜빡임 (1) | 2017.11.23 |