Overview of Data Validation in LightSwitch Applications (Prem Ramanathan)

Overview of Data Validation in LightSwitch Applications (Prem Ramanathan)

Rate This
  • Comments 8

Introduction

In any application that accepts user input and stores it in a data store, the data needs to be validated. When it comes to relational databases, most of them today provide some form of validation. This includes constraints, data formatting, default values, etc. For example, you can’t store a non-numeric value in an integer field. However, for applications that interact with users, there are several drawbacks of relying solely on validation in the data store. One of the major drawbacks of using database validation is that it doesn’t provide an easy way of sharing these rules with the application. Also, it is very hard to convert database errors to meaningful, user-understandable errors.

In a typical interactive application, a significant portion of application code deals with validating data and reporting the errors to the user. Typically the data validation is done by one or more validation rules written by developers. Some are simple rules like validating names against regular expressions, and some are more complex. For example, an application storing employee records may want to make sure that a maximum of 40% of the employees can be on leave on any given day. Typically, in a regular LOB (Line of Business) application, such rules are written by developers mostly using a homegrown infrastructure. These complex rules can often take a lot of effort to write so that they run under the right circumstances.

Another aspect of data validation is presenting the errors to users after running the rules. Typically each application has its own way of presenting errors. The problem of showing these validation errors at the right time is another challenge most of today’s applications face.

With LightSwitch we came up with a simple yet powerful way of writing validation rules, solving the above mentioned problems efficiently. The LightSwitch validation system lets you accomplish the following:

  1. Write validation rules using a simple event driven programming model
  2. Show the visuals for the validation results
  3. Run the validation rules automatically when related data changes

In the following sections, we will examine various aspects of the LightSwitch validation system.

Validation Rules

LightSwitch supports validation for Screens, Entities and DataService (DataService is the provider for entities).

Entity validation rules validate entity properties and entities. Each entity property can specify one or more validation rules that will be run when the entity changes. These rules are accessible from entity designer.

Screen validation rules validate screen properties and screen data. Each screen property can specify one or more validation rules that will be run when relevant data changes. These rules are accessible from screen designer.

DataService validation rules validate entities before saving to storage. These rules run only on server. These rules are accessible from entity designer.

Validation Rule Types

LightSwitch supports two types of validation rules. One is predefined validation (declarative) rules which do not require any kind of code to be written. The other is imperative rules, where you can express business logic in C#/VB code.

Predefined Validation rules

Predefined validation rules perform common validation tasks. They are configured through the LightSwitch designer and are typically in the designer property sheet. Some of the out-of-the-box predefined validation rules in LightSwitch are:

  • Length Validation rule (For string types)
  • Range Validation rule (for integer, decimal types)
  • Decimal Validator for precision, scale validation (for decimal types)
  • Standard entity validator (always runs, validates some of the entity relationship constraints and required values)

Predefined validation rules are shown in the Validation section of the property sheet. For example, to see the predefined validation for an entity property, click the property in the entity designer, and look at the property sheet in validation section. (You can press F4 to bring the property sheet up if it is not visible).

Custom Validation Rules

Custom validation rules provide you with an event handler, where you can write regular C#/VB code to implement the validation rule. Custom Validation rules are the most common type of validation rules. Any complex computation and querying can be performed with them.

The following screenshot shows how to get to predefined and custom validation rules. Also note that, custom validation rules can be reached via Write Code Link on top of the designer.

clip_image001[4]

Screenshot - Validation Rules in property sheet.

 

clip_image002[4]

Screenshot - Write Code dropdown

Validation Workflow

A LightSwitch application runs validation rules at various points in time that is appropriate for the given validation rule. For example, the entity property validation rules are run upon changing the property to provide interactive feedback to the user. The following table lists the different type of validation rules and the scenarios under which they run.

Validation Rule Type

Upon Changing Data

(On Client)

Upon Save

(On Client)

Upon Save

(On Server)

Entity Validation

Yes

Yes

Yes

