Использование динамических объектов с привязкой модели ASP.NET MVC - PullRequest
3 голосов
/ 26 февраля 2012

В приложении 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 ]

2 голосов
/ 26 февраля 2012

Нет, это невозможно с помощью встроенного связующего устройства. Вы, конечно, могли бы написать пользовательскую модель переплета. Единственное свойство, которое может связывать встроенный связыватель модели, это то, что оно кажется из типа MyDynamicThing, и поэтому оно может устанавливать только свойство Id. Он не знает других свойств.

1 голос
/ 10 января 2019

Добавлена ​​поддержка типов (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;
    }
}
1 голос
/ 09 января 2019

Попробуйте это:

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), new ExpandoObjectBinder ());

...