【问题标题】:Model Binder in ASP.NET webformsASP.NET 网络表单中的模型绑定器
【发布时间】:2010-09-09 20:43:22
【问题描述】:

多年来,我一直从事 ASP.NET Web 表单开发,但我被一个专有库宠坏了,它允许我做以下事情:

    UpdateToObject(ControlsCollection, obj)
    UpdateFromObject(ControlsCollection, obj)

从概念上讲,代码所做的事情与 MVC Model Binder 所做的非常相似,即给定表单的发布值作为输入,它将填充自定义对象。基本上,它使开发人员免于编写诸如

之类的猴子代码
employee.Name = txtName.Text;
employee.DOB = DateTime.Parse(txtDOB.Text);

等等..

现在,这个专有库在我参与的新项目中不可用,它是一个 Web 表单项目。所以我想知道是否有办法在 Web 表单的上下文中使用System.Web.Mvc.DefaultModelBinder。目标是实现从域对象和返回的简单和容易的控件填充,理想情况下考虑到验证注释。 如果这是不可能的,有人可以向我指出一个开源解决方案来解决这个需求。我真的不想重写这样的代码。

提前致谢。

【问题讨论】:

    标签: asp.net webforms modelbinders defaultmodelbinder


    【解决方案1】:

    Sherlock,您在尝试使用 MVC 中的 ModelBinder 时会遇到一些问题,因为它们依赖于 ControllerContext。

    我之前回答了一个类似的问题ChangeType, Convert - Converting from one type to another,但这确实是您想要的。

    在我的博客上查看这篇博文 ChangeType – Changing the type of a variable in C#

    基本上,您会得到一个名为ChangeType<T> 的方法,该方法以强类型方式返回您要查找的参数的值,如果参数不存在,则返回默认值。

    现在关于自定义类(主要是 DTO 类型类),如果您不介意使用反射,那么我有一个解决方案也可以处理大多数自定义类。遗嘱末尾提到的 DtoBinder 类很好。

    基本上,最后 3 个代码清单包含您需要的所有代码,以便处理典型 Web 应用程序场景中的几乎所有需求。此外,它是可扩展的,因此如果您需要实现自己的活页夹,您可以非常简单地做到这一点,并在应用程序的任何位置使用 RequestBinder 注册活页夹。

    因此,如果您不想对某些经常使用的 DTO 对象使用反射,您可以为该类型实现一个绑定器并注册它,然后它将使用您的自定义绑定器。在许多方面,它在概念上类似于 MVC ModelBinder。

    已编辑 -

    下面是一个 .cs 文件,其中包含我过去用来完全满足您需要的一堆类。第一个 MsPropertyAssignerProvider 是您要在页面中使用的那个。

    您将遍历您的控件并调用 GetPropertyAssigner 方法,将控件的类型名称传递给它。此方法返回一个 ObjectPropertyAssigner 实例,该实例具有一个名为 SetPropertyValue 的方法,您可以将对象实例和控件实例传递给该方法。

      internal class MsPropertyAssignerProvider
      {
        private Hashtable propertyAssigners;
    
        internal MsPropertyAssignerProvider()
        {
          propertyAssigners = new Hashtable();
          RegisterPropertyAssigner(typeof(TextBox).ToString(), new TextBoxValueExtractor());
          RegisterPropertyAssigner(typeof(DropDownList).ToString(), new DropDownListValueExtractor());
          RegisterPropertyAssigner(typeof(Label).ToString(), new LabelValueExtractor());
          RegisterPropertyAssigner(typeof(CheckBox).ToString(), new CheckBoxValueExtractor());
        }
    
        internal void RegisterPropertyAssigner(string identifier, IMsObjectPropertyAssigner assigner)
        {
          if (propertyAssigners.ContainsKey(identifier))
            throw new DuplicatePropertyAssignerRegistrationException(identifier);
          propertyAssigners.Add(identifier, assigner);
        } 
    
        internal IMsObjectPropertyAssigner GetPropertyAssigner(string identifier)
        {
          return (propertyAssigners.ContainsKey(identifier)) ? (IMsObjectPropertyAssigner)propertyAssigners[identifier] : null;
        }
      }
    

    下面列出了随附的类

      public interface IMsObjectPropertyAssigner
      {
        void SetPropertyValue(object obj, System.Web.UI.Control control); 
      }
    
      internal abstract class BaseValueExtractor : IMsObjectPropertyAssigner
      {
        protected MsReflectionHelper reflectionHelper = new MsReflectionHelper();
        protected string FixStringForNumber(string stringValue)
        {
          if (stringValue.Length == 0)
            return "0";
          else
            return stringValue;
        }
        public abstract void SetPropertyValue(object obj, System.Web.UI.Control control);
      }
    
      internal class TextBoxValueExtractor : BaseValueExtractor
      {
        public override void SetPropertyValue(object obj, System.Web.UI.Control control)
        {
          TextBox textBox = (TextBox)control;
          PropertyInfo propInfo = reflectionHelper.GetPropertyInfo(obj, control.ID);
          Type propType = propInfo.PropertyType;
          if (propType == typeof(System.String))
            reflectionHelper.SetPropertyValue(obj, control.ID, textBox.Text);
          else if (propType == typeof(System.Int16))
            reflectionHelper.SetPropertyValue(obj, control.ID, Int16.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
          else if (propType == typeof(System.Int32))
            reflectionHelper.SetPropertyValue(obj, control.ID, Int32.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
          else if (propType == typeof(System.Int64))
            reflectionHelper.SetPropertyValue(obj, control.ID, Int64.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
          else if (propType == typeof(System.Double))
            reflectionHelper.SetPropertyValue(obj, control.ID, Double.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
          else if (propType == typeof(System.Single))
            reflectionHelper.SetPropertyValue(obj, control.ID, Single.Parse(FixStringForNumber(textBox.Text), System.Globalization.NumberStyles.Currency));
          else
            reflectionHelper.SetPropertyValue(obj, control.ID, textBox.Text);
        }
      }
    
      internal class DropDownListValueExtractor : BaseValueExtractor
      {
        public override void SetPropertyValue(object obj, System.Web.UI.Control control)
        {
          DropDownList dropDownList = (DropDownList)control;
          reflectionHelper.SetPropertyValue(obj, control.ID, dropDownList.SelectedValue);
        }
      }
    
      internal class LabelValueExtractor : BaseValueExtractor
      {
        public override void SetPropertyValue(object obj, Control control)
        {
          Label label = (Label)control;
          reflectionHelper.SetPropertyValue(obj, control.ID, label.Text);
        }
      }
    
      internal class CheckBoxValueExtractor : BaseValueExtractor
      {
        public override void SetPropertyValue(object obj, Control control)
        {
          CheckBox checkbox = (CheckBox)control;
          reflectionHelper.SetPropertyValue(obj, control.ID, checkbox.Checked);
        }
      }
    

    抱歉,无论我做什么,编辑器都会完全弄乱代码列表。但我希望这会有所帮助。

    【讨论】:

    • 只有第一段和最后一段与提出的问题有些相关。我不介意使用反射。这就是专有库的工作方式。
    • 我猜在您的情况下,因为您正在使用控件,因此解决方案涉及更多。 DtoBinder 类可以创建您的 Dto 对象的实例,并在给定 NameValues 集合的情况下分配所有属性值。所以你需要做的是从控件的名称和值中组装这个集合,然后将它交给 DtoBinder。
    • 刚刚用我过去使用过的一些附加类编辑了我的原始答案。
    【解决方案2】:

    难道你不能使用 AutoMapper 来做这样的事情吗?只需设置您的地图,它就会创建新对象并将值复制到其中。

    【讨论】:

      【解决方案3】:

      这是一个相当老的问题,但我在试图弄清楚默认模型绑定器的实际工作原理时遇到了这个问题。

      我在 CodeProject 上有一个项目,它实际上可以满足您的需求(编辑)have a look

      干杯!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-04
        • 2017-03-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多