Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C# 4.0

RiaTasks: Central Silverlight Business Rules Validation

Rate me:
Please Sign up or sign in to vote.
4.98/5 (28 votes)
13 Jul 2010Ms-PL4 min read 79.5K   781   43   27
Using Fluent Validation on the website to validate business rules in a Silverlight application
Image 1

Live Example: http://silverlight.adefwebserver.com/riatasks2businessvalidation/riatasksweb/

Manage Silverlight Business Rules In The Website

Image 2

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

Image 3

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

Image 4

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.

Image 5

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

Image 6

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

Image 7

The flow chart above explains the process.

Fluent Validation

Image 8

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.

Image 9

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

Image 10

Next, we create a folder called BusinessValidation and a file called BusinessRules.cs and add the following code to it:

C#
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!).
Image 11

We then add a file, DataBaseParticalClasses.cs, and add the following code to it:

C#
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:

C#
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

Image 12

The Web Service methods need to be altered to process any errors.

Here is the altered Update method:

C#
#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:

C#
#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

Image 13

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:

C#
#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:

C#
#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

Image 14

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

Image 15

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

Image 16

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

Image 17

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

Image 18

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, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Software Developer (Senior) http://ADefWebserver.com
United States United States
Michael Washington is a Microsoft MVP. He is a ASP.NET and
C# programmer.
He is the founder of
AiHelpWebsite.com,
LightSwitchHelpWebsite.com, and
HoloLensHelpWebsite.com.

He has a son, Zachary and resides in Los Angeles with his wife Valerie.

He is the Author of:

Comments and Discussions

 
QuestionSource code doesn't contain implementation of fluentValidation Pin
md_aliyar5-Oct-12 4:18
md_aliyar5-Oct-12 4:18 
QuestionValidation with Warning in Silverlight Pin
Prithvi Chauhan27-Apr-12 1:47
Prithvi Chauhan27-Apr-12 1:47 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey19-Apr-12 23:45
professionalManoj Kumar Choubey19-Apr-12 23:45 
GeneralMy vote of 5 Pin
Wendelius12-Dec-11 9:14
mentorWendelius12-Dec-11 9:14 
GeneralMy vote of 5 Pin
RaviRanjanKr4-Dec-11 0:59
professionalRaviRanjanKr4-Dec-11 0:59 
GeneralMy vote of 5 Pin
Mohamad K. Ayyash23-Jul-10 12:18
Mohamad K. Ayyash23-Jul-10 12:18 
GeneralRe: My vote of 5 Pin
defwebserver23-Jul-10 17:00
defwebserver23-Jul-10 17:00 
GeneralMy vote of 5 Pin
mukthapk19-Jul-10 1:45
mukthapk19-Jul-10 1:45 
GeneralMy vote of 3 Pin
mukthapk19-Jul-10 1:44
mukthapk19-Jul-10 1:44 
GeneralMy vote of 2 Pin
mukthapk19-Jul-10 1:44
mukthapk19-Jul-10 1:44 
GeneralMy vote of 5 Pin
Kunal Chowdhury «IN»15-Jul-10 7:40
professionalKunal Chowdhury «IN»15-Jul-10 7:40 
GeneralRe: My vote of 5 Pin
defwebserver15-Jul-10 13:34
defwebserver15-Jul-10 13:34 
GeneralWow Pin
Marcelo Ricardo de Oliveira14-Jul-10 8:09
mvaMarcelo Ricardo de Oliveira14-Jul-10 8:09 
GeneralRe: Wow Pin
defwebserver14-Jul-10 8:15
defwebserver14-Jul-10 8:15 
GeneralI'm Impressed Pin
Dewey13-Jul-10 13:40
Dewey13-Jul-10 13:40 
GeneralRe: I'm Impressed Pin
defwebserver13-Jul-10 15:00
defwebserver13-Jul-10 15:00 
GeneralMy vote of 5 Pin
Dewey13-Jul-10 13:37
Dewey13-Jul-10 13:37 
GeneralMy vote of 5 Pin
Sacha Barber13-Jul-10 3:09
Sacha Barber13-Jul-10 3:09 
GeneralRe: My vote of 5 Pin
defwebserver13-Jul-10 3:22
defwebserver13-Jul-10 3:22 
RantNice, but graphically inconsistent again!!! LOL Pin
Alan Beasley13-Jul-10 3:02
Alan Beasley13-Jul-10 3:02 
GeneralRe: Nice, but graphically inconsistent again!!! LOL Pin
defwebserver13-Jul-10 3:23
defwebserver13-Jul-10 3:23 
GeneralNice one again Pin
Sacha Barber13-Jul-10 0:24
Sacha Barber13-Jul-10 0:24 
GeneralRe: Nice one again Pin
defwebserver13-Jul-10 2:13
defwebserver13-Jul-10 2:13 
GeneralRe: Nice one again Pin
Sacha Barber13-Jul-10 2:39
Sacha Barber13-Jul-10 2:39 
GeneralRe: Nice one again Pin
Alan Beasley13-Jul-10 2:59
Alan Beasley13-Jul-10 2:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.