Dec 11 2010

Silverlight…without Ria Services

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

Something More about Ria Services

Ria Services offer an easy solution to the main problems of a typical Business Silverlight application, specifically:

  1. Data Validation. As discussed in a previous post, Ria Services offer support for a data validation based on Data Annotations defined on the server side classes.
  2. Communication with the server and remote validation. Several configuration details of the web service definitions are automatically handled by Ria Services. Moreover, as discussed previously, if the definition of some custom validation attribute is not available on the client side, validation is performed remotely on the server (please refer to the above post for more information).
  3. Ria services automatically set up Asp.net Authentication, Authorization and User Profile Web Services for the Silverlight client.

Unluckily, it is not always possible to use Ria Services, because the Web Services created by Ria Services are specific for Silverlight. Therefore, if one needs a interoperable general purpose service layer that is compatible with various technologies Ria services can not be used.

In such a case the only acceptable option should be the configuration of a Silverlight specific endpoint in a general purpose Web Services. However, often I see developers that do a partial duplication of code implementing a parallel Ria Services Layer specific for the Silverlight clients just to take advantage of the opportunities  offered by Ria Services.

In this post I will show a simple alternative way to obtain the same advantages offered by Ria Services with general purpose Web Services.

Let start with the support for Data Annotations.We can proceed as follows ( I assume  knowledge of the Data Annotations issues discussed in my previous posts here and here.) :

  1. When we define the meta classes of our business classes we  furnish a complete implementation of those classes with the right property types. Moreover, in order to make them serializable we decorate them with the [DataContract] attribute and their properties with the [DataMember ] attribute, since we want they be compatible with Silverlight that has no implementation of the [Serializable] attribute.
  2. We use the above meta classes as View Models or as part of the View Models, both in the server and in Silverlight projects. I advice just linking the same code to both the Silverlight  and  the server projects.
  3. We define our Web Services according to general design constraints, and not only according to Silverlight needs.
  4. We generate the Silverlight proxy, with the option to use already existing classes. This way, we use the original View Models we have linked to the Silverlight project instead of View Models provided automatically by Visual studio during proxy generation.
  5. Once in Silverlight, we wrap the View Model with the Validation Toolkit for Wpf & Silverlight wrapper, in order to furnish it support for both Data Annotations and INotifyPropertyChanged.

With the above procedure we have View Models with Data Validation support without duplicating code, and with the same effort required by the definition of a Ria Domain Service.

The Validation Toolkit for Wpf & Silverlight offers also all tools needed to perform the Remote Validation. On the server we pack all validation errors into an IEnumerable<ValidationErrorInfo> and then we throw a fault Exception: FaultException<IEnumerable<ValidationErrorInfo>> containing the above IEnumerable.

The exception is passed to the client if we decorate the Wcf interface definition with the FaultContract<IEnumerable<ValidationErrorInfo>>] attribute.This is enough for all clients but Silverlight to receive the validation errors. In fact Silverlight is a browser plug-in and as all browser plug-ins it can receive the details of a response only if the status code of the response is either 200(OK) or 404(Not Found), and the status code associated with a fault error normally is not one of them. To overcome this problem we apply the SilverlightFaultBehavior defined in the BindToolkit.Behaviors namespace of the Validation Toolkit for Wpf & Silverlight to the endpoint specific for Silverlight. This endpoint behavior change the status code of the Wcf error response from whatever it is into a 200 status code, thus allowing Silverlight to receive the FaultException.

Once the FaultException is available to the silverlight client,we need just to call the AddValidationFaultExceptionContent(IEnumerable<ValidationErrorInfo> errors) method of the top level wrapper, to send all error messages in their adequate places in the User Interface.

The binary distribution of the Validation Toolkit for Wpf & Silverlight contains a Silverlight example that shows both how to use the wrapper and how to handle remote validation.

