Oct 6 2010

Data Validation 3: Silverlight, INotifyDataErrors, Ria Services and The Validation Toolkit for WPF & Silverlight

Category: WCF | WPF | Silverlight | Entity Frameworkfrancesco @ 01:06

Data Validation 1

Data Validation 2: Enriching WPF with Data Annotations Support

Something More about Ria Services

Silverlight…without Ria Services

In my previous post I discussed how to enrich WPF with support for Data Annotations and for receiving asynchronous validation error from web services. What about Silverlight? In my first post of this series I already said that Silverlight already offers some support for Data Annotations. However, this support is not native of the silverlight presentation layer but is conveyed by other tools.

In Silverlight data controls, such as the Data Form, the support for data annotations has been injected through the specific way this controls are implemented: the Data Control evaluates itself  the data annotations once it receives input from the user. However this doesn’t solve the general problem.

A better solution is offered by the Ria services that offer their support for Data Annotation through the INotifyDataError interface whose support is native in the Silverlight presentation layer: this way if you use Ria services you have support for Data Annotations for ALL Silverlight controls.

Ria services use an approach that is similar to the wrapper approach of the  Validation Toolkit for WPF & Silverlight. However, they don’t need a wrapper around the class for supplying it an adequate implementation of the INotifyDataError interface: when you define a class to be used with a Ria Web Service, Visual Studio automatically creates an analogous class in the Silverlight project with the same data annotations and put inside it the needed implementation of the INotifyDataError interface.

Moreover, Visual Studio provides also automatic support for asynchronous errors coming from Web Services after the class has been submitted to the server. Such asynchronous errors come from those validation attribute that are defined on the server side version of the class but are not defined on the client side version of the class…..Yes, there might be attributes that are not copied on the client side version of the class! In fact, an attribute is copied on the client side only if the code that defines that attribute is available on the client side. This happens only if the attribute has been defined on the server side in a file with the .shared.cs (or .shared.vb)  extension: only in this case visual studio creates automatically a client side version of the attribute!

Why should one create an attribute definition without the .shared. extension? There are two possible reasons:

  1. The attribute definition requires libraries that are available in .Net but are not available in Silverlight.
  2. However, the most important reason is that you DON’T WANT that attribute be used on the client side because the validation it performs requires data that are available only on the sever such as data coming from a database.

Now it is clear that Ria services obtain the same objective of the Validation Toolkit for WPF & Silverlight with a slightly different technique.

Then, why should one use use the Validation Toolkit for WPF & Silverlight? For two main reasons:

  1. The Web Services cannot be implemented as a Ria Web Services(also called Domain Web Services)
  2. To keep the compatibility with WPF that cannot take advantage of the same type of support offered by Ria services to Silverlight.

 

The INotifyDataErrors interface and its advantages on the IDataErrorInfo interface

The INotifyDataError interface is defined as:

public interface INotifyDataErrorInfo

{

      bool HasErrors { get; }

     event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

     IEnumerable GetErrors(string propertyName);

}

GetErrors has essentially the same role that the indexer in the IDataErrorInfo interface, however it returns a list of errors instead that a single error. This is more correct because there might be several errors for each Control: since WPF only supports the IDataErrorInfo one is forced to concatenate all errors into a single string! Moreover, if one pass to GetErrors the empty string it is supposed to return the object level errors. Also the HasErros property represents an evolution: one is not forced to count the errors by firing the validation routed event to verify if the View Model is valid.

However, the really cool new feature is the ErrorsChanged event. Silverlight UI subscribes to this event and is able to update the error status of the interface also asynchronously when new errors are returned as a result of asynchronous call to web services. This behavior is not possible with WPF since WPF only supports the IDataErrorInfo interface.

The Validation Toolkit for WPF & Silverlight is able to update the error status of the UI in response to asynchronous events just because it renounces to delivery the error to its exact target control, but it just renders asynchronous errors as if they all were object level errors. It is worth to point out that this doesn’t mean all asynchronous errors are delivered to the root of the view model but only that they are delivered to the father object of the property they refers to. Since most of remote errors are actually object level errors this behavior is acceptable…but in any case the right behavior…is better. The examples you find in the binary distribution of the Validation Toolkit for WPF and Silverlight shows this difference in the behavior between the Silverlight and the WPF versions.