Screen Validation

Yes

Yes

N/A

DataService Validation

N/A

N/A

Yes

LightSwitch applications do not distinguish between predefined and custom validation rules. They are treated the same way in the running application. When the user tries to save changes on the client application, all validation rules are run again. If there are any validation errors, changes will not be submitted to the server. The user will be asked to correct the changes and try to save again. If there are no validation errors on the client, the changes are submitted to the server.

Once data reaches the server as a result of a save operation, the server will run all the server and common validation rules. If there are any validation errors, the server will reject the save operation and return the validation errors to the client. The user will have to change the data and try to save again. If there are no validation errors found on the server, the server will submit the changes to the data store (database or web service).

The following diagram explains the flow of running validation rules.

image

 

Writing Custom Validation Rules

Custom validation rules can be written for almost all constructs for which you can have validation. This includes entities, screens and entity sets. We already saw how to get to the custom validation code. Now, let’s see what goes into the code of a validation rule.

To write validation for a property on an entity, click on the property in the entity designer, and click the “Write Code” button. You will see “<PropertyName>_Validate” method being shown in the drop down. For example, for the “Name” property, you will see “Name_Validate”. Click the link--it will take you to the code editor with right stubs in place.

This is what C# users will see.

clip_image005[4]

VB users will see this:

clip_image006[4]

You can write your validation logic in the above method (Name_Validate). To report validation results, add validation results to ‘results’ collection. Three type of validation results are supported by LightSwitch.

  1. Validation Errors
  2. Validation Warnings
  3. Validation Information

Validation Errors are the most severe type of validation result. If any validation error is present, then the data can’t be saved to the storage until all the validation errors are corrected.

Validation warnings and Information are used to present warning/Information UI when entering data. Warnings/Information don’t prevent saving data.

EntityValidationResultsBuilder

EntityValidationResultsBuilder is a validation result container that holds all the validation results you add. You can add validation results with one of the four overrides available.

All of the statements below do exactly the same thing.

results.AddPropertyError("Invalid Value");
results.AddPropertyResult("Invalid Value", ValidationSeverity.Error);
results.AddPropertyResult("Invalid Value", ValidationSeverity.Error, Details.Properties.Name);

The last override is interesting because it allows you to specify the property explicitly. Many of you probably wonder what happens if you specify a different property than the one validation rule is associated with. If that happens (say, you specify LastName property), the validation system will attach the validation results to that property instead of attaching them to the current property.

To understand why this might be useful consider having ZipCode and City properties. If ZipCode property changes, then you want to show validation error on City property to notify that it is invalid city. You can achieve this using above API.

 

Save & Validation results

When the user clicks the Save button in a LightSwitch application, all the validation rules are run on the client. If there is any validation error, then the save operation fails. The user can see all the validation errors that caused the failure in the Validation viewer (which we will see in next section).

If there are no validation errors, then the changes are submitted to the server. If any of the server validation rules fail, then the errors are sent back to client, and the user will see those errors in the validation viewer too.

Validation UI on controls

LightSwitch provides a uniform way of showing validation results. Once you add validation rules and run the Application, you can see the validation engine kicking in and doing the work. Typically most of the controls that display the data also show the validation results associated with the data they are showing. For example, a TextBox control showing a Name property will show all the validation results associated with the Name property. Here is an example of three different controls showing validation errors and warnings. The first two show errors, the last one shows warning/information (distinguished by black border).

clip_image008[4]

 

Validation Summary Viewer

Validation summary viewer consolidates all validation results present in the current screen and provides a place to view all of them at once. Also, selecting one of the validation results in the viewer will take the user to the portion of screen data associated with the validation result.

Below is the screenshot showing validation viewer with few validation results. All visible errors are result of running validation on client side.

clip_image009[4]

The screenshot below shows the validation viewer showing errors that came back from the server. To distinguish them from client-side errors, these results are shown in a separate section inside the validation viewer.

clip_image010[4]

 

