RiaTasks: Central Silverlight Business Rules Validation






4.98/5 (28 votes)
Using Fluent Validation on the website to validate business rules in a Silverlight application

Live Example: http://silverlight.adefwebserver.com/riatasks2businessvalidation/riatasksweb/
Manage Silverlight Business Rules In The Website

There are a lot of methods to provide validation in Silverlight. This article demonstrates a method to provide Business Rule Validation for a Silverlight application in the website that launches the Silverlight application. The reasons you would want to do this are:
- Multiple Silverlight applications sharing the same database will share the same validation rules.
- Normal ASP.NET applications using the same database will share the same validation rules.
- The validation rules are easily maintained.
Simple Silverlight Validation

This article picks up from where the blog post, Basic Silverlight View Model Validation, leaves off. In that blog, we covered simple validation on the client. We were only concerned with validating that a user entered data matched the underlying data type. In that example, we validated that a user either entered a valid date, or did not enter a date at all.
We could have created more validation rules, but, what if there were multiple View Models? We would need to duplicate the validation logic in each View Model. Even if we centralized the validation logic, it could not easily be shared among multiple Silverlight applications, or any ASP.NET websites that were using the same database.
The Silverlight Business Rules Validation Application

Let's look at the final application.
If you do not enter a Name or Description, the application will not save any data, and it will indicate the error message(s) in a scrollable box at the bottom of the screen.

If you enter a date, and it is in the past, it will not save any data, and it will also indicate an error message.
Yes, if you make all three errors at the same time, it will show all of them to you in the scrollable error box.
Website Business Rule Validation

The Silverlight application handles all the validation on the web server.

The flow chart above explains the process.
Fluent Validation

We first start with Fluent Validation. This will allow you to easily create business rules using Linq syntax.
You can download Fluent Validation from here.

We add its assembly to the Website project (not the Silverlight project):

Next, we create a folder called BusinessValidation and a file called BusinessRules.cs and add the following code to it:
using FluentValidation;
using System;
namespace RIATasks.Web
{
// This validator will be used for all operations on the Tasks table
public class TaskValidator : AbstractValidator<Task>
{
// These rules ensure that you always have a Name and Description for a Task
public TaskValidator()
{
RuleFor(Task => Task.TaskName).NotEmpty()
.WithMessage("Please specify Task Name");
RuleFor(Task => Task.TaskDescription).NotEmpty()
.WithMessage("Please specify Task Description");
}
}
// This validator will only be used for Insert operations on the Tasks table
public class TaskInsertValidator : AbstractValidator<Task>
{
public TaskInsertValidator()
{
// When inserting the date must be null or in the future
RuleFor(Task => Task.DueDate).Must(BeADateInTheFuture)
.WithMessage("Please specify a date that has not already passed");
}
// If a non-null date is entered make sure it is in the future
private bool BeADateInTheFuture(DateTime? dtCurrentDate)
{
DateTime dtDateInTheFuture = DateTime.Now.AddDays(1);
return ((dtCurrentDate ?? dtDateInTheFuture) > DateTime.Now.AddDays(-1));
}
}
}
A few things to note:
TaskValidator()
will be used to validate only theUpdate
method.TaskValidator()
andTaskInsertValidator()
will be used to validate theInsert
method.- If the database fields change, this code will not compile (this is a very good thing!).

We then add a file, DataBaseParticalClasses.cs, and add the following code to it:
using FluentValidation;
using System;
using FluentValidation.Results;
using System.Collections.Generic;
using System.ComponentModel;
namespace RIATasks.Web
{
#region public partial class Task
public partial class Task
{
public List<string> Errors = new List<string>();
}
#endregion
}
This adds a Errors
column to the Tasks
table. This provides a property that will pass any errors back to the Silverlight application. You will want to add this for each of your database tables.
Add the following code to the file:
public partial class RIATasksDBDataContext
{
#region UpdateTask
// This runs whenever the Task table is updated
partial void UpdateTask(Task instance)
{
TaskValidator validator = new TaskValidator();
ValidationResult results = validator.Validate(instance);
if (!results.IsValid)
{
// There was an error
foreach (var failure in results.Errors)
{
instance.Errors.Add(failure.ErrorMessage);
}
}
// Only proceed if there are no errors
if (instance.Errors.Count == 0)
{
this.ExecuteDynamicUpdate(instance);
}
}
#endregion
#region InsertTask
// this runs whenever a record is Inserted into the Task table
partial void InsertTask(Task instance)
{
TaskValidator validator = new TaskValidator();
TaskInsertValidator Insertvalidator = new TaskInsertValidator();
ValidationResult results = validator.Validate(instance);
ValidationResult Insertresults = Insertvalidator.Validate(instance);
if (!results.IsValid)
{
foreach (var failure in results.Errors)
{
instance.Errors.Add(failure.ErrorMessage);
}
}
if (!Insertresults.IsValid)
{
foreach (var failure in Insertresults.Errors)
{
instance.Errors.Add(failure.ErrorMessage);
}
}
// Only proceed if there are no errors
if (instance.Errors.Count == 0)
{
// No errors - proceed with Update
this.ExecuteDynamicInsert(instance);
}
}
#endregion
}
This code implements the Update
and Insert
partial methods in the Linq to SQL class.
This code calls the validation code in the TaskValidator()
and TaskInsertValidator()
methods. If there are errors, they are added to the Errors
field, and the change to the database is not made.
If there are no errors, this.ExecuteDynamicUpdate(instance)
is called for an Update
, and this.ExecuteDynamicInsert(instance)
is called for an Insert
.
The Web Service

