RIA Services and Validation

A walkthrough of the validation features of RIA Services as a followup of my SilverlightTV recording on the same topic, using samples from the MIX10 Book Club sample...

BookClub Application ScreenshotEarlier today, my SilverlightTV recording on RIA Services and Validation went online. I used validation as a feature area to focus on this first recording on RIA Services, because I think it illustrates both the RIA Services value proposition and key elements of the vision around the project in a very direct manner. Specifically:

  • Focus on end-to-end solutions for data scenarios. It is not sufficient to just address querying data or submitting some changes, but about providing the infrastructure for managing data, editing with validation, tracking errors raised on client and/or server, rolling back changes, and more.
  • The server project and client project are logical halves of the same application. It would be great to preserve and propagate developer intent, and semantics from the database to the middle tier to the client, and share code across tiers where possible. In other words leverage intent and rules as close to the end-user, while enforcing them at each tier. Automatically.
  • Provide out-of-box solutions to common scenarios. The Entity class on the client provides a pretty complete implementation of working with data on the client. It provides identity management, change tracking, change notification, transacted editing and rollback capabilities, as well as tracking and surfacing validation and concurrency conflict errors. In particular, in the context of this post, Entity provides an implementation of infrastructure interfaces such as INotifyDataErrorInfo introduced in Silverlight 4, so you don't have to roll your own.

In my demo for SilverlightTV, I used a product catalog and product editing scenario (my favorite scenario if you ask folks on the team). However for MIX10, I used a Book Club scenario, which I think is quite a bit more interesting (with the potential to become my new favorite scenario).

Check out the video. And then come back to read further. My MIX10 summary post lists everything else going on in the Book Club sample (along with a link to all of the sample code), but the rest of the post focuses on the validation aspects.

Update 3/26/10: For those looking for a copy of the app demo'd on the SilverlightTV show, you can download the Store sample.


Validation Overview/Background

RIA Services supports multiple levels of validation - member/field level validation, entity level validation, and operation level validation. Validation can be expressed as declarative rules via metadata annotations, or implemented via imperative rules in code attached through metadata annotations. These are the metadata annotations in System.ComponentModel.DataAnnotations (ValidationAttribute, and its derivatives). While there is a strong reliance on metadata, a less known fact is that attributes are simply the runtime representation of validation rules - by default, the source of this metadata is based on reflection over types and based on the IL in an assembly, but in reality, you can write custom metadata providers that can pull metadata from any source (such as an XML file, a database, a rules engine or whatever). Metadata attributes simply provide a standardized representation for consumers of the validation semantics.

Validation can happen on the client in addition to the server. It can happen as part of processing a change set submitted to the server or asynchronously as the user is editing an entity in the UI on the client.

As such you can see we have quite deep and extensive support for validation as a core scenario in RIA Services. So now let's look at the different aspects of the validation feature that surface in the Book Club application.


Database Constraints to Validation - Automatically Inferred Validation Rules

A database schema typically defines some basic constraints. For example, a column cannot be null. Or the maximum size of a column. These semantics are captured into the data model if you are using data access technologies such as LINQ to SQL or Entity Framework (as well as other ORMs).

RIA Services provides the smarts to translate this ORM-specific metadata into DAL-agnostic metadata defined in System.ComponentModel.DataAnnotations (eg. RequiredAttribute and StringLengthAttribute), and also propagates it to the client as part of the code generation from server to client. This allows the infrastructure on the client to lookup and apply these rules without the developer having to do anything explicitly. To see this concretely, simply open up the generated code, and check out the metadata on entity members.

The concept at play here is there is a metadata pipeline that spans from database to client, and developer intent flows downstream, without having to re-specify on each tier.

In the Book Club application, the Title column of the Book table is marked as non-nullable. This is reflected on the client with a [Required] attribute on the Title property of the Book entity, causing the following error to show up in the UI when I edit a Book but leave Title as blank.

Automatic Required Validation


Declarative Validation

The developer can annotate the entities on the middle tier via attributes to add additional constraints. For example in the sample, the Book entity has an ASIN property that represents the Amazon ID of a book. This needs to be an alphanumeric field that is 10 characters long. One way to represent this is through a regular expression. There is a [RegularExpression] attribute I can apply.

So here is the code I add on the server:

[MetadataType(typeof(BookMetadata))]
public partial class Book {

    private static class BookMetadata {

        [RegularExpression("^[A-Za-z0-9]{10}$",
           ErrorMessage = "The ASIN must consist of uppercase letters and digits and be 10 characters long.")]
        public static readonly object ASIN = null;
    }
}