Walkthrough : EmployeeTrax

In this walkthrough, we will explore creating validation rules for a simple LightSwitch application that handles employee records and vacation plans for the employees. Let’s call this application EmployeeTrax. EmployeeTrax has two tables. Employee table stores information about a single employee, and a vacation table stores the start and end date of the vacation. Each employee record has 1:many relationship to vacation table. i.e., employee can have multiple vacation plans.

EmployeeTrax will have a simple screen that displays employees and vacation information for each employee. In the walkthrough, we will not go into details of how to create table or screen.

Part 1: Create EmployeeTrax application

Launch LightSwitch IDE. Click new project, and select VB or C# application. Enter “EmployeeTrax” as the solution name.

Create Tables

Create Employee table with following fields:

Field Name

Type

IsRequired

FirstName

String

Yes

LastName

String

Yes

DateOfBirth

Date

Yes

JoiningDate

Date

Yes

Create Vacation table with following fields.

Field Name

Type

IsRequired

StartDate

Date

Yes

EndDate

Date

Yes

Description

String

No

Add relationship between tables.

Add Many:1 relationship between vacation and employee tables. i.e., employee table has collection of vacation tables.

Here is sample screenshot of employee table.

clip_image012[4]

Create Screen

  • Right Click on Screens folder node in Solution explorer
  • Click Add New Screen. “Add New Screen” dialog will show up.
  • Select Employees as screen data.
  • Select “Employee Details” and “Employee Vacations” in the additional data section.
  • Click Ok

 

Part 2: Add Validation Rules

Now, let’s add the following validation rules to EmployeeTrax.

  1. FirstName or LastName can’t be null
  2. FirstName or LastName can’t contain non-alphabetic characters.
  3. Vacation start date can’t be later than end date
  4. Vacation can’t overlap for given employee.
  5. More than 40% employees can’t be on vacation on any given day

Let’s start implementing these rules in EmployeeTrax one by one.

Rule 1: FirstName or LastName can’t be null

This is a simple validation rule supported by LightSwitch predefined validation. When you select IsRequired in the designer for the FirstName and LastName field, the predefined validation is added to the field. We don’t have to do anything more here.

Rule 2: FirstName or LastName can’t contain non-alphabetic characters

LightSwitch doesn’t have any predefined validation rule for this type of data validation. Let’s go ahead a write custom validation rule. To do that,

  • Open employee table in the entity designer.
  • Select FirstName property in the entity designer,
  • Click the arrow (the right edge of the button has the arrow) in the WriteCode link button. This will open up the list of code links available.
  • Click the “FirstName_Validate” link in the drop down. This will take you to the code editor with the stub for validation event. Enter the following code. (In bold)
partial void FirstName_Validate(EntityValidationResultsBuilder results)
{
    if (this.FirstName != null &&
        !System.Text.RegularExpressions.Regex.IsMatch(this.FirstName, @"^[a-zA-Z]+$"))
    {
        results.AddPropertyError("Invalid characters in FirstName. Only alphabets allowed.");
    }
}
Do the same for LastName, and enter the following text for LastName_Validate() method.
partial void LastName_Validate(EntityValidationResultsBuilder results)
{
    if (this.LastName != null &&
        !System.Text.RegularExpressions.Regex.IsMatch(this.LastName, @"^[a-zA-Z]+$"))
    {
        results.AddPropertyError("Invalid characters in LastName. Only alphabets allowed.");
    }
}

For VB:

Private Sub FirstName_Validate(ByVal results As EntityValidationResultsBuilder)
    If FirstName IsNot Nothing AndAlso 
Not
System.Text.RegularExpressions.Regex.IsMatch(FirstName, "^[a-zA-Z]+$")
Then results.AddPropertyError("Invalid characters in FirstName. Only alphabets allowed.") End If End Sub Private Sub LastName_Validate(ByVal results As EntityValidationResultsBuilder) If LastName IsNot Nothing AndAlso
Not
System.Text.RegularExpressions.Regex.IsMatch(LastName, "^[a-zA-Z]+$")
Then results.AddPropertyError("Invalid characters in LastName. Only alphabets allowed.") End If End Sub

