Simple Validation with LINQ to SQL Classes

Avatar

Beth

In the last few posts on LINQ to SQL I’ve showed how to set up an object model using the O/R designer and how to handle a couple data binding scenarios with Comboboxes here and here. Last post on this topic we implemented a one-to-many data entry form and I showed how to work with stored procs as well as how to properly configure delete behaviors. In this post I want to explore how to easily add validation rules to our LINQ to SQL classes and how we can get these rules automatically displayed in the UI.

LINQ to SQL Classes — A Closer Look

Classes that implement the IDataErrorInfo interface in conjunction with INotifyPropertyChanged are able to automatically notify UI objects like the ErrorProvider and the DataGridView to display validation errors. LINQ to SQL classes that are generated for you when you create your object models already implement the INotifyPropertyChanged interface and using partial methods we can easily add validation to our classes. For this example we’ll expand the One-to-Many data entry form we built to include some business rules.

First let’s open up the generated LINQ to SQL classes by opening the .Designer.vb file under the dbml file (if you don’t see the designer file, just click the “show all files” button on the Solution Explorer tool strip first). If we take a look at our Order class we will see the following class definition:

As you can see, this class is just a plain old CLR object (POCO) that implements interfaces that notify when properties are changing or changed on the class. That’s it. It’s the DataContext that does the heavy lifting, knowing what objects have changes, which ones were added and removed and how to persist these to the database. It does its simple mapping via the attributes on the class and it’s properties. Here’s the property for CustomerID on our Order class:

Partial Classes and Methods

In order to add validation and business rules we can add code to the OnCustomerIDChanging and OnCustomerIDChanged methods. But we don’t add them here in this generated file, instead we add them into the Partial Class. This is possible because of a new feature in Visual Studio 2008 called Partial Methods. Partial Classes were introduced in Visual Studio 2005 as a way to extend generated classes with additional functionality by allowing you to split classes across physical files. You can add new methods or properties in order to extend a generated class easily by using the Partial Class keyword and then writing your own code. The compiler will “merge” these files into one class.

In Visual Studio 2008 we can go a step further using Partial methods. Instead of raising and handling private events, partial methods can be used instead as a better performing and cleaner alternative. They are declared by creating a private method with an empty body and decorating it with the Partial keyword. The method may then be “re-implemented” elsewhere within its containing class. If the method is implemented, then the compiler will redirect all calls to the partial method to the implementing method. If the method is not implemented in its containing class, then the compiler silently removes any calls to it from the program.

If we take a look at the generated LINQ to SQL Order class again you can see there is a region called “Extensibility Method Definitions” that contain Partial methods. There will be On…Changed and On…Changing partial methods here for each of the properties on the class.

Notice that the Changing and Changed methods are called at the appropriate times in each of the property setters. This allows us to write clean business rules on these properties in our partial class by defining the partial method. However if we don’t write any code for these partial methods, the calls to them are removed from the IL. To access the partial class code open the model (dbml file) in the O/R designer, select a class, right-click on the class name and select “View Code”. This will create a file named after your model where the partial class code that we write can reside.

So to write some business rules for our classes in this example we’re going to place code in the On…Changing methods and then implement IDataErrorInfo so that we can have the UI automatically display the validation messages. To make it easier to implement IDataErrorInfo on all our LINQ to SQL classes I’m going to create a base class that we can inherit from called BaseBusiness.

Implementing IDataErrorInfo

IDataErrorInfo requires us to implement only two properties, one called Error and one default property Item that both return a string. Error is used to describe what is wrong with the entire object. For instance, if we are displaying this object in a row of a DataGridView this property indicates what error message appears on the row header. If you use DataSets, this corresponds to the DataRow.RowError property. The Item property is used to determine the error message for a specific property (or column) that is passed in. In order to collect these messages for each property on our object (i.e. column in our table) we can use a generic dictionary of strings and either add or remove messages from the dictionary depending on the validation rules. Here’s an example implementation of IDataErrorInfo on our base class:

Next we need to inherit from this class in all our LINQ to SQL Classes. Back in our partial class file we can inherit all our LINQ to SQL classes from BaseBusiness:

Writing the Business Rules

Now we need to write our business rules into these classes. Let’s take Order as an example. There are a couple rules I want to implement here:

  1. The OrderDate cannot be after the ShipDate
  2. The Customer must be specified

There are a couple places that we need to run these rules. One is when any of these fields change and the other is before the changes are submitted to the database. We need to handle both places because the user doesn’t necessarily change all the properties and raise the On…Changing event. We need to handle the case when the user doesn’t fill out a field and immediately attempts to save. This partial method is called OnValidate on our LINQ to SQL classes. This method is called automatically by the DataContext right before it attempts to submit the changes to the database when SubmitChanges is called.

Because you have to run the rules in both of these situations it’s probably easier to create a private method that checks each rule and call that method from both Partial On…Changing and OnValidate methods. If the rule is broken then we just call AddError to add the error message to the dictionary, otherwise we call RemoveError to remove it from the dictionary. Here’s the code for my Order Partial class:

So here we are calling our validation methods from the On…Changing as well as the OnValidate partial methods. If you throw an exception from OnValidate then that will halt the SubmitChanges from going further. I created my own ValidationException class that we can use to check from our save code on the form. I also included any Order Detail errors in the HasErrors property of this Order class. This means that if any order details have errors then this Order also reports that HasErrors is True.

Displaying Validation Errors in the UI

Now that we have our business rules implemented we can hook up the UI to display these messages. Objects that are being displayed in a DataGridView will automatically pick up our validation messages but for simple controls like textboxes we need to hook up an ErrorProvider. Just drag the ErrorProvider from the toolbox onto your form and then specify the BindingSource as the DataSource property, for this example it’s the OrderBindingSource. That’s it. The ErrorProvider will look for any error messages on our objects through the IDataErrorInfo interface. This occurs when a property changes. For instance, if I run the form now and change the Order Date to be after the Ship Date, an error will be displayed as I tab off of the Order Date:

In order to display the messages after validation fails when we attempt to submit changes to the database, we need to write some code to refresh the display. In this scenario we also need to check the OrderBindingSource’s position to make sure that the user is sitting on the invalid Order so the visuals are clear as to what needs fixing. This is why I created my own exception class called ValidationException and added a property to our BaseBusiness class called HasErrors, so that we could easily handle this case.

So back in our form we’ll write the following Save code:

To test this, add a new Order then navigate away from it. When you click Save the validation will fail and you will be positioned back on the order that failed.

I placed the code for this article (including the previous article code on this topic) into a Code Gallery project for you to play with.

Enjoy!

Avatar
Beth Massi

Follow Beth   

0 comments

    Leave a comment