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.
Aga开发者_StackOverflow中文版in, 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.
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.
精彩评论