Monday, March 28, 2011

Localized Validation with DataAnnotations in Silverlight

Like the title explains, I like to validate input in my Silverlight application. I want to do it with DataAnnotations and I also want my ErrorMessages to be in the language of the user. Sounds pretty easy, not?

My solution has the following projects
- Data project with my Entity Framework Model
- Web project hosting my Silverlight application and my WCF RIA Services
- Silverlight project, that’s my application

The idee of DataAnnotations is to define the validation in one place and use it in all clients. WCF RIA Services as well as ASP.NET MVC generate client code based on the server site DataAnnotations and that’s pretty cool.

Therefore I build a partial class for my entity ‘Foo’ and set the attribute [MetadataType(typeof(FooMetadata))] on it and build the ‘FooMetadata’ class in which I set the DataAnnotations attributs. To be precise I define the Required attribute on my property ‘Name’. All that in a central place, the Data project.

Then I run the application. Everything works fine. Great!

Now I need the localization. I'm adding a resx File with the error messages for my validation. On the Required attribute I add the resources like this [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ValidationResources))]. So far so good. Now I build the project and I get an error in the client side class generated thru the WCF RIA Services instead of the DataAnnotations attribute. Type ValidationResources is not known in the client project does it say. Hmm, that's probably right.

First idea is to add a common project to my solution, add the resx to this project and reference it from the Silverlight and the Data project. Bad idea because the Silverlight project can only refer Silverlight projects and the Data project is not happy with a Silverlight class library. So I could go the long way and add a 'normal' class library with the resx File and a Silverlight class library with the resx files linked in and so on.

I do it the simple way and link the resx files from the Data project direct to my Silverlight project by clicking Add->Existing Item->Add as Link. Now I got the type with the same namespace in both projects. To make it look nice in Visual Studio I can edit the Silverlight project file and add the dependency beetween the resx and the generated code file in notepad.
WCF RIA Services are generating the right classes with all the attributes and the solution is building. Are we done yet?

When I run the application I discover one last problem. In my generated resx access code file I find the following line:
… global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Namespace.ValidationResources", typeof(ValidationResources).Assembly);…

Because the namespace is different in the two projects, the resx (XML) file can’t be found at runtime. If I change it to the Silverlight project namespace, everything works fine. But if I would run the custom tool again, my changes are gone. This is not a good solution.

So I move the resx file to the Silverlight application. Everything is generated the right way here. Now I link in the generated code file in the Data project to make the type available for the DataAnnotations attributes. That’s it.

It's also not the perfect solution because if I do have a secound GUI like an ASP.NET MVC application I do need the resx file also in this project. So, if there is a better solution out there please let me know!