.Net 6.0 Üzerinde Validation Factory Yaratmak Part 2
Selamlar,
Bugün bir önceki makalede kaldığımız yerden, yani bir sınıfın propertylerini işaretleme amaçlı kullanılan attributelerin tanımlamasından devam edeceğiz. Hadi gelin tüm işaretleyicileri sıra ile tanımlayalım.
Attributes/DateData: Aşağıda tanımlanan DateData attribute’ü, tanımlandığı property’deki tarih alanını kontrol eder. Tek property’si, yıl değerinin kontrol edildiği “minYear”‘dır. Girilen tarih alanının, bundan büyük olması beklenir.
1 2 3 4 5 6 7 8 9 10 11 |
namespace ValidationFactory.Attributes { [AttributeUsage(AttributeTargets.All)] public class DateData : System.Attribute { public DateData() { } public int minYear{ get; set; } } } |
Attributes/EmailData: Mail alanlarının, EmailValidateType enum propertysi ile, farklı bussineslar için kontrol edilmesini sağlayan bir işaretleyicidir.
1 2 3 4 5 6 7 8 9 10 11 |
namespace ValidationFactory.Attributes { [AttributeUsage(AttributeTargets.All)] public class EmailData : System.Attribute { public EmailData() { } public EmailValidateType type { get; set; } } } |
Attributes/EncryptData : İşaretlendiği property alanının, çift yönlü şifrelenmesini sağlayan bir işaretleyicidir.
1 2 3 4 5 6 7 8 9 10 |
namespace ValidationFactory.Attributes { [AttributeUsage(AttributeTargets.All)] public class EncryptData : System.Attribute { public EncryptData() { } } } |
Attributes/HashData: İşaretlendiği property alanının tek yönlü, yani geri dönülemez olarak şifrelenmesini sağlayan, bir işaretleyicidir.
1 2 3 4 5 6 7 8 9 10 11 |
namespace ValidationFactory.Attributes { [AttributeUsage(AttributeTargets.All)] public class HashData : System.Attribute { public HashData() { } public int minYear{ get; set; } } } |
Attribute/StringData: İşaretlendiği property alanının, 3 farklı bussines için kontrol edilmesini sağlayan bir işaretleyicidir. Diğer Attributelardan farklı olarak 2 property’si vardır.
1 2 3 4 5 6 7 8 9 10 11 12 |
namespace ValidationFactory.Attributes { [AttributeUsage(AttributeTargets.All)] public class StringData : System.Attribute { public StringData() { } public int max { get; set; } public int min { get; set; } } } |
Validators/ValidatorFactory: Amaç, sınıf üzerindeki herbir property Attribute’una göre, uygun validator’ın çekilmesidir. Singleton Design Pattern’ine göre, tüm validatorlar bir seferlik yaratılıp, static bir IValidator Dictionary içine konulurlar. Daha sonra, istenen validatorın model üzerindeki Attribute ismi, Reflection ile çekilip ilgili static dictionary içinden alınır.
Not: Neden Reflection ile Type’dan ilgili Record Oluşturulmamıştır ? Çünkü Recordlar Generic tipde IValidator<T> tipinde bir”Validator”‘dan oluşmaktadır. Normalde Classlar Reflection ile “Type”‘dan generate edilebilirler ama her record’un oluşum tipinin aynı olmamasından dolayı bu yapılamamaktadır. Bu nedenle Record bazında farklılaşmaya gidilmiş ve ortak “Validate()” methodunun çağrılması, yeterli olmamıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
using ValidationFactory.Attributes; namespace ValidationFactory.Validators { public static class ValidatorFactory<T> { static Dictionary<string, IValidator<T>> validatorList = new(); public static IValidator<T> GetValidator(Type attribute) { if (validatorList.Count == 0) { validatorList.Add("DateData", new DateValidator<T>()); validatorList.Add("EmailData", new EmailValidator<T>()); validatorList.Add("EncryptData", new EncryptValidator<T>()); validatorList.Add("HashData", new HashValidator<T>()); validatorList.Add("StringData", new StringValidator<T>()); validatorList.Add("Default", new DefaultValidator<T>()); } return validatorList.ContainsKey(attribute.Name) ? validatorList[attribute.Name] : validatorList["Default"]; } } } |
Validators/ValidateClassProperties: Burada amaç, sınıfa ait tüm propertylerin gezilerek, varsa tanımlı attributeların yakalanıp ilgili validatorların property değerleri üzerinde çalıştırılması ve yakalanan hataların topluca liste halinde geri dönülmesidir.
1-) Geriye dönülecek toplu ErrorList ve aynı property’e birden fazla attribute tanımlanması durumunda, birinde hata çıkması durumunda diğerlerine devam edilmemesi amaçlı “isError” değişkeni aşağıdaki gibi tanımlanmıştır. “isError==true” durumunda “break;” ile ilgili döngüden çıkılacaktır.
2-) Aşağıda görüldüğü gibi sınıfı ait tüm PropertyInfolar, Reflection kullanılarak ilgili Record üzerinden çekilmiş, ve daha sonrasında herbiri için tek tek gezilmiştir.
3-) Aşağıda görüldüğü gibi herbir property üzerinde var olan attributelar bir döngü içinde gezilmiş, “type” değişkenine herbir attribute’ün Type’ı, reflection ile alınmış ve ilgili attribute’a ait validator, yukarıda tanımladığımız GetValidator() methodu ile, “validator” değişkenine atanmıştır.
- Örneğin User’a ait “Email” propertysine, aşağıda görüldüğü gibi 2 Attribute [EmailData ve EncryptData] tanımlanmıştır.
4-) Aşağıda görüldüğü gibi “propValue” değişkenine, ilgili property değeri atanmıştır. Bu örnekte Attributelere ait property alanı, ayrıca bir döngü içine sokulmamış ve en fazla 2 tane olması planlanıp “attributeValue” ve “attributeValue2” değişkenleri bu değerler için oluşturulmuştur. Aşağıda bakılan “if” koşulu ile, ilgili attribute’un bir propertysinin olup olup olmadığına bakılmıştır.
- Örneğin “UserName” property’sinin => “StringData” attribute’ünün => “max, min” adında 2 propertysi bulunmaktadır. İlgili Validation, bu değerlere göre yapılmaktadır.
5-) Aşağıda görüldüğü gibi, Attributelare ait property değerleri varsa çekilmiş ve “attributeValue, attributeValue2” değişkenlerine atanmıştır.
6-) Aşağıda görüldüğü gibi çekilen Validator’a göre, herbir property değeri kontrol edilmekte ve bir hata var ise birden fazla attribute olması durumunda devam edilmemekte ve “break” komutu ile bir sonraki property’e geçilmemektedir. Ayrıca bulunan hatalar da, “errorListe”‘e doldurulmaktadır. “Validate()” methodu => “IValidate” interface’inden türetilmektedir. Tüm validatorlar da, IValidator’dan türetildiği için, Strategy Design Pattern’ine uyularak, ortak Validate methodu her record’dan çağrılabilmektedir.
7-) “return errorList” : Son olarak, tüm propertyler ilgili validatorlar ile kontrol edildikten sonra, bulunan hatalar topluca geri dönülmüştür.
Validators/ValidateClassProperties.cs:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
using System.ComponentModel.DataAnnotations; using System.Reflection; using ValidationFactory.Attributes; namespace ValidationFactory.Validators { public static class ValidateClassProperties { public static List<(bool, Exception)> GetValidateResult(object model) { var errorList = new List<(bool, Exception)>(); bool isError = false; PropertyInfo[] properties = model.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo property in properties) { for (int i = 0; i < property.GetCustomAttributes(true).Length; i++) //It could be more than one Attribute. { Type type = property.GetCustomAttributes(true)[i].GetType(); var validator = ValidatorFactory<string>.GetValidator(type); string propValue = property.GetValue(model).ToString(); int? attributeValue = null; int? attributeValue2 = null; if (property.GetCustomAttributesData()[i].NamedArguments.Count > 0) //Has Parameter on "Attribute" { attributeValue = (int)property.GetCustomAttributesData()[i].NamedArguments[0].TypedValue.Value; if (property.GetCustomAttributesData()[i].NamedArguments.Count > 1) { attributeValue2 = (int)property.GetCustomAttributesData()[i].NamedArguments[1].TypedValue.Value; } } isError = false; foreach ((bool, Exception) err in validator.Validate(propValue, attributeValue, attributeValue2, property.Name, property, model)) { isError = true; errorList.Add(err); } if (isError) break; } } return errorList; } } } |
Decrypt İşlemleri:
Encrypt edilmiş, yani çift yönlü şifrelenmiş datanın okunması durumunda, Decrypt işleminin yapılması gerekmektedir. Minimum kod, maximum iş prensibi ile propertylerden kaçınmak için, Encoding işlemi aşağıdaki gibi güncellenmiştir.
Validators/EncryptValidator.cs: Aşağıda görüldüğü gibi Decrypt işlemi için ayrıca bir property kullanılmamış ve, property alanın’ın Encrypt olup olmadığına bakılmıştır.
İlgili property’nin Encrypted olduğunun anlaşılabilmesi için, başına custom => “encß” değeri konulmuştur.
- Yani “IsEncryted()” methodu ile, ilgili property value’su kontrol edilmiş, Encrypted yani şifreli ise, “DecryptText()” yok eğere değil ise, “EncryptText()” methodu çağrılmıştır.
- EncryptValidator recordu ve diğer tüm Validatorlar artık sadece IValidator<T> interfaceinden değil, “Validator” record’undan da türetilmiştir. Böylece tüm validator Recordlar, “IsEncryted()” ve ayrıca “IsHashed()” methodlarını da kullanabilmektedirler.
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 |
using ValidationFactory.Security; namespace ValidationFactory.Validators { public record EncryptValidator<T>() : Validator, IValidator<T> { public List<(bool, Exception)> Validate(T value, int? max, int? param2, string source, System.Reflection.PropertyInfo pi, object model) { var errorList = new List<(bool, Exception)>(); if (!typeof(T).IsValueType && typeof(T) != typeof(String)) { throw new ArgumentException("T must be a value type or System.String."); } string stringValue = value.ToString(); // Encrypted to the Value using (Encryption en = new()) { pi.SetValue(model, IsEncryted(stringValue) ? en.DecryptText(stringValue.Replace("encß","")) : "encß" + en.EncryptText(stringValue));//Encryption yapıldığını anlamak amaçlı başına eklenir. } if (errorList.Count == 0) Console.WriteLine("All tests succesful"); return errorList; } } } |
Validators/HashtValidator.cs: Aşağıda görüldüğü gibi, ilgili text alanın Hashed olup olmadığına, başına sonradan konan “hasß“, karakteri ile bakılmaktadır.
EKLENEN : “if (!IsEncryted(stringValue) && !IsHashed(stringValue))“=> Eğer text içerik, Encrypt ya da Hashed değil ise, bu durumda value Hashli hale getirilmektedir.
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 |
using ValidationFactory.Security; namespace ValidationFactory.Validators { public record HashValidator<T>() : Validator, IValidator<T> { public List<(bool, Exception)> Validate(T value, int? max, int? param2,string source, System.Reflection.PropertyInfo pi,object model) { var errorList = new List<(bool, Exception)>(); if (!typeof(T).IsValueType && typeof(T) != typeof(String)) { throw new ArgumentException("T must be a value type or System.String."); } string stringValue = value.ToString(); if (!IsEncryted(stringValue) && !IsHashed(stringValue)) { // Hash to the Value using (Encryption en = new()) { pi.SetValue(model, "hasß"+en.HashCreate(stringValue, en.GenerateSalt())); } if (errorList.Count == 0) Console.WriteLine("All tests succesful"); } return errorList; } } } |
Validators/Validator.cs: Aşağıda görüldüğü gibi istenir ise, gelen text value’nun 2 yönlü Encrypted, ya da tek yönlü Hased olup olmadığı cevabı “IsEncryted(), IsHashed()” methodları ile bulunmaktadır.
Not: Aşağıda, text’in hashli olup olmadığının anlaşılması için, HashValidator’da güncellenmiş ve geriye dönen tek yönlü şifreli text’in başına, “hasß” string değeri konulmuştur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
namespace ValidationFactory.Validators { public record Validator { public bool IsEncryted(string value) { return value.IndexOf("encß") == 0; } public bool IsHashed(string value) { return value.IndexOf("hasß") == 0; } } } |
Tüm validatorlara, aşağıda görülen kontrol eklenmiştir. Amaç, şifreli Encrypted ya da Hashed içeriklerin, kontrol edilmemesidir. Örnek kod parçası DateValidator için, aşağıdaki gibi gösterilmektedir. Diğer tüm validatorlarda da yapılan değişiklik, aşağıda eklenen kontrolden başka birşey değildir.
Validates/DateValidator:
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 |
namespace ValidationFactory.Validators { public record DateValidator<T>() : Validator, IValidator<T> { public List<(bool, Exception)> Validate(T value, int? minYear,int? param2, string source, System.Reflection.PropertyInfo pi,object model) { var errorList = new List<(bool, Exception)>(); if (!DateTime.TryParse(value.ToString(), out DateTime temp)) { throw new ArgumentException("T must be proper System.DateTime."); } string stringValue = value.ToString(); if (!IsEncryted(stringValue)) { // Check minYear if ((DateTime.Parse(stringValue)).Year < minYear) { Console.WriteLine("Time is too old. Wrong Date!"); errorList.Add((false, new Exception($"Time is too old. Wrong Date! Year Must Bigger Than > {minYear}") { Source = source })); } if (errorList.Count == 0) Console.WriteLine("All tests succesful"); } return errorList; } } } |
Controllers/HomeController/Index[GET]: Aşağıda örnek amaçlı, bir servisden dönmesi gereken User kaydı, dummy olarak elle girilmiştir. DBtarafında encrypted olarak tutulan Email, Gsm ve hashed olarak tutulan Password alanı, “GetValidateResult()” methodundan geçirilerek, Encrypte alanların Decrypt olması sağlanmıştır. Ayrıca Encrypted olmayan alanların, attribute atanmış olan propertylerinin kontrolü sağlanmıştır.
Not: Dikkat edilir ise Encrypt alanlar, “encß” karakteri ile hashed alanlarda “hasß” karakteri ile başlamaktadır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[HttpGet] public IActionResult Index() { User user = new User() { Name = "Bora", LastName = "Kasmer", UserName = "bA", Password = "hasßm+KKpEl/qgGDrqHZOi6RCQ9Wlj48vYzDnDdm6ctFGO4=æ1rN9gprpjCRDlkINl+ybHw==", Email = "encßnkLseH+tsLd1JH6dMzAB9giK5uX/YjqrczLWwVPrT/M=", Email2 = "bora.kasmer78@gmail.com", BirthDate = new DateTime(1881, 6, 3), Gsm = "encßGWXGzj2SGYu70PzzMYVaFfWwl0wzS6Lc", }; List<(bool, Exception)> errorList = new(); errorList = ValidateClassProperties.GetValidateResult(user); ResultModel model = new() { Exception = errorList.Select(li => li.Item2).ToList(), User = user }; return View(model); } |
Models/ResultModel: Client’a dönülen ViewModel, aşağıdaki gibidir. Geriye, üzerinde işlem yapılan User model ve validate işleminden sonra alınan tüm error Listesi dönülmektedir.
1 2 3 4 5 6 7 8 |
namespace ValidationFactory.Models { public class ResultModel { public List<Exception> Exception { get; set; } public User User { get; set; } } } |
Aşağıda görüldüğü gibi UserName ve BirthDate alanlarında bulunan hatalar listelenirken, encrypted Email ve Gsm Decrypt edilerek ekrana basılmıştır. Password, zaten geri dönülemez Hashli olduğu için, üzerinde işlem yapılmadan olduğu gibi ekrana basılmıştır. Email hatalı olmasına rağmen, başta Encrypted bir alan olmasından dolayı, ayrıca validation’a sokulmamıştır.
Not: Get işlemi sırasında istenir ise Validation yapılmayıp, sadece Encrypt ya da Hashleme de yapılabilir. Bir sonraki adımda, bu konu da detaylıca anlatılacaktır.
İsteğe bağlı olarak Get İşleminde Validation Yapılmaması:
Hatırlarsanız tüm Validatorları => Validator record’undan türettik. Buraya, “IsGetMethod()” şeklinde bir method eklenip, Request tipine bakılabilir. Ve tüm “IsEncryted()” şeklinde kontrollerin yapıldığı yere “IsGetMethod()” kontrolü de eklenip, Request tipi == “GET” ise Validate işleminin yapılmaması sağlanabilir. Hadi gelin isterseniz, kodlara bakalım..
Öncelikle .Net Core 6.0 projesinde program.cs altına, aşağıdaki kod eklenir. Amaç, Validator sınıfı içinden, HttpContex’e erişimin izin verilmesidir.
1-) program.cs:
1 |
builder.Services.AddHttpContextAccessor(); |
2-) Validator.cs: Aşağıda görüldüğü gibi, _httpContext değişekenine, Current HttpContext setlenmiştir. Daha sonra “IsGetMethod()” eklenerek, gelen request methodunun, “GET”‘mi yoksa “POST”‘mu olduğu kontrol edilmiştir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
namespace ValidationFactory.Validators { public record Validator { public HttpContext _httpContext => new HttpContextAccessor().HttpContext; public bool IsEncryted(string value) { return value.IndexOf("encß") == 0; } public bool IsHashed(string value) { return value.IndexOf("hasß") == 0; } public bool IsGetMethod() { return _httpContext.Request.Method == "GET"; } } } |
3-) StringValidator, DateValidator, EmailValidator: “GET” işleminde istenir ise, validation yapan tüm validatorlara, “!IsGetMethod()” kontrolü eklenerek, hata yakalama işlemi iptal edilebilir.
1 |
if (!IsGetMethod() && !IsEncryted(stringValue)) |
Views/Home/Index.cshtml: Aşağıdaki View sayfası, model olarak ResultModel beklemektedir. Gönderilen ErrorList bir döngü şeklinde yukarıda görüldüğü gibi ekrana basılırken, geriye dönülen User model, hemen sayfanın aşağısında decrypt işlemleri yapıldıktan sonra basılmaktadır. Form’un Post olması durumunda => “Home/Index Post” sayfasına yönlendirme işlemi yapılmıştır.
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
@model ResultModel @{ ViewData["Title"] = "Home Page"; } <body> @using (Html.BeginForm("Index", "Home", FormMethod.Post)) { <div class="text-center"> <h1 class="display-4">Welcome</h1> <p>Learn about <a href="https://borakasmer.com">building Web apps with ASP.NET Core</a>.</p> @if (Model.Exception.Count > 0) { <h1>User Error List</h1> <table class="table table-sm table-dark"> <tr> <th scope="col"> Source </th> <th scope="col"> Execption Message </th> </tr> @{ int rowCount = 0; } @foreach (var err in Model.Exception) { rowCount += 1; <tr @((rowCount%2)==0 ? "class=table-primary" : "class=table-secondary")> <td> @Html.DisplayFor(modelItem => err.Source) </td> <td> @Html.DisplayFor(modelItem => err.Message) </td> </tr> } </table> } else { <font color="green"><h1>User Has No Error</h1></font> } <input type="submit" class="btn btn-primary" value="Post Page" /> <hr /> <h2>User Detail</h2> <dl class="row" style="text-align: left;"> <dt class="col-sm-3">User Name:</dt> <dd class="col-sm-9">@Model.User.Name</dd> <dt class="col-sm-3">User Surname:</dt> <dd class="col-sm-9">@Model.User.LastName</dd> <dt class="col-sm-3">User Username:</dt> <dd class="col-sm-9">@Model.User.UserName</dd> <dt class="col-sm-3">User BirthDate:</dt> <dd class="col-sm-9">@Model.User.BirthDate</dd> <dt class="col-sm-3">User Email:</dt> <dd class="col-sm-9">@Model.User.Email</dd> <dt class="col-sm-3">User Gmail:</dt> <dd class="col-sm-9">@Model.User.Email2</dd> <dt class="col-sm-3">User Gsm:</dt> <dd class="col-sm-9">@Model.User.Gsm</dd> <dt class="col-sm-3">User Password:</dt> <dd class="col-sm-9"> <p>@Model.User.Password</p> </dd> </dl> </div> } </body> |
Controllers/HomeController/Index[POST]: DB’ye kaydedilmek üzere, Client tarafından girilmesi gereken User data, örnek amaçlı dummy şekilde manuel olarak aşağıdaki gibi doldurulmuştur. Ve model üzerindeki tüm validationlar, güvenlik için yapılan Encryption ve Hash işlemler, aşağıdaki tek bir komut satırı ile yapılmaktadır.
“Çalıştırılan tek satır => errorList = ValidateClassProperties.GetValidateResult(user)“
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[HttpPost] //public IActionResult Index([FromBody] User model) public IActionResult Index(User model) { User user = new User() { Name = "Bora", LastName = "Kasmer", UserName = "bk@smer", Password = "vbt123456", Email = "bora.kasmer78", Email2 = "bora.kasmer78@gmail.com", BirthDate = new DateTime(1881, 6, 3), Gsm = "5426781944", }; List<(bool, Exception)> errorList = new(); errorList = ValidateClassProperties.GetValidateResult(user); ResultModel resultModel = new() { Exception = errorList.Select(li => li.Item2).ToList(), User = user }; return View(resultModel); } |
- UserName => “[StringData(max = 10,min =3)]” StringValidator’ı kullanılmış ve aşağıda görüldüğü gibi 2 tane hata bulunmuştur. En az 1 harf büyük olmalı ve içinde “@” işareti olmamalıdır.
- Birthdate => “[DateData(minYear =1900)]” DateValidator kullanılmıştır. Aşağıda görüldüğü gibi, doğum yılı 1900’den küçük olmaz şeklinde tek hata bulunmuştur.
- Email =>”[EmailData(type = EmailValidateType.Syntax)], [EncryptData]” Email ve Encrypt Validatorları kullanılmıştır. Önce üstteki sonra alttaki validator, sıra ile çalıştırılmıştır. Öncelikle geçerli bir mail mi diye bakılmış ve olmadığı görülmüştür. Bu nedenle 2. Validator’a geçilmemiş, ve geçerli olmayan email, Encrypt edilmemiştir.
- Not: “Attributelar her zaman, yukarıdan aşağı sıra ile çalıştırılırlar.”
- Gsm([EncryptData]) alanı güvenlik amaçlı Encrypt edilmiş, Password([HashData]) alanı da Hashlenmiştir.
Geldik bir makalenin daha sonuna. Bu iki makalede, girilen datanın herbir propertysi, tek tek gezilerek, eğer herhangi bir attribute ile işaretlenmiş ise, kendisine özgü bir validator’ın kontrolünden geçirilip hatalı durumlar bir liste altında toplanmıştır. Daha sonra raporlanırken, güvenlik amacı ile şifrelenmesi gereken yerler, Encrypted ya da Hashed hale getirilmiştir. İstendiği takdirde, sadece “POST” methodunda validation yapılırken, “GET” methodunda, sadece Encrypted alanların Decryption’ı sağlanabilmektedir.
İlgili sınıfın => propertylerinin, propertylerin => attributelerinin ve en son attributelerin de=> parametrelerinin okunması işlemi, reflection ile yapılmaktadır. Başta kod okunaklığının bozulduğu düşünülse de, kod geliştirme esnasında ilgili kontrollerin tek bir satır ile yapılabilmesi, kesinlikle göz ardı edilmemesi gereken bir yöntemdir. İlgili attribute’a göre validator üretme işi, Singleton olarak static bir Dictionary içinden alınarak yapılsa da, Generic tipde olmayan bir Validator Record ile çalışılsa idi, ilgili recordun Type’ından reflection ile yeni bir validator oluşturulabilirdi. Bu da, yeni bir validator geldiği zaman, ilgili static listenin içine konmasına gerek kalmadan kod geliştimeyi sağlardı. Daha sonrasında, tüm validatorların aynı Interfaceden türetilmesi ve tipe bakılmaksızın “GetValidator()” methodunun çağrılması, Strategy Design Pattern kullanılarak yapılmaktadır.
Biri işi yapmanın birçok yöntemi olsa da, her zaman daha iyisi, daha okunaklısı ve daha temizini arama yolundan hiç vazgeçmeyin. Yeni bir makalede görüşmek üzere hepinize hoşçakalın.
Source Code: https://github.com/borakasmer/ValidationFactory
Bora abi umarım örneklerde kendi numaranı paylaşmamışsındır paylaştıysan da bu yorum ile umarım kaldırırsın.
Hahaha,
Yok yok. Yine de çok teşekkürler. Bir arayıp test etse idin :)
Elinize saglık
Teşekkürler Celal..