为什么ASP.NET MVC在数据绑定期间关心我的只读属性?

时间:2010-02-06 02:59:33

标签: asp.net-mvc asp.net-mvc-2 modelbinders

编辑:添加赏金,因为我正在寻找除此之外的MVC3解决方案(如果存在):

DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;


我的“地址”模型'CityStateZip'上有一个只读属性。

这是从美国地址获取城市,州,邮政的便捷方式。如果该国家不是美国(调用者应首先检查),则会抛出异常。

    public string CityStateZip
    {
        get
        {
            if (IsUSA == false)
            {
                throw new ApplicationException("CityStateZip not valid for international addresses!");
            }

            return (City + ", " + StateCd + " " + ZipOrPostal).Trim().Trim(new char[] {','});
        }
    }

这是我模型的一部分,因此它受到约束。在ASP.NET MVC2 RC2之前,此字段在数据绑定期间从未引起过问题。我从来没有真正想过它 - 毕竟它只是只读。

现在虽然在2010年1月的RC2版本中它在数据绑定期间给出了一个错误 - 因为默认的模型绑定器似乎想要检查这个值(即使它是只读的)。

导致此错误的'base.OnModelUpdated'行。

public class AddressModelBinder : DefaultModelBinder
{
    protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        base.OnModelUpdated(controllerContext, bindingContext);

最后几分钟对模型绑定器的更改显然导致了行为的这种变化 - 但我还不确定它的重现是什么 - 或者这是否是一个错误?我正在将此传递给MVC团队,但好奇是否有人在此期间有任何建议我如何阻止此属性绑定。

这篇文章非常值得一读 - 但是根本没有提到readonly属性(不是我期望它)。问题(如果有的话)可能比这种情况更广泛 - 我只是不确定是否有任何重复 - 如果有的话!

Input Validation vs. Model Validation in ASP.NET MVC


根据@haacked的要求,这里是stacktrace:

我通过简单地将以下行添加到ANY模型并为相应的操作方法发布帖子来实现此目的。在这个例子中,我将它添加到我最简单的模型中。

