Introduction
This article explains how you can add and enable validation logic in simple POCO (Plain Old Clr Objects) and how they can be consumed by a Silverlight Client through a WCF data service.
Background
I wrote this article because while I was creating a simple validation test project in Silverlight .NET, the validation rules applied to my model didn't fire on the consumer side of the WCF-Service (in this case, a Silverlight Client). I was wondering why the rules didn't get fired when my WCF Data Service was consumed by the client and came to the conclusion that the referenced model class on the client created by the proxy didn't include the validation attributes which I added to the model class. This article shows you a possible solution for this issue.
Architecture
The sample application represents a typical MVVM (Model View ViewModel) architecture. I briefly explain the different projects embedded in the solution:
MVVM_Base
: Base ViewModel project which exposes some classes for enabling the MVVM architecture and validation
MVVM_DataService
: Exposes the WCF services which can be consumed by the client
MVVM_Model_SL
: Silverlight Class Library which holds our model class
MVVM_MyValidation
: Silverlight Client Application
MVVM_MyValidation.Web
: Hosting WebSite
|
An in depth explanation of the MVVM pattern is beyond the scope of this article. So the reader should be familiar with the basic MVVM concepts. |
The Model
I kept the model quit simple, just holding two properties with simple attribute validation. The validation triggering logic is encapsulated in the ValidatingViewModelBase
class, which is part of the MVVM base project.
public class Employee : ValidatingViewModelBase
{
private string _firstName;
[Required(ErrorMessage="Firstname is required!")]
public string FirstName
{
get { return _firstName; }
set { _firstName = value; OnPropertyChanged("FirstName"); }
}
private string _lastName;
[Required(ErrorMessage = "Lastname is required!")]
public string LastName
{
get { return _lastName; }
set { _lastName = value; OnPropertyChanged("LastName"); }
}
}
|
If you look at the code (supplied in the zipfile) for the MVVM_Model_SL project, you will notice that I've added links to the MVVM base classes. I had to do this because I implemented the MVVM_Base project as a ordinary class library (and not a specific Silverlight Class Library), so I can re-use my MVVM implementation in other projects. Adding the links was necessary because you can't reference an ordinary Class Library within a Silverlight Class Library project. |
The Data Service
- The service is a WCF service which implements the
IEmployeeService
and exposes the method GetEmployees
.
- The implementation class returns a list of employee objects.
using MVVM_Model_SL;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace MVVM_DataService
{
[ServiceContract]
public interface IEmployeeService
{
[OperationContract]
List<employee /> GetEmployees();
}
}
using MVVM_Model_SL;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace MVVM_DataService
{
public class EmployeeService : IEmployeeService
{
public List<employee /> GetEmployees()
{
return
new List<employee />(){
new Employee{FirstName="Marc",LastName="Spencer"},
new Employee{FirstName="Julie",LastName="Matthews"};
}
}
}
The ViewModel
- The
ViewModel
is part of our Silverlight application and is the glue between the View and our WCF Data Service.
- The View communicates with and gets its data through the
ViewModel
.
- The code from the
ViewModel
is quite self-explanatory.
namespace MVVM_MyValidation.ViewModels
{
public class EmployeeViewModel : MVVM_Base.ValidatingViewModelBase
{
#region Storage
private EmployeeServiceClient _serviceClient;
private ObservableCollection<employee /> _employeeList;
public ObservableCollection<employee /> EmployeeList
{
get { return _employeeList; }
set { _employeeList = value; OnPropertyChanged("EmployeeList"); }
}
#endregion Storage
#region Ctor
public EmployeeViewModel()
{
_serviceClient = new EmployeeServiceClient();
RefreshEmployees();
}
#endregion Ctor
#region Public Interface
public Int32 NumberOfEmployees
{
get { return _employeeList.Count; }
}
private Employee _currentEmployee;
public Employee CurrentEmployee
{
get { return _currentEmployee; }
set { _currentEmployee = value; OnPropertyChanged("CurrentEmployee"); }
}
#endregion Public Interface
#region Private Interface
private void RefreshEmployees()
{
_serviceClient.GetEmployeesCompleted += (s, e) =>
{
EmployeeList = e.Result;
CurrentEmployee = EmployeeList.Count > 0 ? EmployeeList[0] : null;
};
_serviceClient.GetEmployeesAsync();
}
#endregion Private Interface
}
}
The View
- The View is part of the Silverlight Application.
- The View sets its
DataContext
to the ViewModel
.
namespace MVVM_MyValidation.Views
{
public partial class EmployeeView : Page
{
private EmployeeViewModel _viewModel;
public EmployeeView()
{
InitializeComponent();
_viewModel = new EmployeeViewModel();
Loaded += (s, e) =>
{
DataContext = _viewModel;
};
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
}
}
Once the DataContext
is set, the View can easily bind to its properties through the databinding
capabilities in .NET. Next code snippets highlight and document the most important parts:
- The
DataGrid
binds to the EmployeeList
exposed by our ViewModel
.
- The
DataColumns
bind to the exposed properties of the Employee
.
- The
CurrentEmployee
holds a reference to the current selected Employee
in the DataGrid
.
- The
TextBoxes
bind to the properties of the CurrentEmployee
.
The most interesting parts of our View's binding are the detail columns, which let the user modify the contents of the current Employee
properties. The fact that validation exception is activated on the bound properties (through ValidatesOnDataErrors
and ValidatesOnExceptions
attributes). Strange enough unless you explicitly add the model as reference to the project and configure the service reference to reuse types in all referenced assemblies, validation will not be triggered. The cause is that the generated proxy code from the reference doesn't include the validation code on the client side. So you have to "bypass" this generated class in some way by adding the original model class as a reference to the client project and make sure that the service proxy reference is configured to reuse all types in all referenced assemblies. This way, the GUI will be bound to the model class instead of the generated proxy class. As the original model class contains the validation attributes, data validation will occur. The configuration steps to achieve this are shown below:
*Step 1: Add the model as reference to the client project:
*Step 2: Configure the service reference to reuse types in all referenced assemblies:
That's all folks!