首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用MVC验证的CustomTypeDescriptor --如何使用property.GetValue(组件)获得属性值?

使用MVC验证的CustomTypeDescriptor --如何使用property.GetValue(组件)获得属性值?
EN

Stack Overflow用户
提问于 2013-04-26 11:17:44
回答 1查看 1.4K关注 0票数 2

我为我的一个MVC模型创建了自定义TypeDescriptionProvider。我将它用于ValidationAttribute的动态分配。

我使用一个属性的值来决定向其他属性添加哪些属性。在使用DataAnnotationsValidationRunner的web服务中,验证工作正常。

跑步者来源:这里

代码语言:javascript
复制
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中)

代码语言:javascript
复制
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的运营者。

谢谢!

EN

回答 1

Stack Overflow用户

发布于 2013-07-22 13:01:27

最后,我能够在绑定期间使用customTypeDescriptor来生成客户端验证和验证模型所需的HTML标记。

第一个MyCustomTypeDescriptor.cs:

代码语言:javascript
复制
/// <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:

代码语言:javascript
复制
/// <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:

代码语言:javascript
复制
/// <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?

任何有助于使此解决方案更加优雅的改进都是值得欢迎的。谢谢。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16235142

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档