使用ASP.NET MVC模型绑定的动态对象

时间:2012-02-26 04:47:52

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

在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,这意味着收集这些值的额外和不整齐的步骤。

3 个答案:

答案 0 :(得分:2)

不,内置模型绑定器无法实现这一点。你当然可以写一个自定义模型绑定器。内置模型绑定器能够绑定的唯一属性是{em>似乎来自MyDynamicThing类型的属性,这就是为什么它只能设置Id属性。它不了解其他属性。

答案 1 :(得分:1)

尝试一下:

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;
    }
}

注册活页夹mvc初始化:

ModelBinders.Binders.Add(typeof(ExpandoObject),新的ExpandoObjectBinder());

答案 2 :(得分:1)

添加了类型支持(int,字节,长整数,十进制,字符串,数组和子对象):

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;
    }
}