我使用下面的方法自动将数据注释从我的实体复制到我的视图模型。这确保了实体/视图模型的 StringLength 和 Required 值总是相同的。
它使用 Automapper 配置工作,因此只要 AutoMapper 设置正确,如果视图模型上的属性名称不同,它就可以工作。
您需要创建自定义 ModelValidatorProvider 和自定义 ModelMetadataProvider 才能使其正常工作。我对为什么有点模糊的记忆,但我相信服务器端和客户端验证都是如此,以及您基于元数据执行的任何其他格式(例如,必填字段旁边的星号)。
注意:我已经稍微简化了我的代码,因为我在下面添加了它,所以可能会有一些小问题。
元数据提供者
public class MetadataProvider : DataAnnotationsModelMetadataProvider
{
private IConfigurationProvider _mapper;
public MetadataProvider(IConfigurationProvider mapper)
{
_mapper = mapper;
}
protected override System.Web.Mvc.ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
//Grab attributes from the entity columns and copy them to the view model
var mappedAttributes = _mapper.GetMappedAttributes(containerType, propertyName, attributes);
return base.CreateMetadata(mappedAttributes, containerType, modelAccessor, modelType, propertyName);
}
}
验证者提供者
public class ValidatorProvider : DataAnnotationsModelValidatorProvider
{
private IConfigurationProvider _mapper;
public ValidatorProvider(IConfigurationProvider mapper)
{
_mapper = mapper;
}
protected override System.Collections.Generic.IEnumerable<ModelValidator> GetValidators(System.Web.Mvc.ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
{
var mappedAttributes = _mapper.GetMappedAttributes(metadata.ContainerType, metadata.PropertyName, attributes);
return base.GetValidators(metadata, context, mappedAttributes);
}
}
上述 2 个类中引用的辅助方法
public static IEnumerable<Attribute> GetMappedAttributes(this IConfigurationProvider mapper, Type sourceType, string propertyName, IEnumerable<Attribute> existingAttributes)
{
if (sourceType != null)
{
foreach (var typeMap in mapper.GetAllTypeMaps().Where(i => i.SourceType == sourceType))
{
foreach (var propertyMap in typeMap.GetPropertyMaps())
{
if (propertyMap.IsIgnored() || propertyMap.SourceMember == null)
continue;
if (propertyMap.SourceMember.Name == propertyName)
{
foreach (ValidationAttribute attribute in propertyMap.DestinationProperty.GetCustomAttributes(typeof(ValidationAttribute), true))
{
if (!existingAttributes.Any(i => i.GetType() == attribute.GetType()))
yield return attribute;
}
}
}
}
}
if (existingAttributes != null)
{
foreach (var attribute in existingAttributes)
{
yield return attribute;
}
}
}
其他注意事项
- 如果您使用依赖注入,请确保您的容器尚未替换内置的元数据提供程序或验证程序提供程序。在我的例子中,我使用的是 Ninject.MVC3 包,它在创建内核后绑定了其中一个,然后我不得不在之后重新绑定它,以便实际使用我的类。我得到了关于只允许添加一次Required的例外情况,花了一天的大部分时间来追踪它。