Finally, the set up of  Asp.net Authentication, Authorization and User Profile Web Services for the Silverlight client is easy because Asp.net has standard implementations for these Web Service.

For the authentication service we just need to add to our web project an .svc file containing just:

<%@ ServiceHost 
  Language="C#"
  Service="System.Web.ApplicationServices.AuthenticationService" 
  Factory="System.Web.ApplicationServices.ApplicationServicesHostFactory" %>

No code behind or explicit implementation needs to be furnished since the Asp.net class  System.Web.ApplicationServices.AuthenticationService offers a standard implementation of the Authentication Service. However, we need to configure a Membership provider on our Web Site.

In the configuration file we must enable the Authentication Service:

<system.web.extensions>
  <scripting>
    <webServices>
      <authenticationService enabled="true" 
         requireSSL = "true"/>
    </webServices>
  </scripting>
</system.web.extensions>

and we must configure it to use basicHttpBinding(for Silverlight compatibility) and Https:

<services>
    <service name="System.Web.ApplicationServices.AuthenticationService"
        behaviorConfiguration="AuthenticationServiceTypeBehaviors">
      <endpoint contract=
        "System.Web.ApplicationServices.AuthenticationService"
        binding="basicHttpBinding"
        bindingConfiguration="userHttps" 
        bindingNamespace="http://asp.net/ApplicationServices/v200"/>
      </service>
  </services>
  <bindings>
        <basicHttpBinding>
            <binding name="userHttps">
                <security mode="Transport" />
            </binding>
        </basicHttpBinding>
  </bindings>

A detailed reference is here.

Analogously, for the Authorization service we have:

<%@ ServiceHost 
  Language="C#"
  Service="System.Web.ApplicationServices.RoleService" 
  Factory="System.Web.ApplicationServices.ApplicationServicesHostFactory" %>
<system.web.extensions>
  <scripting>
    <webServices>
      <roleService enabled="true"/>
    </webServices>
  </scripting>
</system.web.extensions>
<services>
    <service name="System.Web.ApplicationServices.RoleService"
        behaviorConfiguration="ApplicationServiceTypeBehaviors">
      <endpoint contract=
        "System.Web.ApplicationServices.RoleService"
        binding="basicHttpBinding"
        bindingConfiguration="userHttps" 
        bindingNamespace="http://asp.net/ApplicationServices/v200"/>
    </service>
  </services>
  <bindings>
<basicHttpBinding>
<binding name="userHttps">
<security mode="Transport" />
</binding>
</basicHttpBinding>

a Detailed reference is here.

While for the User Profile we have:

<%@ ServiceHost Language="C#"
Service="System.Web.ApplicationServices.ProfileService" 
Factory="System.Web.ApplicationServices.ApplicationServicesHostFactory" %>
<system.web.extensions>
  <scripting>
    <webServices>
      <profileService enabled="true"
        readAccessProperties="Birthday, FavoriteColor"
        writeAccessProperties="Birthday, FavoriteColor" >
    </webServices>
  </scripting>
</system.web.extensions>

As you can see, while enabling the profile service we need to specify read and write permissions for each property.

The service configuration is quite standard:

<services>
    <service name="System.Web.ApplicationServices.ProfileService"
      behaviorConfiguration="MyServiceTypeBehaviors">
      <endpoint contract=
        "System.Web.ApplicationServices.ProfileService"
        binding="basicHttpBinding" 
        bindingNamespace="http://asp.net/ApplicationServices/v200"/>
    </service>

A detailed Reference is here.

Well. As you can see, the set up of a Silverlight application with all features described in points 1), 2), and 3) is quite easy and can be carried out quickly also without the use of Ria Services. Therefore, there is no need to force the use of Ria Services also when a different solutions should be more adequate.

That’s all for now!. But…..

                                                                                  Stay Tuned !

                                                                                  Francesco

For more information or consulences feel free to contact me

Tags: , , , , , , , ,

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: , , , , , , ,