Asp.net mvc 为什么ASP.NET MVC在数据绑定期间关心我的只读属性?
编辑:添加了赏金,因为我正在寻找一个MVC3解决方案(如果存在的话): DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes=falseAsp.net mvc 为什么ASP.NET MVC在数据绑定期间关心我的只读属性?,asp.net-mvc,asp.net-mvc-2,modelbinders,Asp.net Mvc,Asp.net Mvc 2,Modelbinders,编辑:添加了赏金,因为我正在寻找一个MVC3解决方案(如果存在的话): DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes=false 我的“Address”模型上有一个只读属性“CityStateZip” 这是从美国地址获取城市、州和邮政编码的便捷方式。如果国家/地区不是美国,则抛出异常(调用方应首先检查) 这是我模型的一部分,所以它会被绑定。在ASP.NET MVC2 RC2之前,此
我的“Address”模型上有一个只读属性
“CityStateZip”
这是从美国地址获取城市、州和邮政编码的便捷方式。如果国家/地区不是美国,则抛出异常(调用方应首先检查)
这是我模型的一部分,所以它会被绑定。在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团队,但我很好奇在此期间是否有其他人对我如何防止此属性绑定有任何建议
这篇文章非常值得一读,但是根本没有提到只读属性(我并不期望它)。问题(如果有)可能比这种情况更广泛——我只是不确定是否有任何再流通——如果有的话
根据@haacked的要求,以下是stacktrace: 我通过简单地向任何模型添加以下行并在相应的操作方法中发布帖子来实现这一点。在本例中,我将其添加到最简单的模型中
public string Foo { get { throw new Exception("bar"); } }
[TargetInvocationException:对象“Rolling\u Razor\u MVC.Models.ContactUsModel”上的属性访问器“Foo”引发了以下异常:“bar”]
System.ComponentModel.ReflectPropertyDescriptor.GetValue(对象组件)+390
System.Web.Mvc.c\u DisplayClassb.b\u a()+18
System.Web.Mvc.ModelMetadata.get_Model()+22
System.Web.Mvc.ModelMetadata.get_RealModelType()+29
System.Web.Mvc.d__0.MoveNext()+38
System.Linq.d_u14`2.MoveNext()+273
System.Web.Mvc.d__5.MoveNext()+644
System.Web.Mvc.DefaultModelBinder.OnModelUpdated(ControllerContext ControllerContext,ModelBindingContext bindingContext)+92
System.Web.Mvc.DefaultModelBinder.BindCompleteXelementalModel(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模型,字符串前缀,字符串[]includeProperties,字符串[]excludeProperties,IValueProvider valueProvider)+449
System.Web.Mvc.Controller.TryUpdateModel(TModel模型)+73
当然,我想我可以将
CityStateZip
转换为GetCityStateZip()
,但是我不能像silverlight那样轻松地绑定它。对于遇到此问题的任何其他人来说,这可能是一个临时解决方案。我相信我遇到了类似的问题。我已经发布了详细信息:
编辑:MVC团队的响应(来自上面的URL): 我们对此进行了调查,并得出结论,验证系统的行为符合预期。由于模型验证涉及尝试对所有属性运行验证,并且由于不可为null的值类型属性具有隐式[Required]属性,因此我们正在验证此属性并在过程中调用其getter。我们知道这是一个突破性的变化,从V1的产品,但有必要使新的模型验证系统正常运行 你有几个选择来解决这个问题。其中任何一项都应该有效:
- 将Date属性更改为方法而不是属性;这样MVC框架就会忽略它
- 是否将属性类型更改为DateTime?而不是日期时间。这将从此属性中删除隐式[Required]
- 清除静态DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes标志。这将从应用程序范围内的所有不可为null的值类型属性中删除隐式[Required]。 我们正在考虑在产品的V3中添加一个属性,该属性将向我们发出信号“不要绑定它,不要验证它,只是假装这个属性不存在。”
再次感谢您的报道 我也有同样的问题 有关我的问题的更多信息,请访问 这是否意味着我们需要在编程属性时假设它们将被意外调用(在设置/初始化它所依赖的属性之前等)。。。如果是这样的话,这代表着我们编程实践的改变,我想知道如何继续
同时,我只有一个简单的“如果”检查来解决问题。MVC3仍然存在同样的问题 我认为最好的方法就是在global.asax中实现这一点(来自SevenCentral的回答):
这将禁用所有字段我遇到了类似的问题,当表单发回控制器时,一个我不希望验证的字段接收到错误。在谷歌搜索之后,我发现有人指出名字冲突可能会导致问题
虽然我不认为回发变量类的属性名称有冲突,但通过重命名接收错误的属性解决了我的问题。这对我来说就像一个bug。我完全无法理解为什么ModelBinder需要检查我的只读属性(可能有
public string Foo { get { throw new Exception("bar"); } }
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
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;
}
protected void Application_Start()
{
ModelMetadataProviders.Current = new RESModelMetadataProvider();
ModelBinders.Binders.Add(typeof(SmartDate), new SmartDateModelBinder());
...
}