How to use the Validation Toolkit for WPF & Silverlight with Silverlight

The use of the Silverlight version of the  Validation Toolkit for WPF and Silverlight is almost identical to use of the WPF version:

  1. First, you have to enable ValidatesOnNotifyDataErrors in Silverlight for the bindings you want to apply validation to. 
  2. Then, you need to wrap your View Model into the dynamic object BindWrapper with the instruction: new BindWrapper(ViewModel, true, true); Setting the second parameter to true causes all son objects of the View Model be recursively wrapped, too. Recursive wrapping will continue also through the boundaries of IEnumerables if the third parameter is set to true .
  3. If  there is no top level View Model class but your top level structure is either a simple enumerable or a dictionary you can wrap recursively through them by calling respectively:
    static ObservableCollection<BindWrapper> WrapEnumerable(IEnumerable source, bool wrapDeep = false, bool wrapEnumerables=false)
    or
    public static ObservableDictionary<string, BindWrapper> WrapDictionary<TValue>(IDictionary<string, TValue> source, bool wrapDeep = false, bool wrapEnumerables = false)
    The result is respectively either an observable collection or an observable dictionary(the observable dictionary type is implemented in the toolkit). The meaning of the parameters is the same as  the ones of the BindWrapper constructor.
  4. Use the wrapper in place of your original object. You can get or set a  property of your original View Model by getting or setting a property of the wrapper with the same name: It is a dynamic object it will accept it, and it will retrieve or update the original property of your View Model while triggering the adequate events to update the interface and to perform validation.
  5. Bind the wrapper to your user interface. In the Silverlight version you need to enclose in square brackets the names of the properties in your Bindings, because Silverlight doesn't support dynamic object and I was forced to use a dictionary. For instance, [Age] instead of Age.
  6. Validation of the simple properties is done automatically. When you want to trigger object level validation on the whole View Model or on a part of it, you call theValidateWholeObject method of the relative wrapper. If Some Validation Exceptions are already available you can pass them to ValidateWholeObject as a parameter.
  7. Each time Validation Exceptions comes from a web service you can call AddRemoteErrors(IEnumerable<ValidationException> remoteErrors)  to update the interface.
  8. If for some reason you need to reset the interface from object level validation errors you can call ResetGlobalEvaluation(), but normally you don't need to do it.

In the next post I will say something more on how to handle validation errors on the server side.

                                     Stay Tuned!

                                      Francesco

For more information or consulences feel free to contact me

Tags: , , , , , , ,

Sep 3 2010

Data Validation 1

Category: WPF | Silverlight | MVC | Entity Frameworkfrancesco @ 22:48

Data Validation 2: Enriching WPF with Data Annotations Support

Data Validation 3: Silverlight, INotifyDataErrors, Ria Services and The Validation Toolkit for WPF & Silverlight

Both Dot Net and Silverlight offer support for data validation through  various “ad hoc” tools. However, the supported tools and the way validation errors are propagated to and handled by the user interface depend on the kind of projects involved in the solution. For instance Silverlight supports automatically the INotifyDataErrors interface that is not available in WPF. It also supports data annotations on the business class but only if RIA services or DataForm control are used. (Don’t worry, I am going to explain all these tools!). Moreover, it is not obvious how to select each time the right tool.

This is the first of a series of posts where I shall explain:

  1. How these tools work, 
  2. How to use them and the pre-built support offered for them in WPF, Silverlight and MVC, and, finally,
  3. How to use and combine them in complex scenarios.

The problem solved by the Data Validation tools is the separation between the normal flow of control and the flow of validation errors. This is a labour-saving strategy whose aim is to allow the programmer to concentrate mainly in implementing his business logic. The programmer only needs to define data validation errors and the associated message errors. Then some standard mechanism  automatically propagate them to the UI interface. In this way the plumbing logic to propagate data errors to the UI is implemented once and for all in a standard way and it is not mixed with the code implementing the business logic.  In this series of  posts we shall see how most of this plumbing logic is already taken care of by the pre-built engines used in various type of applications (MVC, WPF, Silverlight, and dynamic data). RIA services also allow this pre-built plumbing logic propagate through the boundaries of web Services. This way data validation constraints that are defined directly on the business classes on the server  can reach the UI of the Silverlight client.

