Связывание модели MVC: почему я не могу выполнить привязку к свойству итератора?

Когда моя модель имеет свойство IEnumerable , которое реализовано как итератор (то есть yield return ), MVC DefaultModelBinder не может связываться с этим свойством, когда входящие значения используют синтаксис квадратных скобок (например, «Foo [0]» ).

Пример модели:

namespace ModelBinderTest
{
    using System.Collections.Generic;
    public class MyModel
    {
        private List<string> fooBacking = new List<string>();
        public IEnumerable<string> Foo
        {
            get
            {
                foreach (var o in fooBacking)
                {
                    yield return o; // <-- ITERATOR BREAKS MODEL BINDING
                }
            }
            set { fooBacking = new List<string>(value); }
        }

        private List<string> barBacking = new List<string>();
        public IEnumerable<string> Bar
        {
            get
            {
                // Returning any non-iterator IEnumerable works here. Eg:
                return new List<string>(barBacking);
            }
            set { barBacking = new List<string>(value); }
        }
    }
}

Пример сбоя 1 :

namespace ModelBinderTest
{
    using System;
    using System.Linq;
    using System.Web.Mvc;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    [CLSCompliant(false)]
    public class DefaultModelBinderTestIterator
    {
        [TestMethod]
        public void BindsIterator()
        {
            // Arrange
            var model = new MyModel();

            ModelBindingContext bindingContext = new ModelBindingContext()
            {
                FallbackToEmptyPrefix = true,
                ModelMetadata = ModelMetadataProviders
                                  .Current
                                  .GetMetadataForType(null, model.GetType()),
                ModelName = "",
                ValueProvider = new NameValueCollectionValueProvider(
                    new System.Collections.Specialized.NameValueCollection()
                        {
                            { "Foo[0]", "foo" },
                            { "Bar[0]", "bar" },
                        },
                    System.Globalization.CultureInfo.InvariantCulture
                )
            };

            DefaultModelBinder binder = new DefaultModelBinder();

            // Act
            MyModel updatedModel = (MyModel)binder.BindModel(
                                     new ControllerContext(), bindingContext);

            // Assert
            Assert.AreEqual(1, updatedModel.Bar.Count(),
                            "Bar property should have been updated");
            Assert.AreEqual("bar", updatedModel.Bar.ElementAtOrDefault(0),
                            "Bar's first element should have been set");

            Assert.AreEqual(1, updatedModel.Foo.Count(),
                            "Foo property should have been updated");
            Assert.AreEqual("foo", updatedModel.Foo.ElementAtOrDefault(0),
                            "Foo's first element should have been set");
        }

    }
}

Приведенный выше модульный тест обновит свойство Bar моей модели до ["bar"] нет проблем (с квадратными скобками в ключах коллекции или без них), но не сможет ничего связать со свойством Foo .

Кто-нибудь знает (на низком уровне), почему реализация свойства IEnumerable в качестве итератора приведет к сбою привязки модели?

Меня не особо интересуют обходные пути 2 ], а скорее некоторый анализ, поскольку я исчерпал свои знания о фреймворке, зашедшем так далеко;)


1: Модульный тест был самым простым способом изолировать проблему для SO, а не рассматривать весь пример приложения MVC .

2: Например, я знаю, что если я уберу квадратные скобки из ввода и повторно использую один и тот же ключ «Foo» для всех значений, привязка модели будет работать.Однако в случае реальной неудачи требуются квадратные скобки, поскольку каждый элемент в коллекции представляет собой сложный тип со своими собственными подсвойствами. Или другой обходной путь: добавьте к действию параметр без итератора IEnumerable и назначьте , что свойству непосредственно внутри действия. Фу.

6
задан Roatin Marth 9 February 2011 в 15:44
поделиться