Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

RiaTasks: Central Silverlight Business Rules Validation

0.00/5 (No votes)
13 Jul 2010 2  
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 the Update method.
  • TaskValidator() and TaskInsertValidator() will be used to validate the Insert 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here