The Web Service methods need to be altered to process any errors.
Here is the altered Update
method:
#region UpdateTask
[WebMethod]
public Task UpdateTask(Task objTask)
{
RIATasksDBDataContext DB = new RIATasksDBDataContext();
try
{
var result = (from Tasks in DB.Tasks
where Tasks.TaskID == objTask.TaskID
where Tasks.UserID == GetCurrentUserID()
select Tasks).FirstOrDefault();
if (result != null)
{
result.TaskDescription = objTask.TaskDescription;
result.TaskName = objTask.TaskName;
result.DueDate = objTask.DueDate;
DB.SubmitChanges();
// Set any errors
objTask.Errors = result.Errors;
}
}
catch (Exception ex)
{
// Log the error
objTask.Errors.Add(ex.Message);
}
return objTask;
}
#endregion
The Insert
method:
#region InsertTask
[WebMethod]
public Task InsertTask(Task objTask)
{
RIATasksDBDataContext DB = new RIATasksDBDataContext();
try
{
Task InsertTask = new Task();
InsertTask.TaskDescription = objTask.TaskDescription;
InsertTask.TaskName = objTask.TaskName;
InsertTask.UserID = GetCurrentUserID();
InsertTask.DueDate = objTask.DueDate;
DB.Tasks.InsertOnSubmit(InsertTask);
DB.SubmitChanges();
// Set the TaskID
objTask.TaskID = InsertTask.TaskID;
// Set any errors
objTask.Errors = InsertTask.Errors;
}
catch (Exception ex)
{
// Log the error
objTask.Errors.Add(ex.Message);
}
return objTask;
}
#endregion
The Silverlight Application

The only .cs file that needs to be modified in the Silverlight application is the TabControlModel.cs file. The previous Message
property has been changed to a List of type String, and a MessageVisibility
property has been added.
The Update
method is changed to this:
#region UpdateTask
private void UpdateTask(Task objTask)
{
// Call the Model to UpdateTask the Task
TasksModel.UpdateTask(objTask, (Param, EventArgs) =>
{
if (EventArgs.Error == null)
{
// Show any errors
Task objResultTask = EventArgs.Result;
Message = objResultTask.Errors.ToList();
// Set the visibility of the Message ListBox
MessageVisibility = (Message.Count > 0) ?
Visibility.Visible : Visibility.Collapsed;
}
});
}
#endregion
The Insert
method is changed to this:
#region InsertTask
private void InsertTask(Task objTask)
{
// Call the Model to Insert the Task
TasksModel.InsertTask(objTask, (Param, EventArgs) =>
{
if (EventArgs.Error == null)
{
// Set the CurrentTaskID Property
// So it can be selected when Tasks re-load
CurrentTaskID = EventArgs.Result.TaskID;
// Show any errors
Message = EventArgs.Result.Errors.ToList();
// Set the visibility of the Message ListBox
MessageVisibility = (Message.Count > 0) ?
Visibility.Visible : Visibility.Collapsed;
// If there are no errors - refresh Task List
if (Message.Count == 0)
{
GetTasks();
}
}
});
}
#endregion
Display The Errors

The last step is to open up the project in Microsoft Expression Blend 4+ and drop a ListBox
on the View...

...and drag the Message
collection from the Data Context window...

...and drop it on the ListBox
to create the bindings to have it display the error messages.

Next, on the ListBox
, select the Advanced options next to Visibility:

Bind it to the MessageVisibility
property. This will hide the ListBox
when there are no errors.
Simple Validation
For any UI element that needs to be validated for type, meaning, you want to ensure that a Date
is entered into a Date
field, or an Integer
is entered into an number field, you can use this method: Basic Silverlight View Model Validation.
For everything else, you could use the method described here. While this demonstrates validating only the Linq to SQL classes, the same validation classes could be called in the web service methods. This will allow you to create complex business rules that span multiple tables.
What this primarily allows you to do is to put all your validation in one place, that can be consumed by multiple Silverlight Applications and ASP.NET websites.