Редактор Asp.Net MVC 3 для динамического свойства

Мы пытались заставить редактор-шаблон работать с динамическим свойством - безрезультатно. Может быть, один из вас может нам помочь.

Вот примерно наш класс:

public class Criterion
{
    ...
    public string Text { get; set; }
    public dynamic Value { get; set; }
    public Type Type { get; set; }
    ...
}

В нашем представлении бритвы есть модель, содержащая список разделов, каждый из которых содержит в себе список критериев. (Мы получаем эту информацию во время выполнения.) Все эти критерии должны отображаться в режиме редактирования - относительно их фактического типа: (выдержка)

@for (int i = 0; i < model.Sections.Count(); i++)
{
    for (int j = 0; j < model.Sections[i].Criteria.Count(); j++)
    {
        var criterion = model.Sections[i].Criteria[j];
        var type = criterion.Type.Name;
        var name = "Sections[" + i + "].Criteria[" + j + "].Value";
        var criterionDisplayName = criterion.Text;
        <label for="[email protected](i)[email protected](j)__Value">@criterionDisplayName</label>
        @Html.Editor(name, type)
    }
}

Это показывает, например, флажок правильно, но он не использует значение, чтобы правильно установить статус флажка (если значение критерия равно true). То же самое касается других типов, таких как ints. (Он правильно заполняет форму после запроса POST, но это потому, что MVC использует временную модель для воссоздания ввода пользователей.)

Насколько мы пробовали и исследовали: возможно ли использовать шаблон редактора со свойствами типа dynamic? Если да - как мы можем заставить его работать? (Мы не хотели бы различать в зависимости от возможного типа. Мы хотели бы, чтобы структура MVC использовала правильный шаблон редактора на основе фактического типа.)

Ответ 1

Динамика не подходит для счета с ASP.NET MVC. Они напоминают мне о ViewBag, и я ненавижу ViewBag от самых глубоких тканей моего тела. Поэтому я бы принял другой подход.

Возьмем, например, следующую модель:

public class Criterion
{
    public string Text { get; set; }
    public object Value { get; set; }
}

Значение может быть любым типом, который вы хотите обработать.

Теперь у вас может быть контроллер, который заполняет эту модель:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new[]
        {
            new Criterion { Text = "some integer", Value = 2 },
            new Criterion { Text = "some boolean", Value = true },
            new Criterion { Text = "some string", Value = "foo" },
        };
        return View(model);
    }
}

а затем соответствующий вид:

@model IList<Criterion>

@using (Html.BeginForm())
{
    for (int i = 0; i < Model.Count; i++)
    {
        <div>
            @Html.LabelFor(x => x[i], Model[i].Text)
            @Html.EditorFor(x => x[i].Value, "Criterion_" + Model[i].Value.GetType().Name)
        </div>
    }

    <button type="submit">OK</button>
}

Теперь для каждого типа, который вы хотите обработать, вы можете определить соответствующий шаблон редактора:

~/Views/Shared/EditorTemplates/Criterion_String.cshtml:

@model string
@Html.TextBoxFor(x => x)

~/Views/Shared/EditorTemplates/Criterion_Boolean.cshtml:

@model bool
@Html.CheckBoxFor(x => x)

~/Views/Shared/EditorTemplates/Criterion_Int32.cshtml:

@model int
@{
    var items = Enumerable
        .Range(1, 5)
        .Select(x => new SelectListItem 
        { 
            Value = x.ToString(), 
            Text = "item " + x 
        });
}

@Html.DropDownListFor(x => x, new SelectList(items, "Value", "Text", Model))

Очевидно, что отображение этой модели в представлении - это только первый шаг. Я полагаю, что вы захотите получить значения, введенные пользователем в действии контроллера POST для некоторой обработки. В этом случае необходимы небольшие адаптации. Нам нужно добавить настраиваемое связующее устройство, которое сможет создать экземпляр правильного типа во время выполнения и включить конкретный тип в скрытое поле для каждой строки. Я уже показал пример в this post. Также обратите внимание в этом примере, что я использовал базовый класс вместо прямой работы с типом object.