Implementing Validation in WPF on Entity Framework Entities

I’ve blogged before about implementing validation on LINQ to SQL classes as well as how to customize the display of error messages in WPF. In this post I want to show how you can use these same techniques to validate entities coming from the Entity Framework (EF). Like LINQ to SQL classes, Entity Framework entities are implemented as partial classes so that you can extend them with your own code on top of the code that the designers generate for you. You can extend EF entities in a similar way as LINQ to SQL classes. 

Creating the Partial Class

Let’s take the example that I started here in this How Do I video on building a simple data entry form to edit customers. You can download the code for that video here. In this sample I have two projects, one for the WPF client (WpfEfDataEntry) and one for the Data Access Layer (WpfEfDAL) that contains a simple Entity Data Model (edmx) of a little database I created that tracks customers and their orders.

image

image

To extend the Customer class that is generated from the EF designer, right-click on the DAL project and select Add –> Class then name it Customer. This places the class in the same Namespace as the entities that are generated by the designer. This is necessary for partial classes to work. (Partial classes are just a way that you can define one class in multiple physical files and Visual Studio will handle compiling them into one class for you.)

image Here’s a trick in VB. You know you got your partial class in the right namespace when you drop down the Declarations dropdown and you see the list of partial methods and properties that the class defines.

Also in VB the Partial keyword is only required on one of the class declarations in one of the files. (In C# it’s required on all of them.) The EF designer generates the Customer class with the partial keyword. If you click the “Show all Files” button on the Solution Explorer toolbar and then expand the .edmx you can open the .Designer file and see the entity Partial Class definitions:

 Partial Public Class Customer    

You can of course be explicit in VB and add the Partial keyword to all your partial class files as well.

Adding Validation to the Partial Class

To add validation we can implement the IDataErrorInfo interface in our customer partial class. Using this interface will make validation errors display in Winforms as well as WPF so I tend to prefer this implementation over others like ValidationRules collection in WPF. For this example let’s make sure that the LastName field isn’t empty but we’ll also provide a default value by specifying it in the constructor. This code is the same code we would use if we were working with LINQ to SQL classes.

 Imports System.ComponentModel

Partial Public Class Customer
    Implements IDataErrorInfo

#Region "IDataErrorInfo Members"
    Private m_validationErrors As New Dictionary(Of String, String)

    Private Sub AddError(ByVal columnName As String, ByVal msg As String)
        If Not m_validationErrors.ContainsKey(columnName) Then
            m_validationErrors.Add(columnName, msg)
        End If
    End Sub

    Private Sub RemoveError(ByVal columnName As String)
        If m_validationErrors.ContainsKey(columnName) Then
            m_validationErrors.Remove(columnName)
        End If
    End Sub

    Public ReadOnly Property HasErrors() As Boolean
        Get
            Return m_validationErrors.Count > 0
        End Get
    End Property

    Public ReadOnly Property [Error]() As String _
        Implements System.ComponentModel.IDataErrorInfo.Error
        Get
            If m_validationErrors.Count > 0 Then
                Return "Customer data is invalid"
            Else
                Return Nothing
            End If
        End Get
    End Property

    Default Public ReadOnly Property Item(ByVal columnName As String) As String _
        Implements System.ComponentModel.IDataErrorInfo.Item
        Get
            If m_validationErrors.ContainsKey(columnName) Then
                Return m_validationErrors(columnName).ToString
            Else
                Return Nothing
            End If
        End Get
    End Property
#End Region

    Public Sub New()
        'Set defaults
        Me.LastName = "[new]"
    End Sub

 

Now we can write our validation code to check the LastName field. If you look back at the generated Customer class in the .Designer file, notice that there are OnFieldNameChanging and OnFieldNameChanged methods that are also declared as Partial. These are partial methods, a new feature introduced with Visual Studio 2008, that allow you to supply additional code that is called from the generated class. The Changing/Changed methods are called in the property setters. We’ll define the OnLastNameChanged to make sure the user enters a LastName:

     ''' <summary>
    ''' This method is called in the LastName property setter of the customer
    '''  partial class generated by the Entity Data Model designer.
    ''' </summary>
    Private Sub OnLastNameChanged()
        'Perform validation. 
        If _LastName Is Nothing OrElse _LastName.Trim() = "" OrElse _LastName.Trim() = "[new]" Then
            Me.AddError("LastName", "Please enter a last name.")
        Else
            Me.RemoveError("LastName")
        End If
    End Sub

End Class

Now all we need to do is specify on the binding in the XAML of the WPF form to display the validation errors.

 <TextBox Name="txtLastName" Width="Auto" Height="28" Margin="3" 
         Text="{Binding Path=LastName, ValidatesOnDataErrors =True}"/>

This is exactly the same as we did in this post when working with LINQ to SQL. Read that post to also see how to change the default error template which controls how the errors are displayed. 

Adding Validation to Entity References

As you can see validating scalar properties on EF entities works the same as with LINQ to SQL classes. However what if we wanted to make sure that an entity reference was also specified on an EF entity? I’ve posted before about how to get notified when entity references change. But what if we also want to make sure an entity reference is not empty? For instance, in the case of the Order entity above, how would we write a validation to make sure that the Customer entity reference was specified on the Order before we tried to save?

Looking back up at the Customer (1)—(*) Order in the diagram above, the Order entity has a reference to its Customer parent as specified by the navigation property. In the database there is a foreign key relationship on CustomerID and that is inferred here by EF. This is a difference from LINQ to SQL classes where the classes contain the foreign keys as scalar properties as well. We can’t validate EF entities the same way because there are no scalar properties for the foreign keys. Instead we need to add an event handler to the AssociationChanged event on the entity reference (like I showed before) and then add in our validation. Remember that the AssociationChanged event will fire twice when we are selecting a new reference, once when the old entity reference is removed and then once when the new one is added.

 Imports System.ComponentModel

Public Class Order
    Implements IDataErrorInfo

    Public Sub New()
        'Handle this event so that UI can be notified if the customer is changed
        AddHandler Me.CustomerReference.AssociationChanged, AddressOf Customer_AssociationChanged

        'Set defaults
        Me.OrderDate = Date.Today()
        'Customer is required 
        Me.AddError("Customer", "Please select a customer.")
    End Sub

    Private Sub Customer_AssociationChanged(ByVal sender As Object, _
                                            ByVal e As CollectionChangeEventArgs)
        If e.Action = CollectionChangeAction.Remove Then
            OnPropertyChanging("Customer")
        Else
            If e.Action = CollectionChangeAction.Add Then
                Me.RemoveError("Customer")
            End If
            OnPropertyChanged("Customer")
        End If
    End Sub

imageWhat I’m doing is putting the Order in an immediate error state so that the user can see that a Customer must be selected on the Order before it is valid. The error will only go away once they select a Customer.

I’ve created a sample application that you can download from Code Gallery that demonstrates using EF with WPF in a variety of ways including this example so have a look. Also make sure you check out these How Do I videos on EF and WPF.

Enjoy!