Posts

Entity validation engine using c# expression rules

Validating entity data is the common task when building business applications. Very often happens that we want to separate the rules from the app code to be able to quickly change it without affecting the application. In this article we will build simple validation engine that will be evaluating rules stored as linq expressions.

In our example we will hardcode the rules in separate .cs files (based on entity type). You may also want to store the linq expressions as a string in database and also compile the code dynamically from the string and then execute the rules based on the output dll file.

validation-engine

In our case we will hard code the rule in .css files. The following example shows the rule definitions for Client entity.

  public class ClientValidationRules : BaseValidationRuleSet<Client>
   {
        public ClientValidationRules(Client arg)
        {
            RuleList.Add(new ValidationRule<Client>(
                //set the rule
                a => a.Name.Trim().Any() && !a.Name.Any(b => char.IsLower(b))
                ////
                , arg)
                {
                    //set the result
                    Message = "Name cannot be all in uppercase!",
                    ResultTypeIfFailed = ValidationResultType.ERROR,
                    SuggestionString = arg.Name.ToLower()
                });

            RuleList.Add(new ValidationRule<Client>(
                //set the rule
               a => a.DateOfBirth.AddYears(18) > DateTime.Now
                ////
               , arg)
            {
                //set the result
                Message = "You must be at lest 18 years old to register!",
                ResultTypeIfFailed = ValidationResultType.ERROR,
                SuggestionString = string.Format("Wait {0} days", DateTime.Now.Subtract(arg.DateOfBirth).TotalDays.ToString("0"))
            });
        }
    }
}

You can see that this way allows us to easily define rule’s logic based on the entity data. We can also define suggestion message for each rule separately. By setting ValidationResultType as a rule result we can for example allow to save data if this is only a warning or prevent saving the data by the user if this is an error.

The implementation is as follows. Let’s start with generic ValidationRule class containing the rule data and delegate to run.

 namespace RuleEngine
 {
    public class ValidationRule<T> where T : class
    {
        public string Message { get; set; }
        public string SuggestionString { get; set; }
        
        public ValidationResultType ResultTypeIfFailed { get; set; }

        internal Func<T, bool> RuleDelegate { get; set; }
        internal T ObjectTovalidate { get; set; }

        public ValidationRule(Func<T, bool> rule, T arg)
        {
            RuleDelegate = rule;
            ObjectTovalidate = arg;
        }

        public bool RunRuleDelegate()
        {
            return RuleDelegate(ObjectTovalidate);
        }
    }
 }

It’s base class will have the “Run” method implementation.

 namespace RuleEngine
 {
    public abstract class BaseValidationRuleSet<T> where T : class
    {
        public List<ValidationRule<T>> RuleList { get; internal set; }

        /// <summary>
        /// Initiates rule list
        /// </summary>
        public BaseValidationRuleSet()
        {
            RuleList = new List<ValidationRule<T>>();
        }

        /// <summary>
        /// Run all added rules
        /// </summary>
        /// <returns></returns>
        public ValidationResult Run()
        {
            foreach (var rule in this.RuleList)
            {
                var result = rule.RunRuleDelegate();

                if (result)
                {
                    return new ValidationResult() { IsValid = false, ResultType = rule.ResultTypeIfFailed, Message = rule.Message, SuggestionString = rule.SuggestionString };
                }
            }

            return new ValidationResult() { IsValid = true, ResultType = ValidationResultType.OK };
        }
    }
}

Let’s define ValidationResult class that will be returned after running the rule delegate.

 namespace RuleEngine
 {
    public class ValidationResult
    {
        public bool IsValid { get; set; }
        public string SuggestionString { get; set; }
        public string Message { get; set; }
        public ValidationResultType ResultType { get; set; }
    }
}

The ValidationEngine class will have the entry point for different entity validation methods. You may also want to create generic function to pass the validating object to. You also can preload all rules at runtime and evaluate dynamically using AppDomain.CurrentDomain.GetAssemblies() method.

  public class ValidationEngine: ValidationEngineBase
  {
      public ValidationResult RunProjectRules(Project project)
      {
          return new ProjectValidationRules(project).Run();
      }
      public ValidationResult RunClientRules(Client client)
      {
          return new ClientValidationRules(client).Run();
      }
   }