Essentially what I am doing is adding a metadata type, BookMetadata, that attaches additional bits of metadata to the Book type.

RIA Services detects this metadata annotation, and determines that there is an implementation of RegularExpressionAttribute on the Silverlight client as well, and as a result propagates this metadata attribute. This allows the client to run the regular expression and validate the ASIN property while a Book is being modified in the user interface. Here is the error I see:

Declarative Regex Validation

You can extend this system, by creating your own custom validation attributes derived from ValidationAttribute, and making it available on both the server and client if appropriate.


Custom Validation

Typically a framework developer will create custom derived ValidationAttribute classes to extend the validation system. An application developer however can choose a simpler route and simply write a class with a validation rule method, and attach that to the entity using the built-in [CustomValidation] attribute.

Here is an example:

[MetadataType(typeof(BookMetadata))]
[CustomValidation(typeof(BookRules), "AddedDateFollowsPublishDate")]
public partial class Book {

    private static class BookMetadata {

        [CustomValidation(typeof(BookRules), "DescriptionIsMeaningful")]
        public static readonly object Description = null;
    }
}

Here I've added both an entity-level and a field-level custom validation rule. Lets look at the implementation of these.

public static partial class BookRules {

    public static ValidationResult DescriptionIsMeaningful(string description, ValidationContext context) {
        if (String.IsNullOrEmpty(description)) {
            return ValidationResult.Success;
        }

        string[] words = description.Split(new char[] { ' ', '\t', '\n', '\r', },
                                           StringSplitOptions.RemoveEmptyEntries);
        if (words.Length > 5) {
            return ValidationResult.Success;
        }

        return new ValidationResult("The description must be at least a sentence to be meaningful.",
                                    new string[] { "Description" });
    }

    public static ValidationResult AddedDateFollowsPublishDate(Book book, ValidationContext context) {
        if (book.PublishDate > book.AddedDate) {
            return new ValidationResult("The book must already be published.",
                                        new string[] { "PublishDate" });
        }

        return ValidationResult.Success;
    }
}

These rules contain your implementation and you can perform any logic you need to perform. Here is what happens when the Description property of a Book isn't fully specified:

Custom Validation

Since these validation rules don't access any server-specific resources or data, they could be executed on the client as well. If this class is defined in a source code file such as BookRules.shared.cs, that causes the file to be linked in into the client project as well.


Server Only and Operation-Level Validation

So far the validation I've been showing was based on rules defined on the entity and enforced on both client and server. Some validation rules apply to operations, for example, only when you are inserting a Book. So they are tied to the operation rather than the entity. Additionally, some rules might need to do a database lookup, or access other server-side resources and as such cannot be executed on the client.

I can attach a validation attribute on the insert method on the DomainService, or implement the rule imperatively within the DomainService method (which is what I do in the sample).

Specifically, in the example, I want to ensure that the ASIN being specified for a book is a valid one, by doing a lookup against the Amazon product catalog (which requires a web service call to Amazon APIs using a secret key stored in server-side configuration that is not sent down to the client):

public class BookShelfService : LinqToEntitiesDomainService<BookClubEntities> {

    [Insert]
    public void ShareBook(Book book) {
        ...

        AmazonService amazonService = new AmazonService();
        if (amazonService.IsValidASIN(book.ASIN) == false) {
            ValidationResult error =
                new ValidationResult("The specified Amazon ID did not resolve to an actual book.",
                                     new string[] { "ASIN" });
            throw new ValidationException(error, null, book);
        }

        ...
    }
}

When the user makes some edits on the client, for example, editing a book, and inserting a new book, the user interface displays the pending changes via edit markers (yellow for modified, and green for new) as such:

Pending Changes Before Submitting

When the user clicks the Save button all the pending changes are packaged into a change set and submitted to the server. As the DomainService methods are invoked on the server, any validation errors are collected and sent back to the client. A good client experience will inform the user there were errors and highlight the entities in error state. In the sample, the UI changes to show error markers, and when the entity is selected, the edit form shows up with error indicators and the error message as raised by the server as shown in this screenshot:

Errors After Submitting


Asynchronous Validation

Sometimes it would be nice to inform the user in advance of an error that can only be determined on the server. For example, if the book being inserted already exists in the database, it would be nice if that could be surfaced in the edit form while the user is editing the book details, rather than committing to the list of pending changes, and only later seeing the error.

In this case, the client user interface doesn't have access to all the books on the client, and so this validation itself needs to be implemented as a server-side validation scenario. However the functionality of the rule can be exposed as an invokable service method to the client (a method can be invoked outside the context of a change set being submitted).

