by Jeff Handley via Jeff Handley on 9/30/2010 9:37:56 AM
Since the beginning of the RIA Services project, a primary tenet was to propagate validation rules defined on the server up to the client, integrating the validation directly into the Silverlight UI. This came to fruition with RIA Services v1.0 and you can in fact apply an attribute to your model on the server and automatically get instant client-side validation of data entry on even the simplest of Textbox controls. In fact, some validation rules are inferred from your data model directly. This is a feat that many other frameworks have attempted to accomplish, but I believe RIA Services has one of the most successful implementations of the concept. In this post, we’ll examine how attributes declared on your model are propagated to the client. By understanding what is going on behind-the-scenes, you’ll be able to better leverage validation capabilities.
The entire RIA Services product is centered around its code generation capabilities. You create DomainService classes on the server and RIA Services generates Entity classes within your Silverlight project that mimic your model on the server. You might not ever see the generated code, or even recognize that it’s happening, but it is. Every time you build your solution, RIA Services generates the classes in your Silverlight project that give you the ability to create, read, update, and delete data, and invoke custom operations within your domain services.
I learn best about these types of topics by examining the results—let’s do just that. I encourage you to walk along with me as we take a brief tour through RIA Service’s generated code.
using System; using System.ComponentModel.DataAnnotations; namespace RudeValidation.Web { public class Meeting { [Key] public int MeetingId { get; set; } public DateTime Start { get; set; } public DateTime End { get; set; } public string Title { get; set; } public string Details { get; set; } public string Location { get; set; } public int MinimumAttendees { get; set; } public int MaximumAttendees { get; set; } } }
namespace RudeValidation.Web { using System.Collections.Generic; using System.Linq; using System.ServiceModel.DomainServices.Hosting; using System.ServiceModel.DomainServices.Server; [EnableClientAccess] public class MeetingService : DomainService { public IEnumerable<Meeting> GetMeetings() { return Enumerable.Empty<Meeting>(); } } }
At this point, we have a new Silverlight project, an entity class defined on the server, a DomainService class that exposes it to the client (thanks to the [EnableClientAccess] attribute), and our solution is building. It isn’t obvious, but RIA Services is now generating code within the Silverlight project each time you build. The generated code is not automatically visible within the Silverlight project, but we can navigate to it easily.
You will notice that the generated code includes a handful of types: WebContext, Meeting, MeetingContext, MeetingContext.IMeetingServiceContract, and MeetingContextEntityContainer. Our focus will be on the Meeting class that is generated. There are a few notable details for this class:
/// <summary> /// Gets or sets the 'Title' value. /// </summary> [DataMember()] public string Title { get { return this._title; } set { if ((this._title != value)) { this.OnTitleChanging(value); this.RaiseDataMemberChanging("Title"); this.ValidateProperty("Title", value); this._title = value; this.RaiseDataMemberChanged("Title"); this.OnTitleChanged(); } } }
We will examine this property setter logic in a subsequent post. But at this point, we can see how properties created on our model on the server translate into Entity classes in our Silverlight project, and how every property declared on the server is represented on the client. Our next step is to apply some standard validators to our model and review the effects this has on our generated code.
Now that we’ve seen that RIA Services is performing code generation for our entities, we recognize that validation attributes applied on the server must somehow get propagated to the client, in the Silverlight project’s rendition of the entity class. Let’s again study this by looking at the results of the code generation. Revisiting the post on Standard Validators, let’s apply the same validators we’d used in there. And in addition to the validation attributes, we’ll also use the [Display] attribute.
public class Meeting { [Key] public int MeetingId { get; set; } [Required] public DateTime Start { get; set; } [Required] public DateTime End { get; set; } [Required] [StringLength(80, MinimumLength = 5)] public string Title { get; set; } public string Details { get; set; } [Required] [RegularExpression(@"\d{1,3}/\d{4}", ErrorMessage = "{0} must be in the format of 'Building/Room'")] public string Location { get; set; } [Range(2, 100)] [Display(Name = "Minimum Attendees")] public int MinimumAttendees { get; set; } [Range(2, 100)] [Display(Name = "Maximum Attendees")] public int MaximumAttendees { get; set; } }
The only difference between this code and what was used in the Standard Validators post is that we’re no longer using the ErrorMessageResourceType/ErrorMessageResourceName properties on the StringLength validator. We’ll cover how to use resources for error messages in a later article.
/// <summary> /// Gets or sets the 'Title' value. /// </summary> [DataMember()] [Required()] [StringLength(80, MinimumLength=5)] public string Title { get { return this._title; } set { if ((this._title != value)) { this.OnTitleChanging(value); this.RaiseDataMemberChanging("Title"); this.ValidateProperty("Title", value); this._title = value; this.RaiseDataMemberChanged("Title"); this.OnTitleChanged(); } } }
That’s pretty straight-forward, isn’t it? The attributes we applied on the server have been discovered and generated on the client, in a 1:1 manner. Here’s what’s happening:
In other words, so long as the exact same attribute declaration can be used on both the server and client, the attribute will be propagated. The biggest exception to this is that the Silverlight version of the attribute cannot exist as a standalone class within the main Silverlight project—it must be either referenced or included through the .shared.cs file name convention.
When using the [CustomValidation] attribute, there are some additional constraints that you must abide by. Because your attribute will be propagated to the client as-is, the ValidatorType and Method specified for the custom validation must also be available on the client. This is most often achieved using the .shared.cs file name convention, as mentioned in the Custom Validation Methods post. However, this can also be done using the class library approach.
If you have a [CustomValidation] attribute that does not work properly on the client, you’ll either get a build error stating that the type cannot be found, or you will get a runtime error saying the validation method cannot be found. It should be pretty apparent either way, so if you hit those scenarios, be sure that your validator type and method are discoverable in your Silverlight project.
Now that your validation attributes are propagated to the Silverlight project, the client UI can now execute your validators without having to go back to the server. RIA Services and Silverlight use the static Validator class in the System.ComponentModel.DataAnnotations assembly and namespace to perform this validation. You can read about how the Validator class works in my post from last year. For specifics on when and how this validation occurs, you’ll have to keep following along, as we’ll cover that topic in my next post!
This post is part of an ongoing series on RIA Services Validation. We have now learned how the attributes you declare on the server get propagated to your client code through RIA Services code generation. Hopefully, you now feel empowered to explore new ways to apply validators to your model. In fact, I implore you to look through the generated code for the other validators we applied, along with the [Display] attribute that we included in our model. Then, try deriving from ValidationAttribute in both a .shared.cs file as well as in a WCF RIA Services Class Library. As you make changes, study the effects on the generated code.
Here are the other posts in this series:
As mentioned, my next post will go into details on when and how RIA Services performs validation. We’ll then continue to explore more details of the RIA Services validation features over many other posts that will cover how to perform entity-level validation, how to leverage ValidationContext, how to make your error messages localizable, and more.
Original Post: RIA Services Validation: Attribute Propagation
The content of the postings is owned by the respective author. Silverlight Feeds is not responsible for the contents of the postings. This site is automatically generated and cannot be reviewed for abusive content. If you find abusive content on Silverlight Feeds, please contact us. Designated trademarks and brands are the property of their respective owners. All rights reserved.