这是我在考虑了其他可能的解决方案后提出的解决方案。它基于找到以下文章...
http://jendaperl.blogspot.co.uk/2010/10/attributeproviderattribute-rendered.html
这篇文章的底部说明了我这样做的原因。
拥有一个现有的数据模型类...
public class UserAccount
{
[Display(Name = "Username", Prompt = "Login As"), Required()]
string UserName { get; set; }
[Display(Name = "Password", Prompt = "Password"), Required(), DataType(DataType.Password), StringLength(255, MinimumLength = 7, ErrorMessage = "The password must be at least 7 characters")]
string Password { get; set; }
}
如果我们可以将属性属性复制到使用它的视图中,这不是很好吗,如果需要的话可以覆盖它们,这样使用这个类的视图就不需要在一个简单的属性上重新审视改变。这是我的解决方案。如下创建一个新属性...
using System.ComponentModel;
namespace MyApp.ViewModels
{
public class AttributesFromAttribute : AttributeProviderAttribute
{
public AttributesFromAttribute(Type type, string property)
: base(type.AssemblyQualifiedName, property)
{
}
public T GetInheritedAttributeOfType<T>() where T : System.Attribute
{
Dictionary<string,object> attrs = Type.GetType(this.TypeName).GetProperty(this.PropertyName).GetCustomAttributes(true).ToDictionary(a => a.GetType().Name, a => a);
return attrs.Values.OfType<T>().FirstOrDefault();
}
}
}
现在您只需将以下内容添加到视图模型类中的相关属性...
[AttributesFrom(typeof(MyApp.DataModel.UserAccount), "UserName")]
例如...
public class RegisterViewModel
{
public UserAccount UserAccount { get; set; }
public RegisterViewModel()
{
UserAccount = new UserAccount();
}
[AttributesFrom(typeof(MyApp.DataModel.UserAccount), "UserName")]
string UserName { get; set; }
[AttributesFrom(typeof(MyApp.DataModel.UserAccount), "Password")]
string Password { get; set; }
[AttributesFrom(typeof(MyApp.DataModel.UserAccount), "Password")]
[Display(Name = "Confirm Password", Prompt = "Confirm Password"), Compare("Password", ErrorMessage = "Your confirmation doesn't match.")]
public string PasswordConfirmation { get; set; }
}
然后复制可以被覆盖的属性(如上面的 PasswordConfirmation),允许在同一个视图模型中使用多个数据模型,如果您需要从代码访问继承的属性,您可以使用 GetInheritedAttributeOfType 方法来实现。比如……
public static class AttrHelper
{
public static T GetAttributeOfType<T>(this ViewDataDictionary viewData) where T : System.Attribute
{
var metadata = viewData.ModelMetadata;
var prop = metadata.ContainerType.GetProperty(metadata.PropertyName);
var attrs = prop.GetCustomAttributes(false);
// Try and get the attribute directly from the property.
T ret = attrs.OfType<T>().FirstOrDefault();
// If there isn't one, look at inherited attribute info if there is any.
if(ret == default(T))
{
AttributesFromAttribute inheritedAttributes = attrs.OfType<AttributesFromAttribute>().FirstOrDefault();
if (inheritedAttributes != null)
{
ret = inheritedAttributes.GetInheritedAttributeOfType<T>();
}
}
// return what we've found.
return ret;
}
}
这可以从例如编辑器模板中调用...
var dataTypeAttr = AttrHelper.GetAttributeOfType<DataTypeAttribute>(ViewData);
它将首先直接查看视图模型的属性,但如果没有找到,它将查看继承的属性并调用 GetInheritedAttributeOfType。
这对我来说效果最好,因为...
我觉得当前在视图模型和数据模型中重复 DataAnnotations 的做法不利于可维护性或重用性。
使用 MetadataType 也是不灵活的,要么全有,要么全无,你不能在单个 ViewModel 上包含多个 MetadataType 属性。
缺乏将数据模型封装在没有属性的视图模型中,因为它也没有灵活性。您必须包含整个封装对象,因此无法将 DataModel 填充到多个视图中。