1

I have a ViewModel for which want to be able to set Metadata properties dynamically. For example I would like to be able to customise the DisplayName and validation error messages using the value of other model properties. I plan to do this with a custom MetadataProvider, following Brad Wilson's article.

I want the provider only to be used with selected ViewModels. So my question is, how do I configure that? I have seen examples using ModelMetadataProviders.Current = new MyModelMetadataProvider(), but this would presumably use the custom provider for all model classes entities. Is it possible to configure a provider for a single ViewModel?

Paul Taylor
  • 5,651
  • 5
  • 44
  • 68

2 Answers2

3

You can't.

However, you can act as a proxy for all other models. Something like:

public class YourProvider<TViewModel>
{
    public YourProvider(InnerProvider provider) {}


    public ModelMetaData GetMetaData(SomeContext context)
    {   
        if (context.ModelType != typeof(TViewModel))
            return _innerProvider.GetMetaData(context);

        //Other logic here.

    }
}

And finally assign it as:

ModelMetadataProviders.Current 
    = new MyModelMetadataProvider<CustomViewModel>(ModelMetadataProviders.Current);
jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • that will work, but I don't want to create a dependency in the custom metadata provider on the types that use it. I want it to be client agnostic so that it is reusable in multiple applications. – Paul Taylor Feb 05 '13 at 13:06
  • You said `I want the provider only to be used with the specific view model` and now you are saying otherwise? – jgauffin Feb 05 '13 at 13:08
  • Sorry, should have said "selected viewmodels". Have edited post to that effect. – Paul Taylor Feb 05 '13 at 13:42
0

I'm pretty sure changing the current model metadata provider in the ViewModel is not safe once you start getting multiple users on the site, let alone thread safe. You might be able to use the attribute method but you'll still have to implement your own ModelMetadataProvider and set it to Current at the start of your app, then inspect for certain attributes and determine your ModelMetaData to return, if there are none then fall through to the base implementation. Though to be honest, the amount of restrictions that you're talking about, having it only handle selected view models but not being allowed to know or test for those view models? It sounds like you're doing something wrong elsewhere...

UPDATE: When I needed a ModelMetadata provider I created one that looks something like this...

public class MyMetadataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        if ((containerType != typeof(MyType))
            return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

        //setup custom ModelMetadata here
    }
}
Nick Albrecht
  • 16,607
  • 10
  • 66
  • 101
  • Agreed, don't like it, but I haven't found another solution. I need to dynamically set metadata for specific ViewModels. The custom ModelMetadata provider works, but can't find a way to limit the scope of the effect. Any other ideas? – Paul Taylor Feb 05 '13 at 18:39
  • I ended up creating my own ModelMetadataProvider, and just inherited from `DataAnnotationsModelMetadataProvider`. Then in the CreateMetadata() method I test the containerType and the modelType to see if it's what I'm looking for, else `call base.CreateMetadata()`. Essentially what jgauffin posted. – Nick Albrecht Feb 06 '13 at 15:40
  • That's what I did too, with a few modifications to avoid creating a dependency on a concrete class. But the real point of my question was how to switch to that provider just for a few selected viewmodels. – Paul Taylor Feb 06 '13 at 18:14
  • 1
    Yeah, I don't know of any way to switch the entire provider for a few select ViewModels. The only way I know of is to inherit from `DataAnnotationsModelMetadataProvider` and test for the types you want to handle explicitly, else fall through to the base. If you wanted to you could implement `ModelMetadataProvider`, and using the `DependencyResolver` try and build a system to do this from scratch, but it's not supported using stock MVC, which I believe is what you were looking for. It's a shame they didn't design it with a little more flexibility. – Nick Albrecht Feb 08 '13 at 17:49
  • In the end, went with this approach, but marking the ViewModels that required special treatment with an interface to avoid depending on a type as in the scenario I am dealing with they wouldn't necessarily be known to this component. – Paul Taylor Feb 20 '13 at 16:58