Here is what I have in my service. The BookExists method is used on the server, but also exposed to the client as a regular service method that can be invoked asynchronously.

public class BookShelfService : LinqToEntitiesDomainService<BookClubEntities> {

    [Insert]
    public void ShareBook(Book book) {
        if (BookExists(book.ASIN)) {
            throw new ValidationException("The book already exists and cannot be added.");
        }

        ...
    }

    public bool BookExists(string asin) {
        bool bookExists = false;

        using (BookClubEntities db = new BookClubEntities()) {
            bookExists = db.Books.Any(b => b.ASIN == asin);
        }
        return bookExists;
    }
}

On the client, the application invokes this same BookExists method, and if the server returns true, the client-side code adds a validation error to the list of validation errors being tracked on the Book entity. The entity uses the INotifyDataErrorInfo infrastructure to inform the UI of a new error. The UI picks up this notification to show an error as show in the screenshot below:

Async Validation Errors

Here is the client code to invoke the service method and add an error:

public void CommitEditing() {
    if (IsEditing == false) {
        return;
    }

    if (SelectedBook.Validate(_bookShelfContext.ValidationContext) == false) {
        return;
    }

    if (SelectedBook.IsNew) {
        _bookShelfContext.BookExists(SelectedBook.ASIN,
            delegate(InvokeOperation<bool> operation) {
                if (operation.Value) {
                    ValidationResult duplicateError =
                        new ValidationResult("This book already exists in the book club.",
                                             new string[] { "Title" });
                    SelectedBook.ValidationErrors.Add(duplicateError);
                }
                else {
                    EndEditing();
                }
            }, null);
    }
    else {
        EndEditing();
    }
}


Summary

So there you go - a sample that demonstrates automatic validation, declarative validation, member and entity level custom validation, server-only validation and asynchronous validation. RIA Services provides the capability for you to incorporate validation to create functional end-user experiences in data entry scenarios.

Posted on Thursday, 3/25/2010 @ 9:39 AM | #Silverlight


Comments

45 comments have been posted.

Ben Hayat

Posted on 3/25/2010 @ 11:57 AM
Hi Nikhil;

Great show; BTW, the link to "MIX10 summary post" at top of the article is broken.

A couple of questions:
a) Do you also have the code for the TV show? I saw something on the video that I actually wanted to look at the code itself. The code posted above is for the book club from MIX and not the TV show.
b) One thing that I'm not 100% clear is, "Why" and "When" does a developer decide that a code should be "Shared" and same questions when should we keep things strictly on server? I do see in your presentation and even in the manual, but I still don't have that clear rule in my head that you Share code because of A, B, C reasons and etc. So far I see "How" but the "Why" and "When" not so clear yet. For example, even when I set a Property as "Required" on the server without being shared, it is still generated on the client and at client side, the validation gets to use it, so if RIA generates server side code in client, why would you classify another set of codes as "Shared"?

Your MIX session and this session clears many issues that are not so clear as someone who is just starting and my humble suggestion is that "You" should make a few of these videos that covers other important aspects of RIA. You'll save a lot of confusions and questions on the forum;

Thank you for the great resources you provided!
..Ben

Nikhil Kothari

Posted on 3/25/2010 @ 12:09 PM
@Ben
Thanks for pointing the broken link - fixed it.

I'll pull up the code for the show - hope I still have it - it was on a demo laptop image I'm not running this moment.

On when to share, the real question one needs to ask is can it be shared? For example, does validation require access to a bunch of reference data that is too expensive or is sensitive and cannot be brought down to the client. Or does validation require access to configuration data that cannot be sent to the client. Or does validation require some other general server-side resource. Or does it require access to APIs only available on the desktop .net framework. If it can't be shared, thats a pretty clear answer. Now if it can be shared you have a choice - it boils down to cost - is it too much code to bring down into the client (for example in terms of xap size). Will having that logic on the client improve end-user experience. These are the considerations in my opinion to think about.

RIA Services only generates [CustomValidation] on the client, if it determines the method you're referring to is available on the client, either through an assembly reference, or through shared code. Same rule applies to specific attributes... the code generator looks at RequiredAttribute, and because it is present in the set of assemblies referenced on the client, the required-ness is propagated. If you wrote a custom derived validation attribute, that didn't exist on the client, that attribute would not get propagated.

Ben Hayat