In order to test our solution we can use console app. We also need to load some fake data into our entities.

     static void Main(string[] args)
        {
            var validationEngine = new ValidationEngine();

            #region load entities
            var client = new Client()
                  {
                      Name = "CLIENT NUMBER1a",
                      DateOfBirth = DateTime.Now.AddYears(-10)
                  };

            var project = new Project()
            {
                Name = "PROJECT NUMBER1",
                StartDate = DateTime.Now.AddDays(1),
                EndDate = DateTime.Now.AddDays(1)
            }; 
            #endregion


            #region validate client
            Console.WriteLine("Starting client validation");

            var result = validationEngine.RunClientRules(client);

            if (!result.IsValid)
            {
                Console.WriteLine(result.Message);
                Console.WriteLine(string.Format("Suggestion: {0}", result.SuggestionString));
            }
            else
            {
                Console.WriteLine("OK");
            }
            #endregion

            Console.WriteLine("--------------------");

            #region validate project
            Console.WriteLine("Starting project validation");

            result = validationEngine.RunProjectRules(project);

            if (!result.IsValid)
            {
                Console.WriteLine(result.Message);
                Console.WriteLine(string.Format("Suggestion: {0}", result.SuggestionString));
            }
            else
            {
                Console.WriteLine("OK");
            }
            #endregion

            Console.ReadLine();
        }

I have included project files bellow for your tests.


#Article update#

We can also create simple business rule engine in very similar way, here is the implementation:

 namespace RuleEngine
 {
    /// <summary>
    /// To be extended if needed
    /// </summary>
    public abstract class BusinessRuleEngineBase
    {
        
    }

    /// <summary>
    /// Runs set of rules for different entities
    /// </summary>
    public class BusinessRuleEngine : BusinessRuleEngineBase
    {
        /// <summary>
        /// Run rules for ScheduledFlight 
        /// </summary>
        /// <param name="scheduledFlight"></param>
        /// <returns></returns>
        public bool RunBusinessRulesFor(ScheduledFlight scheduledFlight)
        {
            return new FlightBusinessRules(scheduledFlight).Run();
        }
    }
 }

Business rule implementation containing evaluating and executing delegate

/// <summary>
/// Runs delegate on the specified business rule set
/// </summary>
/// <typeparam name="T"></typeparam>
public class BusinessRule<T> where T : class
{
    public string RuleName { get; set; }
    internal Func<T, bool> RuleDelegate { get; set; }
    internal Func<T, bool> ExecuteRuleDelegate { get; set; }
    internal T ObjectToValidate { get; set; }
    internal T ObjectToApply { get; set; }

    public BusinessRule(Func<T, bool> rule, Func<T, bool> ruleApply, T arg)
    {
        RuleDelegate = rule;
        ExecuteRuleDelegate = ruleApply;
        ObjectToValidate = arg;
        ObjectToApply = arg;
    }

    /// <summary>
    /// Determines if business rule should be applied
    /// </summary>
    /// <returns></returns>
    public bool ApplyRuleDelegate()
    {
        return RuleDelegate(ObjectToValidate);
    }

    /// <summary>
    /// Applies business rule on the current entity
    /// </summary>
    /// <returns></returns>
    public bool ApplyBusinessDelegate()
    {
        return ExecuteRuleDelegate(ObjectToApply);
    }
    
}

BaseBusinessRuleSet base class

/// <summary>
/// Base class for business rule sets
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class BaseBusinessRuleSet<T> where T : class
{
    public List<BusinessRule<T>> RuleList { get; internal set; }

    /// <summary>
    /// Initiates business rule list
    /// </summary>
    public BaseBusinessRuleSet()
    {
        RuleList = new List<BusinessRule<T>>();
    }

    /// <summary>
    /// Run all added business rules
    /// </summary>
    /// <returns></returns>
    public bool Run()
    {
        foreach (var rule in this.RuleList)
        {
            var result = rule.ApplyRuleDelegate();

            //if rule is true, run business logic
            if (result)
            {
                rule.ApplyBusinessDelegate();

                return true;
            }
        }

        return false;
    }
}

And finally the business rule set-up class.

