Need a good architecture for validation rules

Hope you guys can give me some good suggestions. I think how to create a good architecture for developing C #. I try to explain the scripts because they don't like English:

1) Two classes: Blue Bank and Red Bank

2) Third class: Validation rules

3) Blue and red banks have several fields (values), such as AccountNumber, Amount, InvoicePeriod, etc. Example here (xml):

Blue bank

<AccountNumber>Maria 5987593457</AccountNumber> <Amount>200.00</Amount> <InvoicePeriod>1</InvoicePeriod> 

Red bank

  <AccountNumber>8529458</AccountNumber> <Amount>300.00</Amount> <InvoicePeriod>0</InvoicePeriod> 

Red / Blue banks have the same validation rules, such as sums of fields that must be numerical. But Red / Blue Banks has different validation rules - the AccountNumber field must be alphanumeric in Blue Bank, while the AccountNumber must be numeric in Red Bank otherwise. The InvoicePeriod field should be the default 1 in Red Bank, while it should be the default 0 in Blue Bank otherwise.

My idea:

I want to create each Red / Blue Bank class for different verification rules, and then I also create verification class rules for the same rules as the Blue / Red banks.

My code is here:

Blue Bank Class:

  • Confirm account number, which must be alphanumeric otherwise
  • Check InvoicePeriod, which should be the default of 1 otherwise failed

Red Bank Class:

  • Confirm the account number, which otherwise must be numbered.
  • Check InvoicePeriod, which should be the default 0 otherwise failed

RulesOfValidation class

  • Confirm the amount, which should be numbered (the same rules for the Red / Blue Bank classes)

How does this work with the dictator <,> with these classes? Or any better suggestion with code examples?

Your help would be greatly appreciated.

+4
source share
3 answers

You should use System.ComponentModel.DataAnnotations

First create an abstract class bank

 abstract class Bank { #region fields private List<string> errorMessages = new List<string>(); #endregion #region publioc methods public virtual void Validate() { ValidateRulesAtributes(); } #endregion #region helper methods private void ValidateRulesAtributes() { var validationContext = new ValidationContext(this, null, null); //ValidationContext -> Reference System.ComponentModel.DataAnnotations var result = new List<ValidationResult>(); Validator.TryValidateObject(this, validationContext, result, true); result.ForEach(p => { errorMessages.Add(p.ErrorMessage); }); if (errorMessages.Any()) throw new Exception(errorMessages.Aggregate((m1, m2) => String.Concat(m1, Environment.NewLine, m2))); } protected void Validate(List<string> messages) { if (errorMessages == null) errorMessages = new List<string>(); if (messages != null) messages.ForEach(p => { errorMessages.Add(p); }); ValidateRulesAtributes(); } #endregion #region properties //Abstract to indicate Validation atributes public abstract string AccountNumber { get; set; } public abstract double Amount { get; set; } public abstract int InvoicePeriod { get; set; } #endregion } 

Second Create a Red and Blue Bank with Data Anonymous

 class BlueBank : Bank { //All is ok, no validate public override string AccountNumber { get; set; } public override double Amount { get; set; } public override int InvoicePeriod { get; set; } } class RedBank : Bank { [Required()] public override string AccountNumber { get; set; } public override double Amount { get; set; } [Range(0,0)] public override int InvoicePeriod { get; set; } public override void Validate() { List<string> errors=new List<string>(); if (AccountNumber != "Test") errors.Add("Worng Account"); base.Validate(errors); } } 

Use attributes to check ranges required etc. Override validation () for complex validations

This is a simple example.

 class Program { static void Main(string[] args) { RedBank red = new RedBank(); red.AccountNumber = "Test"; red.Amount=0; red.Validate(); //this No fail red.InvoicePeriod = 3; //This Fail; red.Validate(); red.AccountNumber = "PD"; red.Validate(); //this fal: } } 
0
source

A template specification is a good option for your problem. You can create a specification class for general banking rules and other specification classes for each specific need.

There are several C # libraries for this template:

+5
source

I would go with the IRule interface using the Validate() method, which can be implemented in specific validation classes that can contain validation logic. Then you can stop a few user rules in the bank. Pass a list of objects of type IRule to the bank class and run Validate() for each parameter of the bank to be transferred. Thus, each bank can verify itself on the basis of accepted rules.

 interface IRule { bool Validate(Bank someBank); } abstract class Bank { public string AccountNumber; public string Amount; public string InvoicePeriod; private List<IRule> listOfRules = new List<IRule>(); public void ValidateAllRules(){ foreach (var ite in listOfRules){ ite.Validate(this); //if validation will not pass then I don't know what you want to do ;) } } public void AddRule(IRule rule) { listOfRules.Add(rule); } } class RedBank : Bank { public RedBank(){ listOfRules.Add(new SimpleRule()); listOfRules.Add(new SimpleRule2()); } } class SimpleRule : IRule { public bool Validate(Bank someBank) { return someBank.AccountNumber.Contains("567"); } } class SimpleRule2 : IRule { public bool Validate(Bank someBank) { return someBank.Amount.Contains(".") && someBank.InvoicePeriod.Contains("-"); } } 
+1
source

Source: https://habr.com/ru/post/1481957/


All Articles