Posted on 3/25/2010 @ 12:34 PM
I sure love the way you answer questions. No need to go back & forth to get to the bottom of things. You perfectly answered what I was asking you. Thank you!
I hope you find the source. If you do, please make a note of it on the top of the blog and I'll check back to pick it up. It was related towards the end of the show where you picked up the error message before you even saved and it went all the way to server to check inventory. I just wanted to see the code.

Nikhil, I really think you should spend some time and go over the RIA manual help to verify if it truly represent the product. Lots of new things have been added but the docs I think hasn't improved since PDC and the last two videos of you show, there is more to explain things that the doc covers. I think for RTW, it should cover more details and samples, i.e. where you said, going back to server and checking if the ID already exist.

Finally [Alexand]RIA is out... :-)
Thank you Sir!

alexander

Posted on 3/25/2010 @ 2:23 PM
hi Nikhil, thanks for the story.

can you may please show us how we can test RIA Services (TDD & RIA Services) ? i think that material will be actual today for programmers.

Steve Ariel

Posted on 3/25/2010 @ 11:14 PM
Hi Nikhil, what about validation on the combo box. The category ID is required in the database but if you add a book and leave it blank you get errors. I also noticed the property changed event does not fire when you change the selection in the combo box, can you tell me why this is ?

Lastly concurrency. Please give us a demo of which events to trap the old HRApp walkthrough from 2008 ria services used to show concurrency handling but the newest has had this removed.

I also want to let people know that they need to install the ria services toolkit to get the code to work. I did not install this and the app compiled but it requires and runs but you get a blank browser window displayed and no error. You need microsoft.servicemodel.domainservices.hosting.dll from the toolkit in order to get the soap services going.

Sorin

Posted on 3/25/2010 @ 11:58 PM
Hi Nikhil
What about some samples about inserting with validation and usind SP to retreive (and filter) data ?

Nikhil Kothari

Posted on 3/26/2010 @ 8:22 AM
@Ben
I uploaded the sample Store app.
FWIW, if you're looking for the async validation done for inventory, then the BookClub app also does similar async validation to see if a book already exists when a book is being shared/added.

@Alexander
Perhaps a future post.

@Steve
Lookup combobox not firing a property change - bug that will be fixed by RTM. Validation for the combobox - a good problem - our generated code doesn't allow for it, but I'll do a post of lookup comboboxes and reference data, and will talk about how to do it.

Concurrency/conflict handling - another post.

@Sorin
The sample does show inserting with validation. The sample also does use stored procs for a separate scenario. Will cover sprocs in yet another post.

Ben Hayat

Posted on 3/26/2010 @ 1:25 PM
@Nikhil;

You're 'D' man! Thanks for finding the sample.
>>>>FWIW, if you're looking for the async validation done for inventory, then the BookClub app also does similar async validation to see if a book already exists when a book is being shared/added.

That's exactly what I was looking for.
Thank you Sir!

Ben Hayat

Posted on 3/26/2010 @ 1:26 PM
Ahh, the link to the show sample code is broken!

Nikhil Kothari

Posted on 3/26/2010 @ 1:51 PM
Strange - the URL is actually correct. Something seems to be a problem on the hosting end. I'm looking into it. For some reason FTP into there doesn't work from corpnet (uggh!). Might put it on skydrive or something if that doesn't work...

Another update:
Was able to finally FTP and upload another file, and it now works. Sorry for the trouble.

Ben Hayat

Posted on 3/26/2010 @ 2:11 PM
Y E S!!! :-)

Thanks!

Kirill Chilingarashvili

Posted on 3/27/2010 @ 2:54 PM
Hi Nikhil,

Great Article!
I have read it two times processing every sentence - there is a precious information here :)
I am currently evaluating validation system in RIA.

I just did not upgrade to Silverlight 4 yet - as I feel it will be released soon and did not want to do upgrade stuff twice. But When trying to replicate samples you did in the post in my project I found many differences.
I am using Silverlight 3 and Ria Service released on 18 November 2009 (created on 10th of November 2009).
I just found that ValidationErrors is ReadOnlyCollection in version I use - and I was not able to add new validation errors to the collection on entity.
Is there a newer version of RIA service released? Because I did not find any newer version for VS 2008 so far.

Also when I fire validation exceptions on server methods they are not picked up on client UI - instead exception fires on the client.
I don't use DataForm - are the validation exception handled and shown correctly only when using DataForm? Or this is again difference of RIA framework version?

Many Thanks,
Kirill

Kirill Chilingarashvili

Posted on 3/27/2010 @ 3:44 PM
Hi Nikhil,

please ignore my question about version - I just understood I missed some news this month :)

