3

我有一个包含多态对象列表的 ViewModel。

public class InputParameters
{
    public InputParameters()
    {
        InputPrompts = new List<IInputPrompt>();
    }

    public string Name { get; set; }
    public string Path { get; set; }

    public IList<IInputPrompt> InputPrompts { get; set; }
}

这又看起来是这样的:

public interface IInputPrompt
{
    string Name { get; set; }
    bool IsHidden { get; set; }
    bool IsRequired { get; set; }

    dynamic Value { get; set; }
}
public class TextPrompt : IInputPrompt
{
    public string Name { get; set; }
    public bool IsHidden { get; set; }
    public bool IsRequired { get; set; }
    public dynamic Value { get; set; }
}
public class MultiSelectPrompt : IInputPrompt
{
    public string Name { get; set; }
    public bool IsHidden { get; set; }
    public bool IsRequired { get; set; }
    public dynamic Value { get; set; }

    public MultiSelectList Values
    {
        get
        {
            return new MultiSelectList(((IDictionary<int, string>)Value).ToList(), "Key", "Value");
        }
    }
}

每个派生类型都有一个编辑器视图模板,视图如下所示:

@model OptionListModelBinding.Models.InputParameters
@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>
@using (Html.BeginForm())
{
    <fieldset>
        <legend><strong>@Model.Name</strong></legend>
        <div><em>@Model.Path</em></div>
        <div>@Html.EditorFor(p => p.InputPrompts)</div>
        <div><input type="submit" /></div>
    </fieldset>

}
// editor template
@model OptionListModelBinding.Models.InputParameters
@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>
@using (Html.BeginForm())
{
    <fieldset>
        <legend><strong>@Model.Name</strong></legend>
        <div><em>@Model.Path</em></div>
        <div>@Html.EditorFor(p => p.InputPrompts)</div>
        <div><input type="submit" /></div>
    </fieldset>

} 
// editor template
@model OptionListModelBinding.Models.MultiSelectPrompt
@{
    ViewBag.Title = "MultiSelectPrompt";
}

<h2>MultiSelectPrompt</h2>
<div><strong>@Model.Name</strong></div>
<div><em>@Html.ListBox(Model.Name, Model.Values)</em></div>

这一切都很好地呈现:屏幕截图

问题是这样的:

如何将其绑定回模型?我有一个自定义模型绑定器:(请原谅这段代码的破坏性质)。

    public class InputParameterModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException("bindingContext");
        }
        // iterate over the form fields
        foreach (string item in controllerContext.HttpContext.Request.Form.AllKeys)
        {
            // this is where the problem is ... I cannot use the generic here.
            var dog = FromPostedData<string>(bindingContext, item);

        }

        InputParameters userInput = new InputParameters();

        return userInput;
    }

    // Dino Esposito code
    private T FromPostedData<T>(ModelBindingContext context, string id)
    {
        if (String.IsNullOrEmpty(id)) return default(T);
        var key = String.Format("{0}.{1}", context.ModelName, id);
        var result = context.ValueProvider.GetValue(key);
        if (result == null && context.FallbackToEmptyPrefix)
        {
            result = context.ValueProvider.GetValue(id);
            if (result == null) return default(T);
        }

        context.ModelState.SetModelValue(id, result);

        T valueToReturn = default(T);
        try
        {
            valueToReturn = (T)result.ConvertTo(typeof(T));
        }
        catch { }
        return valueToReturn;
    }
}

编辑 我是否提到列表中的项目是在运行时确定的?

编辑 这是一个报告生成工具的前端。后端服务提供可用报告的列表以及运行每个报告所需的参数。这些在编译时都不知道,报告定义甚至可以随着需要重新编译的门户网站而改变。

我可以有可变数量和类型的输入参数。

4

2 回答 2

0

您不能期望 MVC 实例化 an,IInputPrompt因为它没有逻辑来确定您真正想要的类。您的选择是创建一个 ModelBinder 以将模型绑定到此特定接口,或者将接口更改为具体类。

于 2013-01-04T23:29:15.260 回答
0

我认为您不需要自定义活页夹。您可以简单地将您的视图模型传递回 post 方法:

[HttpPost]
public ActionResult ProcessForm(InputParameters viewModel)
{
    ...
}

默认的 MVC 绑定器会将表单值与 InputParameters 参数的属性相匹配。如果您的视图使用任何类型的特殊表单变量名称,您可能需要在该参数上使用 [Bind] 属性来指示绑定器必须查找的变量名称前缀。

于 2012-11-21T06:59:32.313 回答