3

Is it even possible to setup MVC3 to use the DependencyResolver in order to get a custom ModelMetadataProvider or ModelValidatorProvider? Cause at this point I can't get it to work what so ever via the DependencyResolver. If I set it explicitly via code in the global.asax it works perfectly, but IoC just silently ignores my code. I'm using the StructureMap package from NuGet so it's nothing fancy.

My current hookups via global.asax Global.asax.cs

ModelMetadataProviders.Current = new RedSandMetadataProvider(ModelMetadataProviders.Current);

ModelValidatorProviders.Providers.Add(new RedSandValidatorProvider((IUnitOfWork)DependencyResolver.Current.GetService(typeof (IUnitOfWork))));

These work perfectly. I have to pass the current ModelMetaDataProvider as a constructor to my custom one because you can only have one ModelMetaDataProvider hooked up at a time. So I handle the calls I need to by inspecting method parameters and let the rest fall through to the base implementation.

My ModelValidatorProviders uses a IUnitOfWork object which has a Session property populated by nHibernate. I do this because I need to determine what validation rules are defined in the database based off the property being validated.

Again, these both work. But everytime I try to get them setup using StructureMap so they would be available to the DependencyResolver I can't get the desired result. Has anyone else done this before and gotten it to actually work fully? Is there something fancy I need to do because of the parameters on the constructors? Is there a specific life-cycle that must set in StructureMap's registration of these types? I've looked everywhere for examples of this but they are all either referring to betas or release candidates of MVC3 that don't apply to the final version, or are articles that say that it's possible but don't actually prove it with an example.

I would REALLY appreciate anyone's help with this because I'm pulling my hair out over how simple this should be, that all the resources on the net SAY it's possible, but I can't for the life of replicate any of their claims.

Update

I was using StructureMap.MVC3 1.0.5, I just updated to 1.0.6 after noticing there was an update, however there doesn't seem to be much difference between the versions?

My StructureMap Setup

public static IContainer Initialize() {
    ObjectFactory.Initialize(x =>
                {
                    x.Scan(scan =>
                        {
                                //scan.AssembliesFromApplicationBaseDirectory(); //Would this let us setup dependancy injection to dynamically load plugins?
                                scan.TheCallingAssembly();
                                scan.WithDefaultConventions();
                                scan.LookForRegistries();
                            });
                    //x.For<IExample>().Use<Example>();

                    //x.For<ITempDataProvider>().Use( new CookieTempDataProvider(HttpContext.Current.Request.RequestContext.HttpContext));
                    //x.For<ModelMetadataProvider>().Singleton().Use(new RedSandMetadataProvider(ModelMetadataProviders.Current));
                    //x.For<ModelValidatorProvider>().Use<RedSandValidatorProvider>();
                });
    return ObjectFactory.Container;
}

I've letting the Dependency Resolver be set by the Start() method added by the package using WebActivator.

You can see the lines I was trying to use for registering my metadata and validator providers commented out. I don't know if the way I was doing it were correct or not. I added the call to scan for registries because I have a Registry to configure and add nhibernate to structuremap's container.

Nick Albrecht
  • 16,607
  • 10
  • 66
  • 101
  • 1
    what version of Structuremap.MVC3 are you using? – Paul Mar 29 '11 at 23:46
  • Post your structuremap configuration, the dependency resolver on Nuget uses TryGetInstance method to resolve dependencies in the container, this method doesnt throw an exception when the type is not registered in the container so you dont notice if the type is not registered. – ryudice Mar 30 '11 at 05:03
  • K, I've added the details you mentioned to the question. – Nick Albrecht Mar 30 '11 at 17:25

1 Answers1

1

I think I've FINALLY solved my own problem. I don't know why the Validation Provider works now, it may have been due to the change in the implementation of the GetServices() method in the SmDependencyResolver.cs file that I noticed after updating the StructureMap.MVC3 reference, I'm not 100% sure.