here is the text from silverlight 4 document

"Thebuild of WCF RIA Services released at MIX 2010 will require .NET 4, Visual Studio 2010, and Silverlight 4. The PDC 2009 drop of WCF RIA Services is the last build that is targeted purely for developing for .NET 3.5 SP1, Visual Studio 2008, and Silverlight 3. The newer WCF RIA Services drops significantly benefit from and rely on the work done in .NET 4, Visual Studio 2010, Silverlight 4. This is a cost-benefit tradeoff in favor of longer term needs over near term expediency."

In case anyone tries to understand the same as me :)

Thanks,
Kirill

Leifre

Posted on 3/29/2010 @ 12:00 AM
Hey Nikhil,
I was trying out the server-side only validation where the validation check is embedded in the Insert method (in my case i used the update method) and the error reporting was getting all messed up. I set up the domain service method like this:

public void UpdateCustomer(Customer currentCustomer)
{
ValidationResult error =
new ValidationResult("This message should show up in client side bindings.",
new string[] { "CustName" });
throw new ValidationException(error, null, currentCustomer);

// this.ObjectContext.Customers.AttachAsModified(currentCustomer, this.ChangeSet.GetOriginal(currentCustomer));
}

For some reason, when it gets back to client side, the membername that makes it back to clientside in the SubmitOperation.EntitiesInError.First().ValidationErrors.First() is not the string "CustName" but is the string "UpdateCustomer" which is the name of the domain service method which really has me puzzled. Do you know why this would occur?

As a result, the particular field is not put into an invalid state and i cannot programmatically determine which field it is either to be able to put it into an invalid state.

One more question if you dont mind, Is there way to put things into validationresult inside a domain service method without having throw validation exceptions to get it passed back or is it only possible when doing custom validation attribute style of validation?

Thanks!

matthew

Posted on 3/29/2010 @ 2:09 AM
"It provides identity management, change tracking, change notification, transacted editing and rollback capabilities, as well as tracking and surfacing validation and concurrency conflict errors."

I am trying to find out more information about how concurrency conflicts are handled with RIA and Silverlight. I.e. if two users try to edit the same record what will happen - do I need to manually handle this scenario?

Krunal Jariwala

Posted on 3/29/2010 @ 3:05 AM
Hi Nikhil,

That all working well with that, but if i put data form, custom validation not working.
Any other way to display custom validation in data form while lostfocus or editended event?

Leifre

Posted on 3/29/2010 @ 5:53 AM
Well, I ended up figuring out a way to make sure the validation errors get back to client with the correct member names attached. It's non-destructive (ie all existing validation errors continue to exist) and it links in perfectly with built-in client-side validation and even more, supports multiple validation errors at once, multiple property names and does not need to throw exceptions for the validation information to be passed back. Here's the code:

private void AddValidationError(EntityObject ObjectInError, string ErrorMessage, string[] MemberNames)
{
List<ValidationResultInfo> VRSList;
ChangeSetEntry CSE;

if (this.ChangeSet != null && this.ChangeSet.ChangeSetEntries != null)
{
CSE = (from cs in this.ChangeSet.ChangeSetEntries where cs.Entity == ObjectInError select cs).FirstOrDefault();
if (CSE != null)
{
if (CSE.ValidationErrors != null)
VRSList = CSE.ValidationErrors.ToList();
else
VRSList = new List<ValidationResultInfo>();

VRSList.Add(new ValidationResultInfo { Message = ErrorMessage, SourceMemberNames = MemberNames });
CSE.ValidationErrors = VRSList;
}
}
}

Usage:
AddValidationError(currentCustomer, "This is my error message", new string[] { "LocalName" });
AddValidationError(currentCustomer, "This is my second error message", new string[] { "GenName" });

Its easy enough to convert to an extension method for the domain service as well. Only annoying part is that due to ChangeSet protection level, the ChangeSet must be passed in when converting the above function to an extension method. Please feel free to comment on whether this would mess up anything microsoft is doing :)

Ben Hayat

Posted on 3/29/2010 @ 7:31 PM
Hi Nikhil;

I have one question regarding a code you did in the CatalogService.cs. Here is part of the "IsOutOfStock" mehothos:

using (StoreDB storeDB = new StoreDB())
{
product = storeDB.Products.Where(p => p.ProductID == productID).SingleOrDefault();
}

My question is, why did you create a new instance of StoreDB instead of using the existing ObjectContext as following:
product = ObjectContext.Products.Where(p => p.ProductID == productID).SingleOrDefault();

Although, they both work, but I'm sure you had a good reason for that, and I like to know which way is the right way?
Thanks!

