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?
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);
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
}
}
Related
I'm using feature flags to selectively enable/disable certain aspects of my MVC4 web application in different environments. I have an interface named IConfiguration with a IsEnabled(FeatureFlag) method that provides access to these flags.
To this end, I want to disable certain MVC action methods when the feature they relate to is turned off. I have an attribute named FeatureAttribute defined like this:
// Usage: [Feature(FeatureFlag.I18N)]
public class FeatureAttribute : ActionMethodSelectorAttribute {
private IConfiguration _config;
private FeatureFlag _feature;
public FeatureAttribute(FeatureFlag feature) {
_config = DependencyResolver.Current.GetService<IConfiguration>();
_feature = feature;
}
public override bool IsValidForRequest(ControllerContext controllerContext,
MethodInfo methodInfo) {
return _config.IsEnabled(_feature);
}
}
This works, but using DependencyResolver to get an instance of IConfiguration smells funny. Is there any way I can redesign my code to avoid the Service Locator pattern?
I initially thought of using filter injection, but ActionMethodSelectorAttribute is not actually a filter, so it doesn't apply here.
Attributes are created by the .NET Framework. So you can't do Constructor injection. The only two ways to inject into Attributes is to do PropertyInjection or ServiceLocation.
PropertyInjection can be done like this:
Add a IPlanningStrategy that does the scanning with reflection for your attribute (or ActionMethodSelectorAttribute). Return immediatly if not a controller.
Foreach method that has this attribute add a IDirective that contains a reference to the MethodInfo
Add A IActivationStrategy that injects the attribute using kernel.Inject(attribute).
Best you have a look at the PropertyInjection implementation of Ninject it works exactly like this. It just needs some changes to do what you want.
i want to provide a method of changing the labels associated with controls which will be rendered on the page via a lookup to a sql table.
Ideally i want to inject the meta data display field which is then rendered on the page using the helper.
#Html.LabelFor(m => m.city)
Since this would need to be a sql lookup at runtime, i cannot just change the class scaffolding tt template to stamp on a displayname annotation at design time.
I thought of 3 potential methods.
rewrite all the html.helpers i want to use. Problem with this is you would need to replicate all the functionality of the existing helper prior to making the changes.
write a custom data annotation and stamp it on each property in the class i.e.
[MyCustomNameAttribute]
public string city{ get; set; }
then hopefully in the MyCustomNameAttribute class i can find both the linq field i am referring to, a reference to the metadatamodel and a database context using these i can retrieve and replace the DisplayName based on potential name customisations configured by the User. I tried to do this but was unable to find out how the [Display(Name="city")] annotation works in the background.
Modify the entity model backing code to inject the name into the metadatamodel.
Does anyone have any experience of the above?
Cheers
Tim
You have to create a custom ModelMetaDataProvider by extending the DataAnnotationsModelMetadataProvider.
public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(
IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor,
Type modelType,
string propertyName)
{
var meta = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
if(meta.DisplayName == null)
{
// TO DO read the display value from database and assign here
meta.DisplayName = ..
}
return meta;
}
}
Then you have to set that in the Global.asax.cs
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ModelMetadataProviders.Current = new CustomModelMetadataProvider();
}
I'm assuming that you want to set only the display name from db but suppose you want to load the complete modelmetadata from db then I would suggest you to create a custom ModelMetadataProvider by implementing the abstract class ModelMetadataProvider.
Hitting the database every time definitely not a good idea so we have to workout for some caching strategy.
We have to hit the database for every new containerType (I guess) and read the metadata information for the container along with all its properties and store in the cache with key as the containerType (this could be a difficult job).
I have a search form that can search in different provider.
I started out by having a base controller
public SearchController : Controller
{
protected readonly ISearchService _searchService
public SearchController(ISearchService searchService)
{
_searchService= searchService;
}
public ActionResult Search(...)
{
// Use searchService to query and return a view.
}
}
And child controllers
TwitterController : SearchController
{
...
}
NewsController : SearchController
{
...
}
I use StructureMap to insert all my dependencies in the controller. With this setup, I was able to change the SearchService depending on the type of the controller being instanciated.
x.For<ISearchService>().ConditionallyUse(o =>
{
o.TheDefault.Is.OfConcreteType<NewsSearchService>();
o.If(c => c.ParentType == typeof(TwitterController))
.ThenIt.Is.OfConcreteType<TwitterSearchService>();
...
});
That even allowed me to set different Views for each controller, (just putting the corresponding folder (Twitter, News...) and the Parent controller is still handling all the Search, with a simple
return View(results)
which is displaying the correct view specific to twitter, news, or other
Now that was cool and looked great, I a single form and the different views are displayed in tabs on the same page. That's where it starts to get complicated with this approach. The form has to post to /Twitter to search in twitter, to /News to search in news... which means I should change the action parameter of the form depending on which tab I am and display the correct tab on when the form returns depending on.. the url? craziness follows.
If you have built something like this already or know what's the best approach to this, please advices are welcome.
Now I think I would have less pain using a parameter in the form and posting to a single controller. I am thinking of injecting the correct SearchService depending on this parameter. What would be the best approach? I thought of using a model binder,
So I would have my ActionMethod that look like this:
public ActionResult Search(ISearchService service, Query query)
{
var results = service.Find(query);
}
But I think would need to make a call like this in the ModelBinder
ObjectFactory.GetInstance(...);
Based on the querystring parameter that describe which provider to use, and that doesn't seem more elegant to me. I feel stuck, help :(.
Whenever you need to vary a dependency based on a run-time value, Abstract Factory is the general solution.
Instead of injecting ISearchService into your Controllers, inject an ISearchServiceFactory:
public SearchController : Controller
{
private readonly ISearchServiceFactory searchServiceFactory;
public SearchController(ISearchServiceFactory searchServiceFactory)
{
if (searchServiceFactory == null)
{
throw new ArgumentNullException("searchServiceFactory");
}
this.searchServiceFactory = searchServiceFactory;
}
public ActionResult Search(...)
{
// Use searchServiceFactory to create an ISearchService based on
// run-time values, and use it to query and return a view.
}
}
It is not entirely clear to me which run-time value you need to vary on, but assuming that it's the Query, ISearchServiceFactory might be defined like this:
public interface ISearchServiceFactory
{
ISearchService Create(Query query);
}
I was trying to figure out how to use the abstract factory pattern and still let structuremap resolve all the dependencies of my components.
I believe that is the way I am going to implement it, but I submit this here to get some feedback if someone would read this.
As explain in the previous answer, I do not want to build the whole object graph depending on which provider I need in the Abstract factory.
ie :
class StatServiceFactory : IStatServiceFactory
{
public IStatService Create(string provider)
{
switch(provider)
{
case "blog":
return new StatService(IFacetRepository,ISearchManager,IConfigManager,BooleanQueryBuilder);
//How to resolve the Config, the SearchManager, and BooleanQueryBuilder?
//Add more abstract factories? It starts to get messy in my opinion...
}
}
}
What I can do is have the abstract factory use my container to create an instance of my search managers depending on a parameter (coming from the querystring in my case)
Structuremap allows to create named instances this way :
x.For<ISearchManager>().Use<AbcSearchManager>().Named("Abc");
x.For<ISearchManager>().Use<DefSearchManager>().Named("Def");
I need a way to inject the container in my Abstract factory.
I would probably wrap the container in a wrapper defined like this. That would keep me from leaking Structuremap into my project. I dont need more that those 2 features within the abstract factory anyway, but it is not necessary:
public interface IContainerWrapper
{
object GetInstance<T>();
object GetNamedInstance<T>(string key);
}
and the implementation :
public class ContainerImpl : IContainerWrapper
{
private readonly Container _container
public ContainerImpl(Container container)
{
_container = container;
}
...
}
And setup StructureMap to resolve dependencies to my abstract factory like that :
x.For<IContainer>.Use(new ContainerImpl(this));
x.For<IFactory>.Use<Factory>()
My factory would be then much simpler and would create my instance like that :
public class SearchmanagerFactory
{
private readonly IContainerWrapper _container;
public SearchmanagerFactory(IContainerProvider containerProvider)
{
_container = containerProvider;
}
public ISearchManager Create(string provider)
{
//eed to handle the bad input for provider.
return (ISearchManager)
_container.Resolve<ISearchManager>(provider);
}
}
That seems pretty clean this way :).
Thoughts?
This is more an extensive comment than an answer to explain why an AbstractFactory seems complicated. Here is something that looks closer from the reality:
class StatServiceFactory : IStatServiceFactory
{
public IStatService Create(string provider)
{
switch(provider)
{
case "blog":
return new StatService(IFacetRepository,ISearchManager,IConfigManager,BooleanQueryBuilder);
//How to resolve the Config, the SearchManager, and BooleanQueryBuilder?
//Add more abstract factories? It starts to get messy in my opinion...
}
}
}
The FacetRepository is the same for any provider, but the SearchManager changes, the ConfigManager changes, and the BooleanQueryBuilder is an abstract class with different implementation for different provider (because every API doesnt use the same keyword for their queries) All those dependencies are currently resolved by structuremap, based on the type of the controller.
I would really like to keep the benefit of StructureMap here, rather than using factories all the way, for each different pieces.'
Please see my edit at the end of my question for another suggestion to my problem.
Suppose I want to allow to select our entity (from a dropdown, etc) on a page, let's say Product. As a result I may receive this:
public ActionResult SelectedAction(Guid productId)
{
}
But, I want to use model binders power, so instead I write model binder to get my product from repository and instead use
public ActionResult SelectedAction(Product product)
{
if (ModelState.IsValid) {} else {}
}
My model binder will set model state to false if product is invalid.
Now, there're problems with this approach:
It's not always easy to use strongly-typed methods like Html.ActionLink(c => c.SelectedAction(id)) since we need to pass Product, not id.
It's not good to use entities as controller parameters, anyway.
If model state is invalid, and I want to redirect back and show error, I can't preserve selected product! Because bound product is not set and my id is not there. I'd like to do RedirectToAction(c => c.Redisplay(product)) but of course this is not possible.
Now, seems like I'm back to use "Guid productId" as parameter... However, there's one solution that I'd like to present and discuss.
public class EntityViewModel<T> where T : BaseEntity
{
public EntityViewModel(Guid id)
{
this.Id = id;
}
public static implicit operator EntityViewModel<T>(T entity)
{
return new EntityViewModel<T>(entity.Id);
}
public override string ToString()
{
return Id.ToString();
}
public Guid Id { get; set; }
public T Instance { get; set; }
}
Now, if I use
public ActionResult SelectedAction(EntityViewModel<Product> product)
{
if (ModelState.IsValid) {} else {}
}
all the problems are solved:
I can pass EntityViewModel with only Id set if I have only Id.
I don't use entity as parameter. Moreover, I
can use EntityViewModel as property inside another ViewModel.
I can pass EntityViewModel back to RedirectToController and it will keep its Id value, which will be
redisplayed to user along with the validation messages (thanks to MVCContrib and ModelStateToTempData / PassParametersDuringRedirect).
The model binder will get Instance from the repository and will set model state errors like "Not found in database" and so on. And I can use things like ActionLink(c => c.Action(Model.MyProductViewModelProperty)).
The question is, are there any drawbacks here? I can't see anything bad but I'm still new to MVC and may miss some important things. Maybe there're better and approved ways? Maybe this is why everybody uses entity IDs as input parameters and properties?
Overall that looks like a good appoach to me...
As an alternative, you could use POCO for your viewmodel then I think all 3 problems would be solved automatically. Have you seen the Automapper project that allows an Entity to DTO approach? This would give you more flexibility by separating you ViewModel from your EntityModel, but really depends on the complexity of you application you are building.
MVC's ViewDataExtensions might also be useful instead of creating custom containers to hold various viewmodel objects as you mention in number 2.
MVCContrib's ModelStateToTempData should work for any serializable object (must be serializable for any out of process sessionstate providers eg. SQL, Velocity etc.), so you could use that even without wrapping your entity classes couldn't you?
Here's a little background on my solution:
ASP.Net MVC app
Using Linq-to-SQL with table-per-hierarchy inheritance
Using DataAnnotationsModelBinder as default
So I have a Device abstract class and then a series of derived classes (ServerDevice, DiskDevice, PSUDevice, etc) that inherit from it in the proscribed Linq-to-SQL way. I have one controller that handles all these different related model types, and it renders different partials based on the type and a handy drop down to select them. My (GET) Create method looks like this:
// GET: /Devices/Create/3
public ActionResult Create(int? deviceTypeID)
{
return View(DeviceFactory(deviceTypeID);
}
Where DeviceFactory is a static method returns a new instance of one of the derived classes based on an int discriminator. The POST Create method looks like this:
// POST: /Devices/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([ModelBinder(typeof(DeviceModelBinder))]Device device)
{
if (!ModelState.IsValid)
return View(device);
_repository.Add(device);
_repository.Save();
TempData["message"] = string.Format("Device was created successfully.");
return RedirectToAction(Actions.Index);
}
and my custom model binder looks like this:
public class DeviceModelBinder : DataAnnotationsModelBinder
{
private readonly Dictionary<string, Type> _deviceTypes =
new Dictionary<string, Type>
{
{"1", typeof (ServerDevice)},
{"2", typeof (DiskDevice)}
// And on and on for each derived type
};
protected override object CreateModel(ControllerContext controllerContext,
ModelBindingContext bindingContext, Type modelType)
{
return base.CreateModel(controllerContext, bindingContext,
_deviceTypes[bindingContext.ValueProvider["deviceTypeID"].AttemptedValue]);
}
}
So after trying to hook this up all day, reading about ActionInvoker, custom ActionFilters, and all sorts of other MVC stuff, I'm wondering if the solution I've arrived at is a good one. Help allay my fears that I'm missing some hugely obvious concept and re-inventing the wheel. Is there a better or more concise way?
Thanks!
My POV is it's "smelly" to bind entity/domain types to a UI at all. I explained this in considerably more detail in this answer. IMHO you should nearly always use dedicated presentation models. It is a nice side benefit that model binding do a presentation model is considerably easier, but the more important benefits are discussed in the linked answer.