我为我的一个MVC模型创建了自定义TypeDescriptionProvider。我将它用于ValidationAttribute的动态分配。
我使用一个属性的值来决定向其他属性添加哪些属性。在使用DataAnnotationsValidationRunner的web服务中,验证工作正常。
跑步者来源:这里
internal static class DataAnnotationsValidationRunner
{
public static IEnumerable<ErrorInfo> GetErrors(object instance)
{
return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>()
from attribute in prop.Attributes.OfType<ValidationAttribute>()
where !attribute.IsValid(prop.GetValue(instance))
select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
}
}要获得属性值,我使用以下代码(在MyCustomTypeDescriptor中)
public override PropertyDescriptorCollection GetProperties()
{
var originalProperties = base.GetProperties();
var newProperties = new List<PropertyDescriptor>();
var myProperty = originalProperties.Find("CountryCodeID", false)
var myId = (int)countryProperty.GetValue(base.GetPropertyOwner(myProperty));
foreach (PropertyDescriptor pd in originalProperties)
{
AttributeCollection runtimeAttributes = pd.Attributes;
// add new attributes based on myId value
....
}
return new PropertyDescriptorCollection(newProperties.ToArray());
}当在MVC视图中使用这个描述符时,我会得到以下异常:
值不能为空。参数名称:主描述:在执行当前web请求期间发生了未处理的异常。请查看堆栈跟踪以获得有关错误的更多信息,以及它起源于代码的位置。 异常详细信息: System.ArgumentNullException: Value不能为空。参数名称:主参数
在TypeDescriptor中获得财产价值的正确方法是什么?我通过提供程序对模型类型而不是实例(例如global.asax)使用这个描述符。
编辑:我找到了解决办法。在GetTypeDescriptor的MyTypeDescriptorProvider方法中,我使用实例参数并将其传递给MyCustomTypeDescriptor的构造者。然而,MVC验证不起作用。我认为它自动使用这些动态数据(类似于上面提到的runner )。
编辑2:使用workaroud,我几乎总是看到实例null。因此,不可能在那里获得价值,并将其交给TypeDescriptor的运营者。
谢谢!
发布于 2013-07-22 13:01:27
最后,我能够在绑定期间使用customTypeDescriptor来生成客户端验证和验证模型所需的HTML标记。
第一个MyCustomTypeDescriptor.cs:
/// <summary>
/// CustomTypeDescriptor that provides validation in both MVC Web and WCF services.
/// </summary>
public class MyCustomTypeDescriptionProvider : TypeDescriptionProvider
{
public MyCustomTypeDescriptionProvider(TypeDescriptionProvider parent)
:base(parent)
{
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
return new MyCustomTypeDescriptor(base.GetTypeDescriptor(objectType, instance));
}
}
public class MyCustomTypeDescriptor : CustomTypeDescriptor
{
public MyCustomTypeDescriptor(ICustomTypeDescriptor parent)
: base(parent)
{ }
public override PropertyDescriptorCollection GetProperties()
{
var originalProperties = base.GetProperties();
if (this.IsRequired(originalProperties))
{
var newProperties = new List<PropertyDescriptor>();
foreach (PropertyDescriptor property in originalProperties)
{
var attrs = property.Attributes;
var newAttrs = new Attribute[attrs.Count + 1];
attrs.CopyTo(newAttrs, 0);
newAttrs[attrs.Count] = new RequiredAttribute();
newProperties.Add(TypeDescriptor.CreateProperty(property.ComponentType, property, newAttrs));
}
return new PropertyDescriptorCollection(newProperties.ToArray());
}
else
{
return originalProperties;
}
}
/// <summary>
/// IsRequired just simulates more complex validation rule (dependant on another value in model)
/// </summary>
/// <param name="originalProperties"></param>
/// <returns></returns>
private bool IsRequired(PropertyDescriptorCollection originalProperties)
{
if (originalProperties == null || originalProperties.Count == 0)
{
throw new ArgumentNullException();
}
var dependantProperty = originalProperties.Find("DependantValue", false);
if (dependantProperty == null)
{
throw new InvalidOperationException();
}
var value = (int)dependantProperty.GetValue(base.GetPropertyOwner(dependantProperty));
return value > 0;
}
}然后绑定这个描述符(每个实例!)我使用MyModelValidatorProvider:
/// <summary>
/// validator provider is used only for unobtrusive validation
/// </summary>
public class MyModelValidatorProvider : DataAnnotationsModelValidatorProvider
{
protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
{
var isPropertyValidation = metadata.ContainerType != null && !String.IsNullOrEmpty(metadata.PropertyName);
var model = context.Controller.ViewData.Model as TestCustomizedModel;
if (isPropertyValidation && model != null)
{
TypeDescriptor.AddProvider(new MyCustomTypeDescriptionProvider(TypeDescriptor.GetProvider(model)), model);
AttributeCollection newAttributes;
newAttributes = TypeDescriptor.GetProperties(model).Find(metadata.PropertyName, false).Attributes;
var attrArray = new Attribute[newAttributes.Count];
newAttributes.CopyTo(attrArray, 0);
attributes = attrArray;
}
return base.GetValidators(metadata, context, attributes);
}
}但是,这很好,但是在ModelBinding期间,没有设置ViewData,因此ValidatorProvider不会挂钩。作为一种解决方案,我使用了MyModelBinder:
/// <summary>
/// Model binder that attaches CustomTypeDescriptor and validates model.
/// </summary>
public class MyModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
base.OnModelUpdated(controllerContext, bindingContext);
TypeDescriptor.AddProvider(new MyCustomTypeDescriptionProvider(TypeDescriptor.GetProvider(bindingContext.Model)), bindingContext.Model);
var errors = DataAnnotationRunner.GetErrors(bindingContext.Model);
if (errors != null)
{
foreach (var error in errors)
{
bindingContext.ModelState.AddModelError(error.MemberNames.FirstOrDefault() ?? string.Empty, error.ErrorMessage);
}
}
}
}现在,我可以使用MyCustomTypeDescriptor和DataAnnotationRunner来验证所有MVC web、MVC除控制器以外的其他类、html帮助程序(非突出验证)以及其他项目(如WCF服务).
这一切都很好,不过感觉不太对劲。如果我能够以某种方式将MyCustomTypeDescriptor直接连接到MVC,那就太好了,然而,正如这个链接所宣称的,这似乎是不可能的。
我如何在ICustomTypeDescriptor MVC中提供自己的ASP.NET?
任何有助于使此解决方案更加优雅的改进都是值得欢迎的。谢谢。
https://stackoverflow.com/questions/16235142
复制相似问题