Nikhil Kothari

Posted on 3/30/2010 @ 4:45 PM
@Leifre
Expect to see that bug fixed for RTM. The member name should get sent back to the client without having you muck through the changeset...

@Ben
If you're executing a change set, and you want to make sure your db lookups don't cause objects to materialize into the DataContext/ObjectContext, you'll want to use a separate instance. The right way depends on your scenario (which of course is a general statement).

Realizing - there are so many little nuances - I could do with a second column of comments on every line of code... there must be a way to capture and articulate the train of thought that one has when one writes code, whether it is for a sample app or a production app (so other devs can read it).

Ben Hayat

Posted on 3/30/2010 @ 7:42 PM
Don't forget, we are reading "Architect's" codes! :-) So, I'm sure there is wisdom around each corner. That's why I'm paying attentions to these real cases projects.

Thanks for answering my questions.

Lino

Posted on 4/2/2010 @ 1:07 AM
Hi Nikhil,

do you plan to support compile-time checking on validation methods names. For example, when I specify custom validation rule I must write:
[CustomValidation(typeof(BookRules), "DescriptionIsMeaningful")]
and then I need to implment public static method in class BookRules with exact the same name as stated in quotes. Whit this pattern I don't get compile-time cheking of validation method name. This can easily lead to erroneous code.

I think that Lhotka (in CSLA .NET) solved "registering dependency properties by string names" problem. He made overload method for registering properties that takes as parameter System.Linq.Expressions..Expression<TDelegate>. So you can write code like this:

private static PropertyInfo<string> FirstNameProperty = RegisterProperty<string>(c => c.FirstName);
public string FirstName
{
get { return GetProperty(FirstNameProperty); }
set { SetProperty(FirstNameProperty, value); }
}

INSTEAD OF:

private static PropertyInfo<string> FirstNameProperty = RegisterProperty(new PropertyInfo<string>("FirstName"));
public string FirstName
{
get { return GetProperty<string>(FirstNameProperty); }
set { SetProperty<string>(FirstNameProperty, value); }
}

It would be great if you could make something similar to accomplish compile-time checking on properties and methods names (hopefully general in RIA and Silverlight where now you need to provide names in strings).

Tolga

Posted on 4/6/2010 @ 8:27 PM
Hi:
Thanks for your session at MIX.
I am following your "BookShelf" example. Instead of a ListBox I have a DataGrid. Now, in my datagrid how to I show the associated foreign key relationshipt to "Categories". In other words, how do I show the Category name for the books on the datagrid.
Thanks
--tolga

Nikhil Kothari

Posted on 4/6/2010 @ 11:06 PM
@Lino
You can't have LINQ expressions within attribute declarations (at least not as of c# 4).

@Tolga
Use a TemplateColumn and define the cell template (and possibly edit cell template if needed) so it contains a ComboBox bound appropriately to the Category property.

Anthony Dewhirst

Posted on 4/8/2010 @ 10:34 AM
Hi Nikhil,

I am trying to piece together my first RIA Services app (also first Silverlight app) and I am struggling with certain parts.
The first part that I am not sure that I am suing correct is the Server side validation:
If I have a custom method that does the following:
[EnableClientAccess]
public class AuthorDomainService : DomainService
{
public void MyMethod()
{
throw new ValidationException("hello world");
}
}

then, on the client side, I get an error and it tells me to inspect the ValidationErrors. This is empty.
I loaded your demo example and this works for UpdateProduct. But the big difference here is that you are using entities and I am not in this case.

Is this a bug in the code, or am I using this feature incorrectly?

Thanks
Anthony

Anthony Dewhirst

Posted on 4/8/2010 @ 2:17 PM
Hi Nikhil,

Further to what I posted about ValidationExceptions, if I use the AuthenticationBase<T> and override GetAuthenticatedUser or ValidateUser and what to return that the User needs to change their password without logging them in and wanted to do this by passing back ValidationExceptions or Results, how do I go about this as the LoginOperation doesn't appear to support a ValidationErrors list but will tell you to inspect this if you thrown a ValidationException during the call?

Thanks
Anthony

Bart Bories

Posted on 4/11/2010 @ 11:05 AM
Hi Nikhil,

I have a DataForm with properties of 'User', including a ComboBox with 'UserStatus'

In the auto-generated metadatafile I put the [Include] attribute on the 'UserStatus' prop of User and did an .Include() in the get-method. That worked fine.
But I noticed you don't have the auto-generated metadatafile and including one conflicts with the code from your example.

