HiredMind
HiredMind

Reputation: 1857

How to use Data Annotations on the ViewModel?

I'm using RIA Services with LinqToEntitiesDomainService<> and Silverlight 5 Beta to generate the DomainContext code on the client side. I have Data Annotations in my Models/Entities to do basic validations, and when using the Models directly, controls such as DataForm perform validations out-of-the-box. But I'm wrapping my Model in a ViewModel, so I lose all of that automatic validation. Here is a simplified example:

// In DataModel assembly, regenerated on the client side by RIA Services 
public class PetModel 
{
    [Required]
    public string Name { get; set; }
}

// Only on the client side
public class PetViewmodel
{
    private PetModel _model;

    public PetViewmodel(PetModel model)
    {
        _model = model;
    }

    public string Name
    {
        get { return _model.Name; }
        set { _model.Name = value; }
    }
}

My question is: how can I ensure that Name is considered Required on the client side without duplicating all of my annotations in the ViewModel? (I may have to use these Entities with different ViewModels in the future - and I'd like to keep the same annotations)

I thought of manually adding the MetadataType attribute to the ViewModel, pointing to the Model type:

[MetadataType(typeof(PetModel))]  
public class PetViewmodel
{
...
}

But alas, MetadataTypeAttribute is not available in the Silverlight 5 version of System.ComponentModel.DataAnnotations.

Edit: Clarification - the metadata for my entities is included in the entities with a nested class. I code this class manually because my L2E model is in a separate assembly so the DomainService wizard would not generate it for me.

[MetadataType(typeof(Metadata))]  
public partial class PetModel 
{
    [Required]
    public string Name { get; set; }

    public class Metadata 
    {
        [Required]
        public string Name { get; set; }
    }
}

This should be exactly how the wizard would generate the metadata if I understand it correctly. The RIA Services code generator generates the right data annotations in the client code, so it's picking it up correctly.

Upvotes: 4

Views: 2416

Answers (1)

HiredMind
HiredMind

Reputation: 1857

I found a way to do this. In hopes that it will help someone in the future:

I used a base class for the viewmodel that performs validations. On that base class I implement INotifyDataErrorInfo, and I override the NotifyOfPropertyChange method (This is part of Caliburn.Micro, but if you're not using you could just as easily attach to the PropertyChanged event for the model). In the event handler, I perform validation on that property. In the validation code, I use reflection to find the property of the same name, and any validation attributes on the property (RequiredAttribute, RangeAttribute, etc). Then I use the values from these properties to validate the new value on the changed property, creating ValidationResult objects and adding them to a List<ValidationResult>. If all of the 'local' validations pass, then I proceed to perform 'remote' validations (i.e. Validations that require interaction with the server, such as checking for uniqueness).

It was a lot of work, but unfortunately this - or something similar - is necessary. All of the MS examples appear to not use the MVVM. I thought this was just to keep them short/simple, but the Silverlight team at MS appears not to have given serious consideration MVVM when designing the object model.

Upvotes: 1

Related Questions