3

when I try to register the model metadata provider with simple injector I don't get any error but when I access the instance is the one by default in mvc.

the way that I'm trying to register the simple injector is the followig;

public static class SimpleInjectorInitializer{
    public static void Initialize(){
        var container = new Container(new ContainerOptions AllowOverridingRegistrations = true});
        InitializeContainer(container);
        container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
        container.RegisterMvcAttributeFilterProvider();
        container.Verify();
        DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
    }
    private static void InitializeContainer(Container container){   
        container.Register<ModelMetadataProvider, MyMetadataProvider>();
    }

}

pedrommuller
  • 15,741
  • 10
  • 76
  • 126

1 Answers1

7

If I'm not mistaken, you will have to register your custom metadata provider to MVC explicitly:

ModelMetadataProviders.Current =
    new MyMetadataProvider();

If you need this instance also to be injectable into types that are resolved from the container, you will also need to register it in the container:

ModelMetadataProviders.Current =
    new MyMetadataProvider();

container.RegisterSingle<ModelMetadataProvider>(
    ModelMetadataProviders.Current);

If you want the container to inject dependencies into your custom MyMetadataProvider, you'll need to do something like this:

container.RegisterSingle<ModelMetadataProvider, MyMetadataProvider>();

container.Verify();

// Call this after you're done configuring the container.
ModelMetadataProviders.Current = 
   container.GetInstance<ModelMetadataProvider>();

Since the ModelMetadataProviders.Current is a singleton, the ModelMetadataProvider must be a singleton, or at least, it should not be a problem for it to be cached for the lifetime of the application and accessed simultaniously from multiple threads. This might not be appropriate for your custom provider, when you inject dependencies into it (since those dependencies could have a shorter lifetime than the singleton lifestyle).

In that case you should create a proxy provider that calls back into the DependencyResolver:

public DependencyResolverModelMetadataProvider : ModelMetadataProvider
{
    public override IEnumerable<ModelMetadata> GetMetadataForProperties(
        object container, Type containerType)
    {
        return GetProvider().GetMetadataForProperties(
            container, containerType);
    }

    public override ModelMetadata GetMetadataForProperty(
        Func<object> modelAccessor, Type containerType, string propertyName)
    {
        return GetProvider().GetMetadataForProperty(
            modelAccessor, containerType, propertyName);
    }

    public override ModelMetadata GetMetadataForType(
        Func<object> modelAccessor, Type modelType)
    {
        return GetProvider().GetMetadataForType(
            modelAccessor, modelType);
    }

    private static ModelMetadataProvider GetProvider()
    {
        return (ModelMetadataProvider)
            DependencyResolver.Current.GetService(
                typeof(ModelMetadataProvider));
    }
}

Since this proxy will call into the DependencyResolver on each call (and contains no state), you can safely create a single instance and store it into the ModelMetadataProviders.Current property.

Your configuration will in that case look as follows:

// register the proxy that calls back into the container.
ModelMetadataProviders.Current =
    new DependencyResolverModelMetadataProvider();

// Register it as transient.
container.Register<ModelMetadataProvider, MyMetadataProvider>();

If you want your custom provider to decorate the original provider, you will need to inject this orginal provider into a property of your custom provider (there are other ways, but this is the easiest thing to do):

var original = ModelMetadataProviders.Current;

// register the proxy that calls back into the container.
ModelMetadataProviders.Current =
    new DependencyResolverModelMetadataProvider();

// Register it as transient.
container.Register<ModelMetadataProvider, MyMetadataProvider>();

container.RegisterInitializer<MyMetadataProvider>(prov =>
{
    // The decorated provider is put as a property on the
    // MyMetadataProvider class.
    prov.DecoratedProvider = original;
});

An alternative approach would be to register the custom MyMetadataProvider as decorator class:

container.RegisterSingle<ModelMetadataProvider>(
    ModelMetadataProviders.Current);

container.RegisterDecorator(
    typeof(ModelMetadataProvider),
    typeof(MyMetadataProvider));

ModelMetadataProviders.Current =
    new DependencyResolverModelMetadataProvider();

By registering MyMetadataProvider as decorator, it will be wrapped around the original registered ModelMetadataProvider (which will be injected into the MyMetadataProvider. You'll need the SimpleInjector.Extensions.dll project to get the RegisterDecorator extensions.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • Hi Steven, I'd like to implement a IoC container to resolve the instance of the ModelMetadataProvider. you can take a look to this approach with structuremap http://stackoverflow.com/questions/5479527/setting-up-dependancyresolver-in-mvc3-using-structuremap-for-modelmetadataprovid – pedrommuller Aug 15 '12 at 14:25
  • definitely registering the ModelMetadataProviders lifestyle as a singleton solve the problem I'm getting the the custom type by now, I haven't realized that the dependencies should have a different lifestyle managment, I'll try to set up the DependencyResolverModelMetadataProvider later, thanks for the help is totally appreciated. – pedrommuller Aug 15 '12 at 20:10