How can I load my related entiy 'UserStatus' with my 'User' object?

thx,
Bart

Nikhil Kothari

Posted on 4/11/2010 @ 1:46 PM
@Bart
I just hand-wrote the metadata class, rather than use the wizard. You can use the wizard instead if you want to. I personally, don't since
a) I don't necessarily want all properties.
b) I like my metadata class as as a nested private static class

Hope that makes sense...

Aaron

Posted on 4/28/2010 @ 8:53 AM
Nikhil, thanks for the code samples. What is driving me absolutely crazy is that everyone who is putting out WCF RIA demos is neglecting to include "Add" functionality. I have only seen "Edit" functionality. This has changed now that you have included "Add" functionality in your Store sample. However, what the "Add" functionality still doesn't address is disabling the "OK" button on the popup until the new product is dirty.

So in your example, you can click the "OK" button on the Product Details popup even if no information has been entered. This is a problem. How does one go about disabling the "OK" button on the popup when the popup is using an Entity object that is new (.EntityState == New or Detached) ??

When you edit a new Entity object, the .HasChanges property still returns FALSE. This doesn't make sense. IMO, .HasChanges should return TRUE any time a property of the Entity object changes. If .HasChanges did indeed return TRUE after a new Entity object's property has changed, then you could effectively enable/disable the "OK" button based on the .HasChanges value.

So the work around I have found is to grab the bindings and manually invalidate the object, which will then not allow the "OK" button to be clicked. I see this as a huge hack and a limitation of WCF RIA.

So my question is, is .HasChanges being equal to FALSE after editing a new Entity object by design or a bug? If it is by design, surely there must be a better way to disable the "OK" button until the new Entity object has changed?

I have posted this issue before on other folks blogs and never seem to receive a response. I would much appreciate one from you or the WCF RIA team.

Thanks much!

Nikhil Kothari

Posted on 4/28/2010 @ 11:56 AM
@Aaron

The fact that a new entity reports HasChanges == false is by design. Conceptually there aren't changes made because there isn't a notion of an original state until you're in an entity set and state and changes to that state are tracked.

Depending on your particular app, it might be ok to have the OK button enabled upfront... if all the properties have meaningful defaults, auto-computed values etc. and you're only giving the user to edit/change those as part of creation. Perhaps this is a bit of a rarer situation.

I think the real requirement is that the New Form or whatever UI you use should close/switch to read-only when the new entity can be committed, i.e. has been determined to be valid. However, you need a trigger to perform validation, esp. entity-level validation. From my perspective the OK button is the trigger. When the user clicks OK, you do the validation, and if everything checks out, you close the form. If something isn't valid, you don't close the form, and you show the errors. As such the OK button needs to be enabled at all times.

Hope that makes sense.

Aaron

Posted on 4/28/2010 @ 1:21 PM
Nikhil, thanks very much for your response.

So I'm a bit confused by your response. Are you saying that I should change my approach to UI design? I'm really not sure that a middle-tier framework should dictate how I design my screens...

Aaron

Posted on 4/28/2010 @ 1:26 PM
Nikhil, I just thought I'd add a bit more.

Drawing on your sample Store project, the user can click "Add new", which displays the popup. Without completing any information, they can click "OK", which closes the form and displays a new empty object (although it lights up pink) in the list. Then the user has to click "Modify" to re-open the form to see what the errors are. This is somewhat "clunky" behavior, and I don't think most users would tolerate this.

Instead, I would think that the user would not be able to click "OK" until the bound Entity object was valid, in which case the "OK" button would become enabled. That being said, it sounds like grabbing the bindings to manually invalidate the new Entity object is the only real way to realize this behavior. And based on your reply, it sounds as if this is the approach you would also take if you were trying to enable/disable the "OK" button based on the validity of the Entity objects state. Is this correct?

Thanks again!

Aaron

Posted on 4/29/2010 @ 7:56 AM
Lastly, there seems to be little to no community support for WCF RIA thus far. Is this a correct assessment? If not, please advise as I am finding it difficult to get my WCF RIA questions answered in a timely manner.

Mahesh

Posted on 5/1/2010 @ 10:57 AM
Nikhil,

Can this example be considered as blu-print for MVVM?

If not, what changes will you consider to make it like one?

Thanks,
Mahesh.

Nikhil Kothari

Posted on 5/4/2010 @ 11:04 AM
@Aaron
I am not suggesting to change your UI design based on what a middle-tier framework can do. My response was entirely based on the needs of the UI and user experience in terms of how validation is triggered, and you can determine when to commit the form. That is clearly independent of what might be happening on the server.

