作为对michaelalm答案和请求的回应,这是我最终所做的。我保留了原始答案,主要是出于礼貌,因为Nathan建议的其中一种解决方案可以工作。
此输出是一个替换DefaultModelBinder类的结果,您可以全局注册它(从而允许所有模型类型受益于别名),或者选择性地继承它以用于自定义模型绑定器。
一切都始于可预测的:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class BindAliasAttribute : Attribute
{
public BindAliasAttribute(string alias)
{
Alias = alias;
}
public string Alias { get; private set; }
}
然后我们得到了这个类:
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);
}
}
这个代理了一个“正式”的PropertyDescriptor,通常由DefaultModelBinder找到,但它将其名称呈现为别名。
接下来是新的模型绑定器类:
已更新,采用@jsabrooke的建议以下
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>();
foreach (var p in
this.GetTypeDescriptor(controllerContext, bindingContext)
.GetProperties().Cast<PropertyDescriptor>())
{
foreach (var attr in p.Attributes.OfType<BindAliasAttribute>())
{
additional.Add(new AliasedPropertyDescriptor(attr.Alias, p));
if (bindingContext.PropertyMetadata.ContainsKey(p.Name)
&& !string.Equals(p.Name, attr.Alias, StringComparison.OrdinalIgnoreCase)))
{
bindingContext.PropertyMetadata.Add(
attr.Alias,
bindingContext.PropertyMetadata[p.Name]);
}
}
}
return new PropertyDescriptorCollection
(toReturn.Cast<PropertyDescriptor>().Concat(additional).ToArray());
}
}
然后,从技术上讲,就是这样了。您现在可以使用此SO中发布的解决方案将DefaultModelBinderEx
类注册为默认值:更改asp.net MVC中的默认模型绑定程序,或者您可以将其用作自己模型绑定程序的基础。
选择绑定程序启动方式后,只需将其应用于模型类型,如下所示:
public class TestModelType
{
[BindAlias("LPN")]
[BindAlias("L")]
public string LongPropertyName { get; set; }
}
我选择这段代码的原因是因为我想要一个既可以与自定义类型描述符一起工作,又可以与任何类型一起工作的东西。同样,我希望仍然使用值提供程序系统来获取模型属性值。所以我改变了
DefaultModelBinder
在开始绑定时看到的元数据。这是一个稍微冗长一些的方法 - 但从概念上讲,它在元数据级别上正好做到了你想要的。
一个可能有趣但有点烦人的副作用是,如果ValueProvider
包含多个别名或别名和属性名称的值,那么只会使用检索到的值中的一个。不过只是使用object
进行操作时很难想出一种类型安全合并它们所有的方式。不过这类似于在表单提交和查询字符串中都提供值的情况 - 我不确定MVC在这种情况下会做什么,但我认为这并不是推荐做法。
另一个问题是,当然不能创建一个等于另一个别名或实际属性名称的别名。
通常情况下,我喜欢使用CustomModelBinderAttribute
类来应用我的模型绑定器。唯一的问题是,如果您需要从模型类型派生并更改其绑定行为,则可能会出现问题 - 因为CustomModelBinderAttribute
在MVC执行的属性搜索中是继承的。
在我的情况下,这还好,我正在开发一个新的网站框架,并能够使用其他机制将新的可扩展性推入我的基础绑定器以满足这些新类型;但对于每个人来说都不是这样的情况。