Rule 3: Vacation StartDate should be earlier than end date in vacation

This is a straightforward rule. To add this rule, open vacation table in entity designer. Click StartDate_Valiate link in WriteCode dropdown. Add the following code.

partial void StartDate_Validate(EntityValidationResultsBuilder results)
{
    if (this.StartDate.CompareTo(this.EndDate) > 0)
    {
        results.AddPropertyError("Start Date should be earlier than end date");
    }
}

For VB : 
Private Sub StartDate_Validate(ByVal results As EntityValidationResultsBuilder)
    If StartDate > EndDate Then
        results.AddPropertyError("Start Date should be earlier than end date")
    End If
End Sub

Rule4: Vacations can’t overlap for a given employee

An employee may have multiple vacation plans. The goal of this rule is to make sure that these plans don’t overlap. This rule is not specific to any property, and applies to the entity as a whole. Also, it needs to be run whenever any property on vacation changes. We have two choices here.

  1. Write the rule for StartDate property of vacation, and whenever StartDate changes, we will validate the vacation plan for the employee against other plans for the same employee. You also need to write the same for EndDate.
  2. Write the rule in the DataService, where it will be run before Vacation entity is saved to storage.

In this example, we will choose option 2. Since we are writing DataService validation, the rules will be run only on server upon save.

To do that, open vacation table in entity designer, click the “Vacations_Validate” link in the WriteCode dropdown. Add the following code in the Vacations_Validate() event.

partial void Vacations_Validate(Vacation entity, EntitySetValidationResultsBuilder results)
{
    foreach (Vacation vacation in entity.Employee.Vacations)
    {
        if (vacation.Id == entity.Id)
            continue;
        if (entity.StartDate.CompareTo(vacation.StartDate) >= 0 && 
entity.StartDate.CompareTo(vacation.EndDate) <= 0) { results.AddEntityError("Vacation plan can't overlap. Correct start date"); } if (entity.EndDate.CompareTo(vacation.StartDate) >= 0 &&
entity.EndDate.CompareTo(vacation.EndDate) <= 0) { results.AddEntityError("Vacation plan can't overlap. Correct end date"); } }
}

For VB :

Private Sub Vacations_Validate(ByVal entity As Vacation, ByVal results As EntitySetValidationResultsBuilder)   
    For Each vacation As Vacation In entity.Employee.Vacations
        If vacation.Id = entity.Id Then
            Continue For
        End If

        If entity.StartDate >= vacation.StartDate AndAlso
                entity.StartDate <= vacation.EndDate Then
            results.AddEntityError("Vacation plan can't overlap. Correct start date")
        End If

        If entity.EndDate >= vacation.StartDate AndAlso
                entity.EndDate <= vacation.EndDate Then
            results.AddEntityError("Vacation plan can't overlap. Correct end date")
        End If
    Next
End Sub

Note: How do you tell if a method is going to execute on server or client? One easy way to figure it out is to look at the code file path. If the file is in Client project, then the rule will execute only in client. If you are looking in the common project, the rule will execute on the client and server. If the file is in the server path, it will run only on the server.

clip_image014[4]

Rule 5: More than 40% can’t be on vacation on any given day

This rule involves retrieving all the vacation records and making sure that there are no more than 40% employees are on leave. We will write this validation rule in DataService which will run only on server.

Note: Writing this rule on the client may make the performance of the application slow depending on the number of employees. Since this rule needs to fetch all the vacation and employee records, it is not recommended to write this type of rule on the client.

To get started, open the vacation table in the entity designer, click “Vacation_Validate” link in the “write code” drop down. This will take you to the code editor (class ApplicationDataService). Add the following code (in bold).