I also mentioned that when you click OK, and there are errors, you would avoid closing the form. So there is no need for the user to Modify a new item just to see/fix errors based on that UI flow.

I am personally not trying to disable the OK button, so I guess, I wouldn't say that is the approach I would take. I haven't really understood what you mean by grabbing bindings etc. but that isn't something I need to do based on the UI flow I envision, and furthermore looking at just bindings doesn't cover entity-level validation, so its a non-starter IMO.

Finally, if you go to the RIA Services forums, you should find community support, as well as participation from the team members.

Nikhil Kothari

Posted on 5/4/2010 @ 11:07 AM
@Mahesh
This is close to how I'd do MVVM (in particular commands in the UI, as opposed to ICommand properties on view model, as well as the conceptual 1:1 mapping of view/viewmodel). There are a few things I'd do differently if I was using more of Silverlight.FX.

And there are a few things I am trying to get into the platform, which would further help clean up MVVM, but that is more future-looking.

Hope that helps.

Mahesh

Posted on 5/14/2010 @ 10:56 AM
Isn't the default validation style supported for comboboxes?

I am trying to implement "Required" validation for Categories combobox while adding a new item in the example that was shown on SilverlightTV, it doesn't seems to support the Red border around combobox kind of validation support.

Is it a bug?

ChangLab

Posted on 6/16/2010 @ 12:48 AM
tell me what the version of silverlight did you test please

Nikhil Kothari

Posted on 6/16/2010 @ 6:56 AM
RIA Services requires Silverlight 4. That is what was used here...

Mahesh

Posted on 7/5/2010 @ 8:34 AM
Any response to Steve Ariel's question Posted on 3/25/2010 @ 11:14 PM ?
Any response to Mahesh's question Posted on 5/14/2010 @ 10:56 AM ?

manga

Posted on 10/22/2010 @ 7:53 AM
Hi Nikhil,

Can the ErrorMessage string be read from a resource file? And if it can be done are there any issues with defining and using a resource string on the server and passing it on to the client?

[Required]
[RegularExpression("^[A-Z0-9]{2,10}$", ErrorMessage = "The model number must be made of upto 10 uppercase letters and digits.")]
public static readonly object ModelNumber = null;

Pritesh Akhani

Posted on 1/11/2011 @ 6:04 AM
Hello nikhil,

I just have one scenario in my project.I have two different kind of users . I want to validate the data for a one user type while i want bypass the validation for another kind of user type.
Is it possible ?

simon

Posted on 4/14/2011 @ 8:02 PM
I have found that the ValidationContext does not get serialized over to the server. Also the ValidationContext is null when Validation is called as part of an end edit. Have i missed something ? The lack of ValidationContext for these scenarios would mean it cant be used consistently inside the validator.

Waleed

Posted on 8/11/2011 @ 4:48 PM
Hello Nikhil,
I would like to ask abouit the validation for Navigation Property like the entitycollection doors inside a car model, how can you create a validation that prevent the doorID or how to test the count of total added doors <= 4.

Appreciate if you could help solve that issue.

Regards

Daniel

Posted on 8/15/2011 @ 3:09 PM
Hi,

I'm creating an application using Silverlight and RIA Services. On a page, I'm displaying a Customer entity as well as his address, which is an other entity. For this, in my viewmodel, I've a property of type "Customer" and it's address property is set to a new entity of type "Address".

How can I validate the data when I want to save the Customer? The user should fill the data for the customer and his address. If both are valid, it should create a new "Customer" entity and a new "Address" entity.

If I write customer.Validate(...), it will validate the Customer entity and it will ignore its "Address" entity. Do I have to validate each entity one by one? As the "Address" entity is a property of the "customer" entity, isn't it possible to validate it as all other customer properties?



Daniel Varrin

Daniel

Posted on 8/15/2011 @ 3:22 PM
Hi,

I'm creating an application using Silverlight and RIA Services. On a page, I'm displaying a Customer entity as well as his address, which is an other entity. For this, in my viewmodel, I've a property of type "Customer" and it's address property is set to a new entity of type "Address".

How can I validate the data when I want to save the Customer? The user should fill the data for the customer and his address. If both are valid, it should create a new "Customer" entity and a new "Address" entity.

If I write customer.Validate(...), it will validate the Customer entity and it will ignore its "Address" entity. Do I have to validate each entity one by one? As the "Address" entity is a property of the "customer" entity, isn't it possible to validate it as all other customer properties?



Daniel Varrin
Post your comment and continue the discussion.