public class FlightBusinessRules : BaseBusinessRuleSet<ScheduledFlight>
{
    /// <summary>
    /// Setup business rules for ScheduledFlight
    /// </summary>
    /// <param name="arg"></param>
    public FlightBusinessRules(ScheduledFlight arg)
    {

        RuleList.Add(new BusinessRule<ScheduledFlight>(
            //set the rule
                a => (double)arg.Passengers.Count(p => p.Type == PassengerType.AirlineEmployee) / arg.Passengers.Count > arg.FlightRoute.MinimumTakeOffPercentage
            ,
            //execute rule
                 e =>
                 {
                     //lower the base price till 100
                     if (arg.FlightRoute.BasePrice >= 100)
                     {
                         arg.FlightRoute.BasePrice -= 10;
                     }

                     //add more logic here...

                     return true;
                 },
                arg) { RuleName = "ApplyDiscountFor_AirlineEmployee" });


        RuleList.Add(new BusinessRule<ScheduledFlight>(
            //set the rule
              a => a.FlightRoute.BasePrice == 0
              ,
            //execute rule
               e =>
               {
                   //enforce minimal base price
                   arg.FlightRoute.BasePrice = 100;

                   return true;
               },
              arg) { RuleName = "EnsureMinimalBasePrice" });

    }
}

RuleEngine

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...Loading...

MVC property and model level validation techniques

MVC framework gives us a lot of different techniques for validating model and properties. The simplest way of model validation is to apply property attributes. After applying property attributes the client side validation will be performed automatically using unobtrusive JavaScript. The error message are being displayed in the UI if we provide @Html.ValidationSummary(true) or property level @Html.ValidationMessageFor(m => m…….). The trick is to apply range validation to checkbox as it don’t have a value when is not checked.

   public class Client
    {
        [Required]
        public string ClientName { get; set; }

        [DataType(DataType.Date)]
        [Required(ErrorMessage = "Please enter a date of birth")]
        public DateTime DateOfBirth { get; set; }

        [Range(typeof(bool), "true", "true", ErrorMessage = "You must accept the terms")]
        public bool AreTermsAccepted { get; set; }
    }

One of the easiest validation methods is to validate model explicitly in the action method. We are simply adding the errors to ModelState object to be displayed in the UI when needed.
We can also check if binder was able to assign value by using ModelState.IsValidField. This is perfect for dateTime types. Checking business rules is also very easy with this technique.

[HttpPost]
public ViewResult CreateClient(Client client)
{
    if (string.IsNullOrEmpty(client.ClientName))
    {
        ModelState.AddModelError("ClientName", "Please enter client name");
    }
    
    if (!client.IsTermsAccepted)
    {
        ModelState.AddModelError("IsTermsAccepted", "You must accept the terms to create account");
    }

	if (ModelState.IsValidField("ClientName") && ModelState.IsValidField("IsTermsAccepted") && client.ClientName == "Mike") 
	{
       ModelState.AddModelError("", "Mike is already registered");
    }

    if (ModelState.IsValid)
    {
        repository.SaveClient(client);

        return View("Registered", client);
    }
    else
    {
        return View();
    }
}

Another way of model validation is overriding DefaultModelBinder validation methods. The logic is basically similar to what is described above but it’s gives us the way for separating the validation logic and turning it on/ off when necessary by registering in global app start function.

By overriding the SetProperty function we find each property by name. Next, we check the values before it will be bound to the model. In OnModelUpdated function we perform model validation as this is being called after all model properties has been bound.

 public class ValidatingModelBinder : DefaultModelBinder
 {
    protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
    {
        // call the base implementation
        base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);

        // do property level validation
        switch (propertyDescriptor.Name)
        {
            case "ClientName":
                if (string.IsNullOrEmpty((string)value))
                {
                    bindingContext.ModelState.AddModelError("ClientName", "Please enter your name");
                }
                break;
            case "AreTermsAccepted":
                if (!((bool)value))
                {
                    bindingContext.ModelState.AddModelError("AreTermsAccepted", "You must accept the terms");
                }
                break;
        }
    }

    protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // call the base implementation
        base.OnModelUpdated(controllerContext, bindingContext);

        // get the model
        var model = bindingContext.Model as Client;

        // perform model level validation
        if (model != null && bindingContext.ModelState.IsValidField("ClientName") && model.ClientName == "Mike")
        {
            bindingContext.ModelState.AddModelError("", "Mike is already registered");
        }
    }
 }

We also need to register our validator in the application global file:

ModelBinders.Binders.Add(typeof(Client), new ValidatingModelBinder());

