代码之家  ›  专栏  ›  技术社区  ›  Simon_Weaver

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

  •  20
  • Simon_Weaver  · 技术社区  · 15 年前

    编辑:添加了奖励,因为我正在寻找一个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);
    

    对ModelBinder的最后几分钟的更改显然导致了行为的这种变化——但我还不太确定它的再流通是什么——或者这是否是一个bug?我将这个消息传递给MVC团队,但是我很好奇在此期间是否还有其他人对我如何阻止这个属性的绑定有任何建议。

    这篇文章非常值得一读关于这些更改的内容——但根本没有提到只读属性(我不希望这样做)。这个问题(如果有)可能比这个情况更广泛——我只是不确定是否有任何再流通——如果有的话!

    Input Validation vs. Model Validation in ASP.NET MVC


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

    我只需将以下行添加到任何模型中,并将其发布到相应的操作方法中,就可以得到这个结果。在这个例子中,我将它添加到了我最简单的可能模型中。

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

    [targetInvocationException:对象“rolling-razor-mvc.models.contactusModel”上的属性访问器“foo”引发了以下异常:“bar”] System.ComponentModel.ReflectPropertyDescriptor.GetValue(对象组件)+390 system.web.mvc.<>c_uu DisplayClassB.<getPropertyValueAccessor>b_uu A()+18 system.web.mvc.modelmetadata.get_model()+22 system.web.mvc.modelmetadata.get_RealModelType()+29 system.web.mvc.<getvalidatorsimpl>d_uuu 0.moveNext()+38 system.linq.<selectManyIterator>d_uuu 14`2.moveNext()+273 system.web.mvc.<validate>d_uuu 5.moveNext()+644 system.web.mvc.defaultmodelbinder.onmodelupdated(controllerContext controllerContext,modelbindingContext bindingContext)+92 system.web.mvc.defaultModelBinder.bindComplexElementAlModel(ControllerContext ControllerContext,ModelBindingContext BindingContext,对象模型)+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模型,字符串前缀,字符串[]includeProperties,字符串[]excludeProperties,ivalueProvider值提供程序)+449 system.web.mvc.controller.tryupdatemodel(tmodel模型)+73

    7 回复  |  直到 6 年前
        1
  •  17
  •   Simon_Weaver    15 年前

    我想我也遇到了类似的问题。我已经发布了详细信息:

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


    编辑 :来自MVC团队的响应(来自上面的URL):

    我们对此进行了调查,并得出结论,验证系统的运行情况符合预期。由于模型验证涉及尝试对所有属性运行验证,并且由于不可为空的值类型属性具有隐式[必需]属性,因此我们正在验证此属性并在进程中调用其getter。我们知道这是一个突破性的变化,从v1的产品,但它是必要的,使新的模型验证系统运行正确。

    你有几个办法来解决这个问题。其中任何一个都应该有效:

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

    再次感谢你的报告!

        2
  •  2
  •   Simon_Weaver    13 年前

    MVC3仍然存在同样的问题。

    我认为最好的方法是在global.asax中实现这一点(来自Sevenentral的答案):

     DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
    

    这对所有人都是无效的

        3
  •  1
  •   cedd    10 年前

    这对我来说就像是一只虫子。我完全不明白为什么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());
    
        ...
    }
    
        4
  •  0
  •   Simon_Weaver    15 年前

    当然我想我可以转换 CityStateZip GetCityStateZip() 但是我不能很容易地将它绑定到类似Silverlight的东西中。对于遇到此问题的任何其他人来说,这可能是暂时的解决方法。

        5
  •  0
  •   Community kfsone    7 年前

    我也有同样的问题!!

    有关我的问题的更多信息,您可以访问 ASP.NET MVC 2.0 Unused Model Property being called when posting a product to the server?

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

    同时,我有一个简单的“如果”检查来解决这个问题。

        6
  •  0
  •   Zarepheth    12 年前

    我遇到了一个类似的问题,一个我不希望被验证的字段在表单发回控制器时接收到一个错误。在咕噜了几声之后,我遇到了 http://codeblog.shawson.co.uk/mvc-strongly-typed-view-returns-a-null-model-on-post-back/ 有人指出,名称冲突可能导致问题。

    虽然我不认为我的回发变量类有冲突的属性名,但是重命名接收错误的属性解决了我的问题。

        7
  •  0
  •   Dave Slife    6 年前

    同样的问题仍然存在于MVC 5.2.3中,甚至不是验证代码导致的问题。这个 DefaultModelBinder 在验证代码之外调用getter,即使它们是只读属性,甚至没有将请求数据传递给与这些属性匹配的控制器。

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

    我不确定该解决方案是否可以重构为与MVC 3一起工作,但希望它可以帮助其他人在MVC的较新版本中仍然遇到与我一样的问题。

    推荐文章