In response to michaelalm, the answer and request is what I ended up doing. I left the original answer, noted mainly out of politeness, as one of the solutions proposed by Nathan would work.
The result of this is the replacement of the DefaultModelBinder class, which you can either register globally (thereby allowing all types of models to take advantage of anti-aliasing) or selectively inherit for custom model bindings.
It all starts, predictably with:
/// <summary> /// Allows you to create aliases that can be used for model properties at /// model binding time (ie when data comes in from a request). /// /// The type needs to be using the DefaultModelBinderEx model binder in /// order for this to work. /// </summary> [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)] public class BindAliasAttribute : Attribute { public BindAliasAttribute(string alias) { //ommitted: parameter checking Alias = alias; } public string Alias { get; private set; } }
And then we get this class:
internal sealed class AliasedPropertyDescriptor : PropertyDescriptor { public PropertyDescriptor Inner { get; private set; } public AliasedPropertyDescriptor(string alias, PropertyDescriptor inner) : base(alias, null) { Inner = inner; } public override bool CanResetValue(object component) { return Inner.CanResetValue(component); } public override Type ComponentType { get { return Inner.ComponentType; } } public override object GetValue(object component) { return Inner.GetValue(component); } public override bool IsReadOnly { get { return Inner.IsReadOnly; } } public override Type PropertyType { get { return Inner.PropertyType; } } public override void ResetValue(object component) { Inner.ResetValue(component); } public override void SetValue(object component, object value) { Inner.SetValue(component, value); } public override bool ShouldSerializeValue(object component) { return Inner.ShouldSerializeValue(component); } }
This proxies the "native" PropertyDescriptor, which is usually found in DefaultModelBinder , but presents its name as an alias.
Next we have a new middleware class:
public class DefaultModelBinderEx : DefaultModelBinder { protected override System.ComponentModel.PropertyDescriptorCollection GetModelProperties(ControllerContext controllerContext, ModelBindingContext bindingContext) { var toReturn = base.GetModelProperties(controllerContext, bindingContext); List<PropertyDescriptor> additional = new List<PropertyDescriptor>();
And, then technically, that everything is there. Now you can register this default
DefaultModelBinderEx class using the solution sent as an answer in this SO:
Change the standard model binding in asp.net MVC , or you can use it as a base for your own connecting device.
Once you have selected your template for how you want the insert to be clicked, you simply apply it to the model type as follows:
public class TestModelType { [BindAlias("LPN")]
The reason I chose this code was because I wanted something that would work with custom type descriptors, as well as the ability to work with any type. Equally, I wanted the value provider system to be used when searching for model property values. So I changed the metadata that DefaultModelBinder sees when it starts to bind. This is a slightly longer approach, but conceptually it does exactly what you want at the metadata level.
One potentially interesting and slightly annoying side effect would be if the ValueProvider contains values ββfor more than one alias or alias and a property by its name. In this case, only one of the obtained values ββwill be used. It's hard to think of a way to merge them all with a safe type when you are only working with object . It seems, however, to supply a value in both the form column and the query line - and I'm not sure what exactly MVC does in this scenario, but I don't think he recommended the practice.
Another problem, of course, is that you should not create an alias equal to another alias, or really the name of the actual property.
I like to apply my model bindings, in general, using the CustomModelBinderAttribute class. The only problem with this might be if you need to get the type of the model and change its binding behavior - since CustomModelBinderAttribute inherited in the attribute lookup performed by MVC.
In my case, this is normal; I am developing a new site infrastructure and can push new extensibility into my base binders, using other mechanisms to satisfy these new types; but this does not apply to everyone.