partial void Vacations_Validate(Vacation entity, EntitySetValidationResultsBuilder results)
{
    foreach (Vacation vacation in entity.Employee.Vacations)
    {
        if (vacation.Id == entity.Id)
            continue;
        if (entity.StartDate.CompareTo(vacation.StartDate) >= 0 &&
            entity.StartDate.CompareTo(vacation.EndDate) <= 0)
        {
            results.AddEntityError("Vacation plan can't overlap. Correct start date");
        }

        if (entity.EndDate.CompareTo(vacation.StartDate) >= 0 &&
            entity.EndDate.CompareTo(vacation.EndDate) <= 0)
        {
            results.AddEntityError("Vacation plan can't overlap. Correct end date");
        }
    }

    int overlapCount = 0;
    foreach (Vacation vacation in this.Vacations)
    {
        if (vacation.Employee == entity.Employee)
            continue;

        if (entity.StartDate.CompareTo(vacation.StartDate) >= 0 &&
            entity.StartDate.CompareTo(vacation.EndDate) <= 0)
        {
            overlapCount++;
        }
                
        else if (entity.EndDate.CompareTo(vacation.StartDate) >= 0 &&
            entity.EndDate.CompareTo(vacation.EndDate) <= 0)
        {
            overlapCount++;
        }
    }

    // add the validation error.
    if (overlapCount / (double)this.Employees.Cast<Employee>().Count() > 0.4)
        results.AddEntityError("Vacation dates not available. Choose different dates");
}

For VB:

Private Sub Vacations_Validate(ByVal entity As Vacation, ByVal results As EntitySetValidationResultsBuilder)
    For Each vacation As Vacation In entity.Employee.Vacations
        If vacation.Id = entity.Id Then
                    Continue For
        End If

        If entity.StartDate >= vacation.StartDate AndAlso
               entity.StartDate <= vacation.EndDate Then
            results.AddEntityError("Vacation plan can't overlap. Correct start date")
        End If

        If entity.EndDate >= vacation.StartDate AndAlso
               entity.EndDate <= vacation.EndDate Then
            results.AddEntityError("Vacation plan can't overlap. Correct end date")
        End If

    Next

    Dim overlapCount As Integer = 0
    For Each vacation As Vacation In Me.Vacations

        If vacation.Employee.Equals(entity.Employee) Then
            Continue For
        End If

        If entity.StartDate >= vacation.StartDate AndAlso
               entity.StartDate <= vacation.EndDate Then
            overlapCount = overlapCount + 1
        ElseIf entity.EndDate >= vacation.StartDate AndAlso
               entity.EndDate <= vacation.EndDate Then
            overlapCount = overlapCount + 1
        End If
    Next
    'add the validation error.
    If overlapCount / CDbl(Me.Employees.Cast(Of Employee)().Count()) > 0.4 Then
        results.AddEntityError("Vacation dates not available. Choose different dates")
    End If
End Sub

Part 3: Run the Application

Now press F5 in the LightSwitch IDE. This will launch the EmployeeTrax application. The below screenshot shows the EmployeeTrax application with no data.

clip_image016[4]

Rule1 & Rule2

Click Add button on the list view, and fill in the details. Leave the FirstName column empty. Add some digits to LastName column. Now click Ok. You can see that the red borders around LastName and FirstName fields. Click on one of the fields to bring the validation error popup. The following screenshots shows the same. Note that the popup with error message shows up only when the focus is on the control.

clip_image017[4]

 

The below screenshot shows the validation errors as a result of running Rule 2.

clip_image018[4]

Now, correct the above errors and click save. This will save the data without any errors.

Rule 3

Now, let’s verify Rule3. To do that, click “Add” on the vacation grid, and enter start date later than end date. You will see that error shows up

clip_image019[4]

Fix the above error by changing the start date to 7/1/2010 and save.

Rule 4

Now, let’s see the Rule No.4 in action. Add another vacation plan by clicking add new on the vacation grid. Now, enter start and end date as 7/10/2010 and 7/11/2010. Click Ok. Now you see that no error appears since the rule is a server only rule. Click save. Save will fail with an error. And now take a look at the validation viewer. You can see that the error message we added in Rule4 shows up.

