Interfejs IValidatable
Zacznijmy od samego początku ponieważ walidacja musi być łatwo dostępna i wielokrotnego użytku każdy obiekt który będzie posiadał walidacje musi implementować powyższy interfejs. Nic specjalnego:
public interface IValidatable { ValidationResult Validate(); }
ValidationResult
Klasa ta składa się ze słownika który przechowuje informacje o błędzie jak i pole którego dotyczy informacja. Natomiast z powodu ze dane pole może posiadać wiele błędów dlatego przechowuje listę stringów. Oprócz tego posiada 2 funkcje 1 do zliczania błędów a druga do dodawania błędów do słownika. Sama klasa wygląda tak:
public class ValidationResult { private IDictionary<string, IList<string>> _validationErrors; public ValidationResult() { _validationErrors = new Dictionary<string, IList<string>>(); } public IDictionary<string, IList<string>> Errors { get { return _validationErrors; } private set { _validationErrors = value; } } public int CountErrors() { int errorCount = 0; foreach (var errorMessages in _validationErrors.Values) { errorCount += errorMessages.Count; } return errorCount; } public void AddError(KeyValuePair<string, string> error) { if (error.Key != null) { if (_validationErrors.ContainsKey(error.Key)) { _validationErrors[error.Key].Add(error.Value); } else { IList<string> newErrorList = new List<string>(); newErrorList.Add(error.Value); _validationErrors.Add(new KeyValuePair<string, IList<string>>(error.Key, newErrorList)); } } } }
ValidationHelper
Żeby troszeczkę sobie ułatwić dodawanie błędów postanowiłem stworzyć klasę pomocnicza która będzie nieco automatyzować dodawanie błędów. Będzie to obiekt który posiada 2 metody które dodają błąd jeżeli warunek jest niespełniony. Różnią się tylko 1 parametrem związanym z kluczem błędu. Zwykle nazywa się tak samo jak dane pole którego dotyczy. I w tym celu służy wersja metody z wyrażeniem lambda. Natomiast druga przyjmuje parametr string. Czyli jest bardziej uogólniona. Klasa wygląda tak:
public class ValidationHelper<T> { public ValidationHelper() { } public KeyValuePair<string, string> CreateErrorIf(bool condition, string errorKey, string errorMessageKey) { if (!condition) return new KeyValuePair<string, string>(errorKey, ResourceHelper.GetErrorMessage(errorMessageKey)); else return new KeyValuePair<string, string>(); } public KeyValuePair<string, string> CreateErrorIf<TResult>(bool condition, Expression<Func<T, TResult>> errorKey, string errorMessageKey) { var expressionMember = errorKey.Body as MemberExpression; return CreateErrorIf(condition, expressionMember.Member.Name, errorMessageKey); } }Metoda która używa wyrażenia lambda po prostu pobiera z niego nazwę danego pola. A teraz przykładowe wykorzystanie powyższych klas:
public class User : BaseEntity, IEntity, IValidatable { ... public virtual ValidationResult Validate() { ValidationResult validationResult = new ValidationResult(); ValidationHelper<User> validationHelper = new ValidationHelper<User>(); validationResult.AddError(validationHelper.CreateErrorIf(!String.IsNullOrEmpty(Username), f => f.Username, ResourceKey.Common.RequiredField)); validationResult.AddError(validationHelper.CreateErrorIf(Username.Length >= 256, f => f.Username, ResourceKey.Common.FieldLength)); ... return validationResult; } }
Oprócz pisania tego mechanizmu powolutku uzupełniam brakujące testy w projekcie związane już z gotową funkcjonalnością. To tyle na dziś :)
1 komentarz:
Czekam na więcej:) Zawsze można tu czegoś ciekawego się dowiedzieć. W biznesie także super się sprawdzi system od http://zimbra.pl, jest on świetny do sprawnej komunikacji w firmie.e
Prześlij komentarz