 public string Foo { get { throw new Exception("bar"); } }
  

[TargetInvocationException:对象'Rolling_Razor_MVC.Models.ContactUsModel'上的属性访问者'Foo'引发了以下异常:'bar']      System.ComponentModel.ReflectPropertyDescriptor.GetValue(Object component)+390      System.Web.Mvc。<> c__DisplayClassb。< GetPropertyValueAccessor> b__a()+18      System.Web.Mvc.ModelMetadata.get_Model()+22      System.Web.Mvc.ModelMetadata.get_RealModelType()+29      System.Web.Mvc。< GetValidatorsImpl> d__0.MoveNext()+38      System.Linq。< SelectManyIterator> d__14`2.MoveNext()+273      System.Web.Mvc。< Validate> d__5.MoveNext()+644      System.Web.Mvc.DefaultModelBinder.OnModelUpdated(ControllerContext controllerContext,ModelBindingContext bindingContext)+92      System.Web.Mvc.DefaultModelBinder.BindComplexElementalModel(ControllerContext controllerContext,ModelBindingContext bindingContext,Object model)+60      System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext,ModelBindingContext bindingContext)+1048      System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext,ModelBindingContext bindingContext)+280      System.Web.Mvc.Controller.TryUpdateModel(TModel model,String prefix,String [] includeProperties,String [] excludeProperties,IValueProvider valueProvider)+449      System.Web.Mvc.Controller.TryUpdateModel(TModel model)+73

7 个答案:

答案 0 :(得分:17)

我相信我遇到了类似的问题。我发布了详细信息:

http://forums.asp.net/t/1523362.aspx


编辑:来自MVC团队的回复(来自以上网址):

我们对此进行了调查并得出结论认证系统的行为符合预期。由于模型验证涉及尝试对所有属性运行验证,并且由于非可空值类型属性具有隐式[Required]属性,因此我们将验证此属性并在此过程中调用其getter。我们知道这是对产品V1的重大改变,但有必要让新模型验证系统正常运行。

您可以通过几种方法解决此问题。其中任何一个都应该有效:

  • 将Date属性更改为方法而不是属性;这样,它将被MVC框架忽略。
  • 将属性类型更改为DateTime?而不是DateTime。这将从此属性中删除隐式[必需]。
  • 清除静态DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes标志。这将从应用程序范围内的所有非可空值类型属性中删除隐式[必需]。 我们正在考虑在产品的V3中添加一个属性,该属性将向我们发出“不绑定它,不验证它,只是假装这个属性不存在。”

再次感谢您的报告!

答案 1 :(得分:2)

MVC3仍然存在同样的问题。

我认为最好的方法就是在global.asax(来自SevenCentral的回答)中这样做:

 DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

这将禁用所有这些

答案 2 :(得分:1)

这对我来说就像是一个错误。我完全不明白为什么ModelBinder需要检查我的只读属性(可能有一些技术细节,但我绝对不理解,并且不愿意花时间去尝试)。

我在我的解决方案中添加了以下模型元数据提供程序以解决问题

protected override CachedDataAnnotationsModelMetadata CreateMetadataPrototype(IEnumerable<Attribute> attributes, Type containerType, Type modelType, string propertyName)
{
    var metadata = base.CreateMetadataPrototype(attributes, containerType, modelType, propertyName);

    if (metadata.IsReadOnly)
    {
        metadata.IsRequired = false;
    }

    return metadata;
}

protected override CachedDataAnnotationsModelMetadata CreateMetadataFromPrototype(CachedDataAnnotationsModelMetadata prototype, Func<object> modelAccessor)
{
    var metadata = base.CreateMetadataFromPrototype(prototype, modelAccessor);

    if (prototype.IsReadOnly)
    {
        metadata.IsRequired = false;
    }

    return metadata;
}

您还需要将以下内容添加到Global.asax.cs

protected void Application_Start()
{
    ModelMetadataProviders.Current = new RESModelMetadataProvider();
    ModelBinders.Binders.Add(typeof(SmartDate), new SmartDateModelBinder());

    ...
}

答案 3 :(得分:0)

当然我想我可以将CityStateZip转换为GetCityStateZip()但是我不能轻易地将它绑在像silverlight这样的东西上。这可能适用于遇到此问题的其他任何人的临时修复。

答案 4 :(得分:0)

我有完全相同的问题!!

有关我的问题的详情,请访问ASP.NET MVC 2.0 Unused Model Property being called when posting a product to the server?

这是否意味着我们需要对我们的属性进行编程,假设它们将被意外调用(在它依赖的属性被设置/初始化之前等等)...如果是这样,这代表了我们编程实践的变化我想知道如何继续。

同时,我只是有一个简单的'if'检查来解决这个问题。

答案 5 :(得分:0)

我遇到了类似的问题,当表单发回控制器时,我不希望验证的字段收到错误。经过一些谷歌搜索,我遇到了http://codeblog.shawson.co.uk/mvc-strongly-typed-view-returns-a-null-model-on-post-back/,其中指出名称冲突可能会导致问题。

虽然我认为我的post-back变量类没有冲突的属性名称,但重命名接收错误的属性解决了我的问题。

答案 6 :(得分:0)

相同的问题在MVC 5.2.3中仍然存在,并且甚至不是导致该问题的验证代码。 DefaultModelBinder会在其验证代码之外调用getter,即使它们是只读属性,即使没有将与这些属性匹配的请求数据传递到控制器也是如此。

在此处查看更详细的说明和我的完整解决方案:https://stackoverflow.com/a/54431404/10987278

我不确定该解决方案是否可以重构以与MVC 3一起使用,但是希望它可以帮助其他仍遇到同样问题的其他人,在我后来偶然发现此问题的MVC版本中。