I summarize what I said in a couple of statements:

  1. Data constraints can be defined in their natural place i.e in the business classes;.
  2. Pre-built plumbing logic allows such a definitions to propagate till the UI with a minimum programming effort.

Let now move describing the Data Validation tools. They are:

  1. Data Annotations. Attributes that decorate either the members or the classes themselves and that state in a declarative way the constraints imposed either on each single member or on the whole  instantiation of the class. There are standard constraints such as [Range(a, b)] that constraint the member value to be within a and b, but also custom constraints that specify user defined code to be executed to verify the constraints.This is the simplest and more effective way to specify constraints, because the code to handle them is completely separated by the code of the class also in the case of custom constraints and can be reused all over the application. Since constraints are stated in a declarative way they are self-documented and they can be easily changed to fit new specifications.
  2. Interfaces IDataErrorInfo and INotifyDataErrors.  The business class may expose validation errors in a standard way by implementing the members of one of these interfaces. Other modules such as the pre-built error handling UI modules of Silverlight, WPF and MVC can query these interfaces to handle the errors. The disadvantage of this approach is that the implementation of the interfaces and hence the error validation code is mixed with the code handling normal operations of the class. However  it takes advantage of standard plumbing logics to propagate errors in the same way as Data Annotations. The better use of these interfaces is letting error defined with Data Annotations propagate to other modules in case the other modules are not able to handle directly Data Annotations: this way it is possible to build easily the plumbing logics to propagate errors through several layers in complex scenarios. Ria services do this! Errors defined on the server-side business classes are propagated to the UI of the client through a INotifyDataErrors interface implementation on the client-side business class that is generated automatically by Visual Studio. INotifyDataErrors has the advantage of exposing errors through events, specifically,event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged ,  while IDataErrorInfo do the same job with the only help of the indexer: string this[ string memberName] { get; }.The event solution is more attractive because it allows asynchronous data validation, for instance with the help of a web service that performs server-side verifications. When the asynchronous call to the web service returns, the UI is updated by raising the event. Obviously also INotifyDataErrors expose directly the errors through the GetErrors methods, and it has also a Boolean member HasErrors that can be used to enable/disable some UI logics. Unluckily, INotifyDataErrors is only available in Silverlight!
  3. Validation Rules. Validation rules are only available in WPF and are defined on the UI, not on the business classes! Therefore a discourage their use and i will not discuss them further.
  4. Exceptions. Exceptions are not a specific tool to handle data validation but sometimes they are useful to help other tools, such as Data Annotations, to propagate validation errors easily through several layers.

The table below summarizes the main properties of the above tools, and it is an useful start point in the design of data validation logic:

  WPF
built-in UI  support
Silverlight
built-in UI  support
MVC
built-in UI  support
Namespaces
IDataErrorInfo yes yes yes System.ComponentModel
INotifyDataErrors absent in .net yes absent in .net System.ComponentModel
Data Annotations no only with RIA or data controls yes System.ComponentModel.DataAnnotations
(need to add reference to assembly with the same name)

As already hinted INotifyDataErrorInfo is available only in Silverlight, while Data Annotations have only a limited built-in support.Silverlight native binding class offers no support for data annotations but data controls such as the DataForm control know how to use the data annotations of the object that is bound to them. Moreover, Ria services offer support for data annotations because Visual Studio wraps the automatically generated client-side business class within a  INotifyDataErrorInfo interface implementation that extracts validation errors from the data annotations. Ria services are able to perform data validation directly on the client-side if all custom data validation attributes are made available on the client side, otherwise they do the validation with the help of the server-side copy of the business object.

For what concerns Asp.net applications while the dynamic data engine relies on data annotations for data validations ad for defining the way data are presented to the user, standard Asp.net applications have no built-in mechanism to take advantage of the above described validation techniques. However, It is worth to point out that also in case of lack of built-in support for data annotations it is easy to validate either single members or whole objects by using the static validation methods of the Validator class in the System.ComponentModel.DataAnnotations namespace. Validations errors extracted this way can be made available to the UI by implementing either INotifyDataErrorInfo or IDataErrorInfo, or, in case of Asp.net applications, by a custom presentation logic.

In the next posts we will see how to use in practice INotifyDataErrorInfo, IDataErrorInfo, and data annotations.

Stay Tuned!

Francesco

Tags: , , , ,