clip_image020[4]

Discard the second overlapping vacation by clicking refresh button on the top of the application (next to save). Click Discard in the confirmation message.

Rule 5

Now, let’s verify Rule5. Add another employee and add vacation plan that overlaps with the first employee. i.e., the start date or end date should be within 7/1/2010 and 8/1/2010. Now click save. Now, you can see that the save fails with error saying that vacation dates are not available.

clip_image022[4]

 

Conclusion

LightSwitch provides a very robust validation system that enables developers to write powerful validation rules with very little effort. I hope this article was informative and provides a good starting point for validation in LightSwitch. Do let me know (by comments) if this is useful, and what topics on validation you would like me to cover in the future. In the next article, we will look under the hood at the validation system and explore the validation running policy and the dependency system.

 

Leave a Comment
  • Please add 1 and 4 and type the answer here:
  • Post
  • Wow. I have implemented this sort of thing manually so I know what it takes. This is the better way of doing things.

  • Hi!

    Why don't you take advantage of the Attributes provided by DataAnnotations?

    Custom coding every validation rule in a real world app instead of leveraging the support what .Net provides and just assigning validators to properties is a no brainer for me :-( This method is like Access in 2000...

    Thanks,

    Attila

  • Attila: Sure you know, .NET is general system platform and C#/VB are general languages, LS goes further toward set of common patterns and its layout in LOB application done right (by done right I mean really right; many developers even dont know why they are not right in some scenarios). Here the patterns are implemented, allowing callbacks to custom bussines code only, with proper extensibility at both server and client side.

    .NET Attributes are declarative at compile time (or heavy way using reflection, aatribute is instance of class), LS is now quite lot compiled ahead but it may "run live model" compilling through DLR in future, so needs to be efficient so here are methods. Designer also know properties and events only and DataAnnotations are rather primarily ASP.NET MVC convention?? Or not? LS has its own level even on top of EF for some reasons.

    As I understood LS, declarative "no code" validations are supported by creating (even quite complex) custom LS entity datatype (sys developers do) which is then reused in bussines applications (app developers do).

    Could somebody clear this?

  • @Attila: LightSwitch abstracts underlying technologies and presents a more uniform experience across the product. In case of validation, LightSwitch allows developers to write extensions which can provide predefined validation rules, which can be set through proeprty sheet. There are few predefined validation rules that come out of the box.  Also, LightSwitch validation is more than just validation errors. You can have Warnings/Informations as part of validation rules which is not supported by .net validation attributes. These are few reasons why LightSwitch doesn't use the .Net attributes.

    @falken007: You are correct that LightSwitch does provide set of common patterns for writing code. The "no code" or predefined validators can be created by pro developers by writing extensions to LightSwitch. Validators apply to types, and entity properties can use validators for that type.

  • That looks nice.

    Is there any way we could use this framework from a desktop WPF app? I'm particularly thinking about the EntityValidationResultsBuilder and the Validation Summary Viewer.

  • Hi,

    Working thru the example, I get the error 'Employee' is not a member of 'EmployeeTrax.Vacation' in the VB code that checks for multiple employees on vacation at same time. VB Line:

    If vacation.Employee.Equals(entity.Employee) Then

        Continue For

    End If

    Error keeps program from compiling. Any ideas or suggestions.

    Thx.

    Vern

  • Thanks Pren!

  • @James: Unfortunately, there is no way currently to reuse EntityValidationResultsBuilder or ValidationSummaryViewer outside of LightSwitch applications. They are custom built for LightSwitch, and not meant for use outside of LightSwitch.

    @VPX: It seems that there is some problem in the relationship between the entities. Open the employee entity in the designer, and verify that it matches the specs as in the screenshot in the blog. Also, make sure that there is 1:Many relationship between employee and vacation.

    -

    Prem.

Page 1 of 1 (8 items)