How to Validate ASP.NET and MVC Web Forms Using a Business Rules Engine
A Brief Introduction to Web Form Validation
As you know, the purpose of an HTML form is to deliver user input from the client to the server. One of the goals of web applications is to make sure that the incoming data is valid, safe, and ready for further processing. This is called "input validation". The process of input validation can be divided into three steps:
- Format validation. For example, the server needs to make sure that fields don't contain any unsafe values, that the required fields have data, that values of different types can be safely converted into those types, etc. Normally, format validation is done using validation controls.
- Conversion of received string values into typed values. For example, if the form accepts loan applications, the incoming data needs to be converted into some kind of loan application object where the string "annual gross income" might be converted into GrossIncome property of type System.Decimal.
- Optional validation of business rules against strongly typed data. For example, the server might check that annual income is greater than total debt and that the applicant has been employed for the last five years.
The first two steps are well documented and understood. They are almost always required and, to some degree, can be automated using common or custom tools and controls. But the third step is not that simple. Many developers are absolutely sure that they never use business rules in their web forms. And most of them are wrong. The reason is simple: we often mix format and rule validation in one statement without realizing it. Of course, some small forms don't use business rules at all. But that’s typically an exception to the rule. Consider the following code:
DateTime test;
if(txtLastEmployed.Text.Length == 0 ||
!DateTime.TryParse(txtLastEmployed.Text, out test) ||
test < DateTime.Now.AddYears(-5))
{
DisplayWarningMessage("The date of last employment is either empty,
or has invalid format, or is in the past of limit.");
}
In this example, the first condition is format validation, the second is type conversion, and the third is evaluation of a business rule. It's easy to see that the first two conditions will never change unless the LastEmployed property is removed from our imaginary data object. But the third condition is not guaranteed to stay the same forever. Because of the way it's written, if the business owner changes the five year term to any other number, we will have to compile, test, and deploy the entire application all over again just because of this minor change.
Of course, we can always store condition values in configuration files. For example, the following snippet demonstrates such an implementation:
try
{
}
catch(Exception ex)
{
if (ConfigurationManager.AppSettings["Production"] == "true")
{
LogException(ex);
DisplayAnExcuse("Sorry, we screwed up!");
}
else throw ex;
}
This code enforces the business rule "If the environment is production, then log the exception and apologize. Else, throw it up the stack so the developer can work with it". The rule is very small, it contains only one condition, and it's easy to change its value in the configuration file without needing to recompile the entire web app. (Note that this example is only for demonstration. Unless some special exception is expected, you wouldn't want to handle general exceptions in the page code. Exception handling belongs in Global.asax.)
But what if a web form has dozens of input controls and must enforce complicated business rules that can (and certainly will) change in the future? For example, a large shopping cart or a business tax form? In this case, we can eliminate lots of issues, bugs, test cases, and maintenance headaches if we employ a business rules engine.
Let's briefly talk about business rules and rule engines before we dive in.
Business Rules
A business rule is a collection of conditions that can be grouped into rule sets by their execution priorities. You don't need to learn anything special to understand what they are and how they work. Simply attend any corporate meeting and listen to business analysts as they explain requirements for a piece of software. "We need to make sure that all approved applicants are 18 or older and have a credit score of at least 640". This requirement is a business rule. Let's rewrite it in a machine-like format:
If Applicant.Age is greater or equal to 18
and GetCredit(Aplicant.SSN) is greater or equal to 640
then Approve
else Reject
The Applicant class is typically called the “source objects” or “fact objects”. All its public value typed properties (Age, SSN) are called "rule fields" or "facts". Some rules engines also support in-rule methods (GetCredit) that they can invoke during rule evaluation.
Rules can be of two types: evaluation type and execution type. Evaluation type rules are designed to answer only one question: "Does the source object conform to the rule or not?" The return of such a rule is always a Boolean value. This type of rule cannot have actions that must be invoked as a result of the rule evaluation. They also cannot have the "else if" and "else" flow sections. Here is an example of an evaluation type rule:
Check if Car.Year > (Today.Year - 4)
and (Car.Engine.Type = Petrol or Car.Engine.Turbo = True)
Major engines also support execution type rules. These rules allow the division of logic into flow sections, exactly like "if - else" statements in any computer language. Only the "if" section is required in execution type rules. The "else if" and "else" flow sections are optional. Each flow section must contain one or more actions that the engine invokes if the section evaluates to true. In .NET-based engines, rule actions are public instance or static methods that return System.Void. Action methods can be parameterless or take any number of value-typed parameters or collections of values. The execution type rules can also assign values to properties of the source object. Here is an example of an execution type rule:
If Car.Engine.Cylinders = 16
or (Car.Brand = "Ferrari" and Car.Price < 10,000)
then BuyRightAway()
else if Car.Price > 30,000 then MakeOffer(25,000)
else ShopSomewhereElse()
Business Rules Engines
We can define three major features that a rules engine should implement:
- Rules can be created, edited, tested, deployed, and evaluated against source objects without recompilation of the main code.
- Business users must be able to author rules with little to no involvement of the IT department.
- Rule evaluation must be fast.
Building It
So, why is a rules engine great for web form validation? Because once set up, it gives business users the ability to manage business logic without bothering you, the developer. Of course, you could argue that the cost of the engine itself (sometimes hundreds of thousands of dollars) and even the cost of implementation and maintenance of such a system is hardly justifiable when you simply need to validate a web form, even if it's a complicated one. Such systems belong in banks and government agencies where they execute hundreds of huge rulesets against millions and millions of incoming source objects.
However, this is no longer true. Today, business rules are a perfect fit for web form validation if we use the new free or inexpensive rules engines that have recently begun to emerge.
So, let's build a web form that will use such an engine for data validation. There are two things we need to take care of first, though. We need to define the requirements for our small example and we need to know the details about the engine we are going to use. Then we’ll be ready to go.
For the sake of simplifying this article and making the supplementing project more manageable, I took a VS 2012 MVC demo project from a vendor’s website and modified it to suit the needs of this article. Using that project (the download link is available at the top of this article), you will be able to create a rule, fill out the form with test data and evaluate your input against that rule, all in the same HTML view. The final result will look like this screenshot:
In real life, though, you would definitely want to separate the rule editor from the web form. The editor would be part of your intranet web application somewhere on a corporate network. Business users would use it to manage validation rules for your web form(s) and save the rules to a database or a file system. The form that needs to be validated would be hosted on another website. Its server would take care of loading the proper rules from their storage location and evaluating them against the posted input every time a user submits the form.
For our example, we are going to use the relatively new ASP.NET and MVC business rules engine called Code Effects which is implemented as a server control. It has a free version, supports .NET 4.0 and up, comes in a small .NET assembly that you simply reference in your project, and doesn't require installation on the server or any special account privileges, so it can run anywhere. Download a copy of Code Effects here: http://codeeffects.com/Doc/Business-Rule-Engine-Downloader
The Code Effects component has a unique web UI that allows rule authors to create business rules by simply selecting elements from context-sensitive menus. This is very similar to the way IntelliSense works in Visual Studio. It makes rule authoring a very intuitive and easy process for business people, requiring almost no learning curve.
The Code Effects rule editor also employs parentheses to prioritize the order of execution of rule equations. This is quite different from the traditional approach which simply gives each equation a "priority". The use of parentheses greatly simplifies life for rule authors. For example, a typical execution type rule with four equations would look something like this in a traditional rules engine:
When
And
Name = "John" #priority 10
Age < 60 #priority 100
Or
Street = "123 Main Street" #priority 1
City = "London" #priority 10
Then Do()
Else DoNot()
Not really a business user-friendly statement. But with parentheses, the same rule is now readable and understandable by pretty much anybody:
if (Name = "John" and Age < 60)
or (Street = "123 Main Street" and City = "London")
then Do
else DoNot
This is only one of many reasons why I think these new engines are great for web form validation. Very cool stuff.
Let’s not go totally crazy about this particular engine, though. Although it comes with great features and is very fast, it does lack some key features that large corporations usually find invaluable. For example, it does not support the RETE algorithm. Even though about 95% of all projects that I’ve been involved in didn’t really need RETE, this shortcoming of this engine needs to be pointed out. But for our particular case, the Code Effects engine will do just fine.
As I said earlier, I took a vendor’s MVC demo project (http://codeeffects.com/Doc/Business-Rule-Demo-Project) and removed most of it, leaving only a single view with the rule editor and a simple web form on it. This demo project used a class called Patient as the test source object for the rule editor. I know absolutely nothing about medical procedures/processes/policies, and I've never seen any medical validation rules in my life. But that's the whole point of this article - let the professionals worry about their business logic and how they want to handle it. We just need to develop a tool that enables them to do that. So, I left that Patient class as my data source, removing most of its members in order to further simplify this demo.
Let me walk you through the project and its main elements. Again, this is the VS 2012 MVC project. The vendor’s website also provides demos for VS 2010 for both MVC and ASP.NET.
- The project references Code Effects’ assembly called CodeEffects.Rule.dll which is located in the /Lib folder. The /Views/Web.config file references the main Code Effects namespaces, lines 18 and 19.
- The main view is located in the /Views/Post/Index.cshtml file. It declares the rule editor by invoking three Code Effects routines: the first initializes the main CSS theme, the second declares the rule editor, defines its location on the page and sets its basic values, and the last routine actually renders the editor’s HTML and references its client scripts:
@{
ViewBag.Title = "Web Form Validation Example";
Html.CodeEffects().Styles()
.SetTheme(ThemeType.White)
.Render();
}
@using (Html.BeginForm("Evaluate", "Post", FormMethod.Post))
{
<div class="area">
<div style="margin:20px 0;">
<span>Validation output:</span>
<span style="color:Red;">@ViewBag.Message</span>
</div>
<div style="margin:20px 0;">
@{
Html.CodeEffects().RuleEditor()
.Id("ruleEditor")
.Mode(RuleType.Ruleset)
.ShowToolBar(false)
.ShowHelpString(false)
.Rule(ViewBag.Rule)
.Render();
}
</div>
</div>
<div class="area">
<h1 class="title" style="margin-top:20px;">Web Form That Needs Validation</h1>
@{
Html.RenderPartial("_PatientForm");
}
</div>
}
@{
Html.CodeEffects().Scripts().Render();
}
I reused the original demo’s partial view /Views/Shared/_PatientForm.cshtml to include our simple web form to the main Index.cshtml view. The controls in that form represent properties of our Patient source object.
Because we use postback, the view wraps the rule editor and the web form in the HTML Form tag that posts the whole thing to the Evaluate action of the Post controller, which is described below.
- The Patient source object is declared in the /Models/Patient.cs file. It defines several properties that I’m going to use to describe our imaginary medical patient. It also declares several members that are worth noting:
- The Output property. I’ll use it to display error messages. Because I’m not going to set its value directly from my rules, it is hidden from the rule editor with the use of the ExcludeFromEvaluation attribute.
- The public instance Warn() method. It will be used as a rule action to set an error message when the input data of the particular control is invalid.
- The public instance IsEmail() method. This method will be used as Code Effect’s in-rule method to validate the format of the patient’s email address. In-rule methods are .NET methods that can be used in rules as if they were plain properties.
public class Patient
{
public Patient()
{
this.Gender = Gender.Unknown;
}
[Field(Description = "Patient's name", Max = 30)]
public string Name { get; set; }
[Field(Description = "Patient's email", Max = 100)]
public string Email { get; set; }
[Field(ValueInputType = ValueInputType.User)]
public Gender Gender { get; set; }
[Field(Min = 0, Max = 200, Description = "Current pulse")]
public int? Pulse { get; set; }
public bool Allergies { get; set; }
public Address Home { get; set; }
[ExcludeFromEvaluation]
public string Output { get; set; }
[Method("Is Email")]
public bool IsEmail(string email)
{
return Regex.IsMatch(
email,
@"^([\w-]+\.)*?[\w-]+@[\w-]+\.([\w-]+\.)*?[\w]+$",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
}
[Action]
public void Warn(string message)
{
this.Output += "; " + message;
}
}
With Code Effects, you can use any plain .NET class as a source object without adding rule metadata to it. The editor will find all public value typed properties and fields of that class and use them as rule fields. But the Code Effects component defines lots of attributes that we could use to specify how the engine should behave. For example, one of the most useful features of decorating the source class with attributes is the ability to set custom DisplayName values for fields, actions and in-rule methods. The default names such as Patient.Home.Street may look fine to programmers, but business users would prefer to work with the “Home Street” instead. You can refer to Code Effects’ documentation for details on source attributes and how to use them.
- The /Controllers/PostController.cs file declares our Post controller which uses two MVC actions, the GET Index and the POST Evaluate, lines 25 and 34. Both actions instantiate the RuleModel class and send it to the main view using the ViewBag object. Among other things, the rule model instructs Code Effects to reflect our Patient class and use its properties and methods as rule fields, in-rule methods and actions. The Evaluate action also defines the rule evaluation logic which I’ll go over in detail later in this article.
That’s all you need to set up Code Effects in an MVC application. Now, let’s get to the fun part – creation of the actual rules.
Each control in our web form has its own validation logic. Code Effects allows us to evaluate all that logic at once in a single evaluation by setting its RuleEditor.Mode property to the Ruleset value. This mode lets us define multiple complex rules in the same rule area of the editor, using a separate rule for each control that requires validation. In Code Effects, this collection of rules is called a ruleset. The Evaluator class evaluates all rules in a ruleset at once instead of one by one.
To keep this demo simple, I’m going to create only three rules that define my validation logic for the Name, DOB and Email fields. But of course, you can run this demo and create as many rules as you want to in order to become familiar with the editor and its features. This is my ruleset:
If Name has no value or Name is ""
then Warn ("The Name field cannot be blank; ")
If Email has no value or Email is "" or Is Email(Email) is False
then Warn ("The Email field cannot be blank and must be in a correct format; ")
If Allergies is True and (Pulse has no value or Pulse is greater than 140)
then Warn ("No patients with allergies who have abnormal pulse or no pulse at all!")
Here is how it all looks in the client:
If I leave the Name and the Pulse textboxes empty, type “blah blah” into the Email textbox, check the Allergies checkbox and click the Validate button, I get the following:
My web form posted to the Evaluate action, passing it instances of the Patient and the RuleModel as parameters. The action first makes sure that the rule area is not empty and the submitted rule is valid (line 48 of the PostController.) It then gets the XML of my ruleset from the editor, creates a fresh instance of the Evaluator<Patient> class, evaluates the ruleset against the instance of the Patient, and outputs any error messages to the ViewBag object.
Believe it or not, we got our web form and we can evaluate it against our own business rules. I think you’ve already realized that it takes longer to read this article than to actually build the code that it describes. Of course, the real system would be more complex. Code Effects supports lots of features and you can learn more about the editor and the engine by reading their documentation. I would like to point out just one more interesting thing about our code.
Notice how little effort it took the Evaluate action to validate our ruleset. Obviously, a business rule has a very little value if it’s missing elements or the order of the elements is not valid. Machines don't create rules - people do. Therefore, it is absolutely vital to make sure that each rule is valid and ready to be used.
Rule validation is an important step in all major rules engines. Often, it comes as an independent component or a process that you must purchase, install and learn to use separately. Code Effects does it all for you automatically. You don't need to write any code to validate your rules. Code Effects rejects invalid rules and highlights each invalid element, so you immediately know where you made a mistake while creating that rule. Hover your mouse over each highlighted element to see the detailed description of the issue.
Hope this article helped you to understand business rules and web form validation a bit more. Happy programming!