【问题标题】:ASP.NET MVC 2 Beta - Default Model BinderASP.NET MVC 2 Beta - 默认模型绑定器
【发布时间】:2009-11-19 03:45:33
【问题描述】:

从 ASP.NET MVC 1.0 切换到 ASP.NET MVC 2 Beta 后,我遇到了一些不同的行为。我检查了重大更改,但不清楚问题出在哪里。

问题与默认模型绑定器和实现 IDataErrorInfo 的模型有关。

属性(IDataErrorInfo.Item):

public string this[string columnName]

不再为每个属性调用。我错过了什么?

【问题讨论】:

标签: c# .net asp.net asp.net-mvc asp.net-mvc-2


【解决方案1】:

MVC 1.0 中的 DefaultModelBinder:

protected virtual void OnPropertyValidated(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
{
    IDataErrorInfo model = bindingContext.Model as IDataErrorInfo;
    if (model != null)
    {
        string str = model[propertyDescriptor.Name];
        if (!string.IsNullOrEmpty(str))
        {
            string key = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);
            bindingContext.ModelState.AddModelError(key, str);
        }
    }
}

MVC 2.0 beta 中的 DefaultModelBinder:

protected virtual void OnPropertyValidated(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
{
    ModelMetadata metadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
    metadata.Model = value;
    string prefix = CreateSubPropertyName(bindingContext.ModelName, metadata.PropertyName);
    foreach (ModelValidator validator in metadata.GetValidators(controllerContext))
    {
        foreach (ModelValidationResult result in validator.Validate(bindingContext.Model))
        {
            bindingContext.ModelState.AddModelError(CreateSubPropertyName(prefix, result.MemberName), result.Message);
        }
    }
    if ((bindingContext.ModelState.IsValidField(prefix) && (value == null)) && !TypeHelpers.TypeAllowsNullValue(propertyDescriptor.PropertyType))
    {
        bindingContext.ModelState.AddModelError(prefix, GetValueRequiredResource(controllerContext));
    }
}

它不使用 IDataErrorInfo this[string columnName] 属性...看起来像一个错误,因为 DefaultModelBinder 仍然使用 Error 属性。至少是不一致。

编辑

我用了reflector,发现DataErrorInfoPropertyModelValidator好像没有用,于是我自己创建了一个类:

public class DataErrorInfoModelPropertyValidatorProvider : ModelValidatorProvider
{
    // Methods
    public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
    {
        if (metadata == null)
        {
            throw new ArgumentNullException("metadata");
        }
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        var validators = new List<ModelValidator>();
        validators.Add(new DataErrorInfoPropertyModelValidator(metadata, context));
        return validators;
    }

    internal sealed class DataErrorInfoPropertyModelValidator : ModelValidator
    {
        // Methods
        public DataErrorInfoPropertyModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
            : base(metadata, controllerContext)
        {
        }

        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            if (container != null)
            {
                IDataErrorInfo info = container as IDataErrorInfo;
                if (info != null)
                {
                    string str = info[Metadata.PropertyName];
                    if (!string.IsNullOrEmpty(str))
                    {
                        ModelValidationResult[] resultArray = new ModelValidationResult[1];
                        ModelValidationResult result = new ModelValidationResult();
                        result.Message = str;
                        resultArray[0] = result;
                        return resultArray;
                    }
                }
            }
            return Enumerable.Empty<ModelValidationResult>();
        }
    }
}

然后我用了:

ModelValidatorProviders.Providers.Add(new DataErrorInfoModelPropertyValidatorProvider());

它有效:) 这只是临时解决方案。必须在最终的 MVC 2 中更正。

编辑

我还在DataErrorInfoPropertyModelValidatorValidate() 方法中将if (base.Metadata.Model != null) 更改为if (container != null)。

【讨论】:

  • 对 metaData.GetValidators 的调用应该产生一个 DataErrorInfoPropertyModelValidator。调用其 Validate 应导致直接调用 IDataErrorInfo 接口。它似乎并没有那么远。
【解决方案2】:

经过一些进一步的调试工作,我相信我明白为什么在我的特殊情况下没有调用IDataErrorInfo.Item。以下代码在 ASP.NET MVC 2 Beta 中用于验证 IDataErrorInfo 属性:

internal sealed class DataErrorInfoPropertyModelValidator : ModelValidator
{
    public DataErrorInfoPropertyModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
        : base(metadata, controllerContext)
    {
    }

    public override IEnumerable<ModelValidationResult> Validate(object container)
    {
        if (Metadata.Model != null)
        {
            var castContainer = container as IDataErrorInfo;
            if (castContainer != null)
            {
                string errorMessage = castContainer[Metadata.PropertyName];
                if (!String.IsNullOrEmpty(errorMessage))
                {
                    return new[] { new ModelValidationResult { Message = errorMessage } };
                }
            }
        }
        return Enumerable.Empty<ModelValidationResult>();
    }
}

我的模型包含一个属性 System.Nullable&lt;int&gt;,当模型绑定器从 HTML 帖子中绑定一个空字符串时,Metadata.Model 等于 null,因此验证不会运行。

这与 ASP.NET MVC 1.0 有着根本的不同,在 ASP.NET MVC 1.0 中,此方案一直触发验证器直到调用 IDataErrorInfo.Item

我只是没有按照预期的方式使用某些东西吗?

【讨论】:

  • 感谢您报告此事。我与其他一些 MVC 开发人员进行了交谈,我们怀疑 null 检查在 Preview 2 和 Beta 之间的某个时候起到了作用,但后来变得不必要了。我提交了一个错误以删除支票。
  • 感谢您对此进行调查!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-30
  • 1970-01-01
相关资源
最近更新 更多