【问题标题】:Using dynamic objects with ASP.NET MVC model binding将动态对象与 ASP.NET MVC 模型绑定一起使用
【发布时间】:2012-02-26 04:47:52
【问题描述】:

在 ASP.NET MVC3 应用程序中,如果我想将我的表单发布数据建模绑定到 ExpandoObject(或我自己的从 DynamicObject 派生的对象,我在其中实现自己的 Try... 成员),我需要编写我自己的自定义模型绑定器?

如果我这样做:

public ActionResult Save(ExpandoObject form)
{
    ....
}

form 的值为null

或者如果我有:

public class MyDynamicThing : DynamicObject
{
    public int Id { get; set; }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        // Set breakpoint here but doesn't get hit when model binding
        return base.TrySetMember(binder, value);
    }
}

...在我的控制器中:

public ActionResult Save(MyDynamicThing form)
{
    ....
}

在上面的示例中,Id 设置为表单中的值。但是,如果我在 TrySetMember 中设置断点,则不会受到影响。

我可以调用任何神奇的咒语来强制内置模型绑定器与ExpandoObjects 或我自己从DynamicObject 派生的类一起工作吗?

我可以求助于获取原始表单发布集合,但我必须将此数据序列化为 JSON,这意味着获取这些值需要一个额外且不整洁的步骤。

【问题讨论】:

    标签: asp.net-mvc asp.net-mvc-3 model-binding


    【解决方案1】:

    不,这对于内置模型绑定器是不可能的。您当然可以编写自定义模型绑定器。内置模型绑定器能够绑定的唯一属性是它似乎来自MyDynamicThing 类型的属性,这就是它只能设置Id 属性的原因。它不知道其他属性。

    【讨论】:

      【解决方案2】:

      试试这个:

      public class ExpandoObjectBinder : DefaultModelBinder
      {
          public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
          {
              if (bindingContext == null)
                  throw new ArgumentNullException("bindingContext");
      
              IDictionary<string, object> model = new ExpandoObject();
      
              string modelName = bindingContext.ModelName;
              var form = controllerContext.HttpContext.Request.Unvalidated.Form;
              var keys = form.AllKeys.Where(k => k.StartsWith(modelName + "."));
              Debug.Write("ExpandoObjectBinder keys count is " + keys.Count());
              foreach (var key in keys)
              {
                  var propName = key.Replace(model + ".", "");
                  model.Add(propName, form[key]);
              }
              if (model.Count == 0)
                  throw new Exception("Data is empty.");
              return model;
          }
      }
      

      注册binder mvc初始化:

      ModelBinders.Binders.Add(typeof(ExpandoObject), new ExpandoObjectBinder());

      【讨论】:

        【解决方案3】:

        添加类型支持(int、byte、long、decimal、string、array 和 subobjects):

        public class ExpandoObjectBinder : DefaultModelBinder
        {
            public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {
                if (bindingContext == null)
                    throw new ArgumentNullException("bindingContext");
        
                string modelName = bindingContext.ModelName;
                var form = controllerContext.HttpContext.Request.Unvalidated.Form;
                var model = ParseProperties(modelName, form);
                return model;
            }
            public object ParseProperties(string modelName, NameValueCollection form)
            {
                var keys = form.AllKeys.Where(k => k.StartsWith(modelName + "."));
                Debug.WriteLine("ExpandoObjectBinder keys count is " + keys.Count() + " for " + modelName);
                IDictionary<string, object> model = new ExpandoObject();
                List<string> subModelNames = new List<string>();
                List<string> arrModelNames = new List<string>();
                //ModelName: Dialog.Attributes[0].Options
                foreach (var key in keys)
                {
                    //Dialog.Attributes[0].Options.Byte
                    //Dialog.Attributes[0].Options.Inner.Byte
                    //Dialog.Attributes[0].Options.Inner.Integer
                    //Dialog.Attributes[0].Options.SimpleArray[0]
                    //Dialog.Attributes[0].Options.SimpleArray[1]
                    var propName = key.Replace(modelName + ".", "");
                    //Byte
                    //Inner.Byte
                    //Inner.Integer
                    //SimpleArray[0]
                    //SimpleArray[1]
                    if (!(propName.Contains('[') || propName.Contains('.')))
                    {
                        model.Add(propName, GetValue(form, key));
                    }
                    //array (can allow sub objects)
                    if (propName.Contains('['))
                    {
                        var names = propName.Split('[');
                        var arrModelName = names[0];
                        if (!arrModelNames.Contains(arrModelName))
                            arrModelNames.Add(arrModelName);
                    }
                    //not array but can has sub objects
                    if (!propName.Contains('[') && propName.Contains('.'))
                    {
                        var names = propName.Split('.');
                        var subModelName = names[0];
                        if (!subModelNames.Contains(subModelName))
                            subModelNames.Add(subModelName);
                    }
                }
        
                foreach (var subModelName in subModelNames)
                {
                    var key = modelName + "." + subModelName;
                    object val = form[key];
                    val = ParseProperties(key, form);
                    model.Add(subModelName, val);
                }
        
                foreach (var arrModelName in arrModelNames)
                {
                    //Dialog.Attributes[0].Options.SimpleArray[
                    var key = modelName + "." + arrModelName + "[";
                    var arrKeys = form.AllKeys.Where(k => k.StartsWith(key));
                    var isComplexArray = false;
                    int length = 0;
                    foreach (var arrKey in arrKeys)
                    {
                        var aKey = arrKey.Replace(key, "");
                        if (aKey.Contains("."))
                            isComplexArray = true;
                        var parsed = aKey.Split(']');
                        var num = int.Parse(parsed[0]);
                        if (num > length)
                            length = num;
                    }
                    List<object> vals = new List<object>();
                    if (isComplexArray)
                    {
                        for (int i = 0; i < length + 1; i++)
                        {
                            var arrKey = key + i + "]";
                            object val = ParseProperties(arrKey, form);
                            vals.Add(val);
                        }
                    }
                    else
                    {
                        for (int i = 0; i < length + 1; i++)
                        {
                            var arrKey = key + i + "]";
                            vals.Add(GetValue(form, arrKey));
                        }
                    }
                    model.Add(arrModelName, vals);
                }
                return model;
            }
        
            object GetValue(NameValueCollection form, string key)
            {
                object val = form[key];
                if (decimal.TryParse(form[key], out decimal decimalVal))
                    val = decimalVal;
                if (long.TryParse(form[key], out long longVal))
                    val = longVal;
                if (int.TryParse(form[key], out int intVal))
                    val = intVal;
                if (byte.TryParse(form[key], out byte byteVal))
                    val = byteVal;
                if (bool.TryParse(form[key], out bool boolVal))
                    val = boolVal;
                return val;
            }
        }
        

        【讨论】:

        • 嘿,这是很久以前的事了,我不记得我需要这个做什么了。我有一种感觉,我确实编写了自己的自定义模型活页夹。不过感谢您的回答。当我有空闲时间时,我会玩。
        猜你喜欢
        • 2023-03-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-25
        • 1970-01-01
        • 2013-04-02
        • 1970-01-01
        • 2017-04-02
        相关资源
        最近更新 更多