Next form of validation is applying custom attributes where you can apply your specific validation rules.

 public class IsTrueAttribute : ValidationAttribute
 {
    public override bool IsValid(object value)
    {
        return value is bool && (bool)value;
    }
 }

[IsTrueAttribute(ErrorMessage="You must accept the terms")]
public bool AreTermsAccepted { get; set; }

We can also derive from existing attributes and add additional logic we need.

 public class FutureDateAttribute : RequiredAttribute
 {
    public override bool IsValid(object value)
    {
        return base.IsValid(value) &&
        value is DateTime &&
        ((DateTime)value) > DateTime.Now;
    }
}

We also can apply custom model validation attribute by inheriting from ValidationAttribute. Please not that model validation wont be performed if there are some property level validation errors.

 public class ClientValidatorAttribute : ValidationAttribute
 {
    public ClientValidatorAttribute()
    {
        ErrorMessage = "Mike is already registered";
    }

    public override bool IsValid(object value)
    {
        var client = value as Client;

        if (client == null || string.IsNullOrEmpty(client.ClientName))
        {
            //we don't have right model to validate
            return true;
        }
        else
        {

            return !(client.ClientName == "Mike");
        }
    }
 }

  //in our model class
  [ClientValidatorAttribute]
  public class Client {

Another way of validation is to create self-validating model by inheriting from IValidatableObject interface and overloading Validate function. The validation will be called after all property values has been assigned. This approach has benefit of having all model validation rules in the class definition.

 public class Client : IValidatableObject
 {
    public string ClientName { get; set; }
    [DataType(DataType.Date)]
    public DateTime DateOfBirth { get; set; }
    public bool AreTermsAccepted { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        List<ValidationResult> errors = new List<ValidationResult>();

        if (string.IsNullOrEmpty(ClientName))
        {
            errors.Add(new ValidationResult("Please enter client name"));
        }

        if (DateTime.Now > DateOfBirth)
        {
            errors.Add(new ValidationResult("Please enter a date in the future"));
        }

        if (errors.Count == 0 && ClientName == "Mike")
        {
            errors.Add(new ValidationResult("Mike is already registered"));
        }

        if (!AreTermsAccepted)
        {
            errors.Add(new ValidationResult("You must accept the terms"));
        }

        return errors;
    }
 }

At the end I will show you how to create custom validation provider. In this approach we inherit from ModelValidatorProvider to use GetValidators method. This will allow us to apply custom validators on the very high level.
We basically check ContainerType to apply property level validation of the specified type and ModelType to apply custom model validator.

 public class CustomValidationProvider : ModelValidatorProvider
 {
    public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
    {
        if (metadata.ContainerType == typeof(Client))
        {
            return new ModelValidator[] { new ClientPropertyValidator(metadata, context)};
        }
        else if (metadata.ModelType == typeof(Client))
        {
            return new ModelValidator[] { new ClientValidator(metadata, context)};
        }

        return Enumerable.Empty<ModelValidator>();
    }
}

Property validation looks like shown below.

 public class ClientPropertyValidator : ModelValidator
 {
    public ClientPropertyValidator(ModelMetadata metadata, ControllerContext context)
        : base(metadata, context)
    {
    }

    public override IEnumerable<ModelValidationResult> Validate(object container)
    {
        var client = container as Client;
        if (client != null)
        {
            switch (Metadata.PropertyName)
            {
                case "ClientName":
                    if (string.IsNullOrEmpty(client.ClientName))
                    {

                        return new ModelValidationResult[] {
                                    new ModelValidationResult {
                                    MemberName = "ClientName",
                                    Message = "Please enter client name"
                                    }};
                    }
                    break;
                   
                case "AreTermsAccepted":
                    if (!client.AreTermsAccepted)
                    {
                        return new ModelValidationResult[] {
                                new ModelValidationResult {
                                MemberName = "AreTermsAccepted",
                                Message = "You must accept the terms"
                                }};
                    }
                    break;
            }
        }

        return Enumerable.Empty<ModelValidationResult>();
    }
 }

Model validator is also very similar. Please note that container value will be null in the Validate function.

 public class ClientValidator : ModelValidator
 {
    public ClientValidator(ModelMetadata metadata, ControllerContext context)
        : base(metadata, context)
    {
    }

    public override void Validate(Client container, IList<ModelValidationResult> errors)
    {
        var client = (Client)Metadata.Model;

        if (client.ClientName == "Mike")
        {
            errors.Add(new ModelValidationResult
            {
                MemberName = "",
                Message = "Mike is already registered"
            });
        }
    }
}

We register our provider in application global file:

ModelValidatorProviders.Providers.Add(new CustomValidationProvider());

As you can see the MVC framework allows us to apply a lot of validation methods that we can use. It should satisfy even the most complicated scenarios. Of course there is also client side validation that I skipped as this can be a topic for the separate article.

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...Loading...

Custom code validator using RichTextBox

Knowing how to format text using RichTextBox control lets you build wide range of tools such as code comparers, validators, chat applications etc. In this article I will show you how to build simple code checker/ validator.

code-checker

We start with creating new win form application and class library that we will read our test methods from. We will then compare these functions to our pattern methods.

Lets create some dummy methods now.

 public class TestClass
 {
        /// <summary>
        /// Test summary1
        /// this is dummy method
        /// </summary>
        public static void Test1()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Test summary2
        /// this is dummy method
        /// </summary>
        public static void Test2()
        {
            var test = new List<string>();
            test.Add("test1");
            test.Clear();
        }

        /// <summary>
        /// Test summary3
        /// this is dummy method
        /// </summary>
        public static void Test3()
        {
            var test = new List<string>();
            test.Add("test1");
            test.Add("test2");
            test.Add("test3");
            test.Add("test4 123456789");
            test.Add("test5");
            test.Add("test6");
            test.Clear();
        }
    }

Next, we will read this methods using reflection and then we bind it to the listbox control.

  var testTypes = typeof(TestClass).Assembly.GetTypes()
                   .Where(t => t.Name.StartsWith("Test")).OrderBy(t => t.Name).ToList();

  var testMethods = testTypes.First().GetMethods().Where(m => m.IsPublic && m.IsStatic).ToArray();

   lstTests.Items.AddRange(testMethods.ToArray());
   lstTests.SelectedIndex = 0;

When reading the summary tags we will use function that will parse text in .cs file in a search of summary tags.

  internal static string GetComment(string file, string methodName)
   {
            var commentContent = string.Empty;
            var buffer = string.Empty;

            foreach (var line in File.ReadAllLines(file))
            {
                buffer += line;

                if (line.ToLower().Contains(methodName.ToLower()))
                {
                    var summaryPositionStart = buffer.LastIndexOf("<summary>", StringComparison.OrdinalIgnoreCase);
                    var summaryPositionEnd = buffer.LastIndexOf("</summary>", StringComparison.OrdinalIgnoreCase);
                    if (buffer.Length <= summaryPositionStart || summaryPositionEnd - summaryPositionStart <= 0) { return ""; }

                    return PrepareSummaryString(buffer.Substring(summaryPositionStart, summaryPositionEnd - summaryPositionStart));
                }
            }

      return "";
   }

The final step is to format RichTextBox line by line giving it appropriate color depending on our requirements.

  public static void InsertColorText(string inString, RichTextBox textbox, Color methodColor)
   {
            textbox.Text = "";
            inString = inString.TrimEnd(' ', '\r', '\n');

            var lines = inString.Split('\n');
            var font = new Font("Courier New", 8f, FontStyle.Regular);

            for (var i = 0; i < lines.Length; i++)
            {
                textbox.SelectionFont = font;
                textbox.SelectionColor = methodColor;

                if (i < 2 || i + 1 >= lines.Length)
                {
                    textbox.SelectionColor = Color.Gray;
                    textbox.SelectedText = TrimLeft(lines[i], 2);
                    continue;
                }

                if (lines[i].Contains("throw new NotImplementedException();"))
                {
                    textbox.SelectionColor = Color.Red;
                    textbox.SelectedText = TrimLeft(lines[i], 4);
                    continue;
                }

                //calculate color for the current line
                if (lines[i].Trim().Length > 20)
                {
                    textbox.SelectionColor = Color.Blue;
                    textbox.SelectedText = TrimLeft(lines[i], 4);
                    continue;
                }

                textbox.SelectedText = TrimLeft(lines[i], 4);
            }
            textbox.SelectedText = "\n";
        }
    }

Whatever you want to build, this example shows you basic RichTextBox formatting implementation you can use in your solution. I have included working example below for your testing.
RichTextBox-Formatting

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...Loading...