为什么叫吸气剂? [英] Why is getter being called?

查看:70
本文介绍了为什么叫吸气剂?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的模型中具有以下方法:

Have the method below in my model:

public bool ShowShipping() { return Modalities.Scheduled.Has(Item.Modality); }

但是以前它是这样的属性:

But previously it was a property like this:

public bool ShowShipping { get { return Modalities.Scheduled.Has(Item.Modality); } }

访问页面后,整个模型将填充包含Item属性的数据.项包含需要在视图上显示的数据,但没有需要回发的数据.因此,在回发(是的,post操作将模型作为参数)上,Item属性保留为空.

Upon accessing the page, the entire model is populated with data which includes the Item property. Item contains data that needs to be displayed on the view, but no data that needs to be posted back. So on post back (yes, the post action takes the model as a parameter) the Item property is left null.

这应该没问题,因为视图中只有一行代码可以访问ShowShipping.因此,我期望除非填充Item,否则它将永远不会被访问.但是在回发时,我遇到了一个错误,该错误发生在击中我的发布操作的第一行之前,并且在ShowShipping中显示了空引用错误.因此,我必须假设错误是在将表单数据序列化为模型的新实例时发生的……但是,当整个解决方案中唯一可访问它的位置是视图中的一行时,为什么会在序列化中调用此属性? ?

This should not be a problem because there is only one line of code that accesses ShowShipping, which is on the view. So I am expecting that it will never be accessed except when Item is populated. However on post back I get an error which occurs before the first line of my post action is hit and it shows a null reference error in ShowShipping. So I have to assume the error is happening as it serializes form data into a new instance of the model... but why would it call this property in serialization when the only place in the entire solution that accesses it is one line in the view?

推荐答案

在System.Web.Mvc 5.2.3.0版本中,DefaultModelBinder确实执行了验证,这可能违反了关注点分离,并且没有办法通过任何设置或配置将其完全关闭.其他SO帖子提到在Global.asax.cs中的以下代码行中关闭了值类型的隐式required属性,即Application_Start()方法...

In System.Web.Mvc version 5.2.3.0, the DefaultModelBinder does perform validation, arguably violating separation of concerns, and there isn't a way to shut it off entirely via any setting or configuration. Other SO posts mention turning off the implicit required attribute for value types with the following line of code in your Global.asax.cs, Application_Start() method...

DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

请参阅: https://stackoverflow.com/a/2224651 (该论坛引用的论坛直接来自ASP. .net团队).

See: https://stackoverflow.com/a/2224651 (which references a forum with an answer directly from the asp.net team).

但是,这还不够.由于DefaultModelBinder.BindProperty(...)方法中的代码,所有模型类的getter仍将执行.从源代码...

However, that is not enough. All model class getters are still executed because of the code inside the DefaultModelBinder.BindProperty(...) method. From the source code...

https://github.com/mono/aspnetwebstack/blob/master/src/System.Web.Mvc/DefaultModelBinder.cs

215  // call into the property's model binder
216  IModelBinder propertyBinder = Binders.GetBinder(propertyDescriptor.PropertyType);
217  object originalPropertyValue = propertyDescriptor.GetValue(bindingContext.Model);
218  ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
219  propertyMetadata.Model = originalPropertyValue;
220  ModelBindingContext innerBindingContext = new ModelBindingContext()
221  {
222      ModelMetadata = propertyMetadata,
223      ModelName = fullPropertyKey,
224      ModelState = bindingContext.ModelState,
225      ValueProvider = bindingContext.ValueProvider
226  };
227  object newPropertyValue = GetPropertyValue(controllerContext, innerBindingContext, propertyDescriptor, propertyBinder);

第217行是违规者.它会在从请求中设置值之前调用getter(此方法的最终目的),显然是这样,它可以将ModelBindingContext参数中的原始值传递给第227行的GetPropertyValue(...)方法.我找不到任何原因.

Line 217 is the offender. It calls the getter prior to setting the value from the request (the ultimate purpose of this method), apparently so that it can pass the original value in the ModelBindingContext parameter to the GetPropertyValue(...) method on line 227. I could not find any reason for this.

我在模型类中广泛使用了计算所得的属性,如果该属性表达式依赖于以前未设置的数据,则肯定会引发异常,因为这将表明代码中其他位置的错误. DefaultModelBinder行为破坏了设计.

I use calculated properties extensively in my model classes that certainly throw exceptions if the property expression relies on data that has not been previously set since that would indicate a bug elsewhere in the code. The DefaultModelBinder behavior spoils that design.

为解决我的问题,我编写了一个自定义模型联编程序,该联编程序重写了BindProperty(...)方法并删除了对getter的调用.该代码只是原始源代码的副本,减去217和219行.由于未使用模型验证,因此我也删除了243至259行,并且该代码引用了派生类无法访问的私有方法(另一个DefaultModelBinder.BindProperty(...)方法的问题设计).这是自定义模型活页夹.

To solve the problem in my case, I wrote a custom model binder that overrides the BindProperty(...) method and removes the call to the getters. This code is just a copy of the original source, minus lines 217 and 219. I also removed lines 243 through 259 since I am not using model validation, and that code references a private method to which the derived class does not have access (another problematic design of the DefaultModelBinder.BindProperty(...) method). Here is the custom model binder.

public class NoGetterModelBinder : DefaultModelBinder {

   protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) {

      string fullPropertyKey = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);
      if (!bindingContext.ValueProvider.ContainsPrefix(fullPropertyKey)) return;
      IModelBinder propertyBinder = Binders.GetBinder(propertyDescriptor.PropertyType);
      ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
      ModelBindingContext innerBindingContext = new ModelBindingContext() {

         ModelMetadata = propertyMetadata,
         ModelName = fullPropertyKey,
         ModelState = bindingContext.ModelState,
         ValueProvider = bindingContext.ValueProvider,

      };
      object newPropertyValue = GetPropertyValue(controllerContext, innerBindingContext, propertyDescriptor, propertyBinder);
      propertyMetadata.Model = newPropertyValue;
      ModelState modelState = bindingContext.ModelState[fullPropertyKey];
      if (modelState == null || modelState.Errors.Count == 0) {

         if (OnPropertyValidating(controllerContext, bindingContext, propertyDescriptor, newPropertyValue)) {

            SetProperty(controllerContext, bindingContext, propertyDescriptor, newPropertyValue);
            OnPropertyValidated(controllerContext, bindingContext, propertyDescriptor, newPropertyValue);

         }

      } else {

         SetProperty(controllerContext, bindingContext, propertyDescriptor, newPropertyValue);

      }

   }

}

您可以将该类放在您的Web项目中的任何位置,我只是将其放在Global.asax.cs中.然后,再次在Global.asax.cs的Application_Start()中,添加以下代码行,使其成为所有类的默认模型绑定程序...

You can place that class anywhere in your web project, I just put it in Global.asax.cs. Then, again in Global.asax.cs, in Application_Start(), add the following line of code to make it the default model binder for all classes...

ModelBinders.Binders.DefaultBinder = new NoGetterModelBinder();

这将防止在模型类上调用吸气剂.

This will prevent getters from being called on your model classes.

这篇关于为什么叫吸气剂?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