But the ModelMetaDataProvider did had a problem. The example I was using from the net for implementing a custom ModelMetaDataProvider had it setup in the global.asax file like so.

ModelMetadataProviders.Current = new RedSandMetadataProvider(ModelMetadataProviders.Current);

Now this works fine, and it's only ever hit once when the app starts. But for implementing it in IoC I think it was causing a problem due to the ModelMetadataProviders.Current reference. I don't know why I never got an error but on a whim I changed it to instantiate a new DataAnnotationsModelMetadataProvider() instance and that seemed to resolve the problem. My registration of the custom ModelMetadataProvider in structure map looks like this now.

x.For<ModelMetadataProvider>().Use(new RedSandMetadataProvider(new DataAnnotationsModelMetadataProvider()));

I hope this helps anyone else who may have had this problem.

UPDATE: Followup to Rudimenter's question: I actually had some difficulty with controlling the lifetime of my provider as well. My ModelMetaDataprovider is pulling information from a database to determine the model metadata I would pass back but because of the way MVC controls the ModelMetadataProvider the lifetime of my datasource was out of sync with the lifetime of the provider. In the end I admit I took the lazy way out and created a private property in my provider that returned my datasource with it's lifetime controlled by the DependancyResolver. If anyone has any suggestions I am open to alternatives but at the time this was the solution that worked for me.

    private IMyMetadataRepository metadataRepository;
    private IMyMetadataRepository MetadataRepository
    {
        get
        {
            if (metadataRepository == null || !metadataRepository.IsConnected)
                this.metadataRepository = (IMyMetadataRepository)DependencyResolver.Current.GetService(typeof(IMyMetadataRepository));
            return metadataRepository;
        }
    }

UPDATE: Apparently this questions keeps getting traffic so I thought I'd update my answer. As per the comments by others, MVC only queries the Dependency resolver once for an implementation of ModelMetadataProvider. And I ran into other problems with maintaining the lifetime of my database repository. I've since changed the code to be as follows and have not had any problems regarding this since.

public class RedSandMetadataProvider : DataAnnotationsModelMetadataProvider
{
    private IRepository<PseudoObjectStructure> StructureRepository
    {
        get
        {
            return (IRepository<IMyMetadataRepository>)DependencyResolver.Current.GetService(typeof(IRepository<IMyMetadataRepository>));
        }
    }

    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        ModelMetadata metadata = null;
        //logic for populating metadata here
        return metadata;
    }
}

I recommend you use new ModelMetadata() to setup your starting point and modify it as needed. Feel free to store the object you retrieve from the database to determine the MetaData in using metadata.AdditionalValues in case you want to use it in a ValidatorProvider as well.

Nick Albrecht
  • 16,607
  • 10
  • 66
  • 101
  • If you want to supply something (like a ModelMetadataProvider) from your container, you must not register it through any other mechanism (like ModelMetadataProviders.Current). – Robin Clowers Mar 30 '11 at 18:46
  • I know, I was only registering it in one place when I was working on this. I would comment it out from the other location each time I would feel up to the task of trying to troubleshoot it again. That wasn't my issue. – Nick Albrecht Mar 31 '11 at 17:27
  • Could you tell me which "lifetime" are you using for your custom ModelMetadataProvider. Singleton, PerRequest,...? – rudimenter Apr 03 '12 at 15:32
  • updated with my answer, though I'm open to suggestions if there are other ways I could have gone about it. – Nick Albrecht Apr 03 '12 at 17:47
  • I posted an issue on their project site (http://aspnet.codeplex.com/workitem/10156) about the incorrect lifecycle management. I'm providing the link here so others who have found this answer and the workaround can also watch for resolution on the issue in the framework itself. – CassOnMars May 09 '12 at 23:11
  • Awesome! Thanks for the heads up, I've gone over and voted for the issue as well. – Nick Albrecht May 10 '12 at 17:52