问题 动态属性的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="Sections_@(i)__Criteria_@(j)__Value">@criterionDisplayName</label>
        @Html.Editor(name, type)
    }
}

这确实显示了一个正确的复选框,但它没有使用该值来正确设置复选框状态(如果criterion.Value为true则检查)。其他类型也是如此,比如 ints。 (它确实在POST请求后填写表单,但这是因为MVC使用临时模型来重新创建用户输入。)

我们已经尝试和研究过:甚至可以使用具有类型属性的编辑器模板 dynamic?如果是的话 - 我们怎样才能让它发挥作用? (我们不希望根据可能的类型来识别。我们希望MVC框架根据实际类型使用正确的编辑器模板。)


12761
2017-07-14 18:27


起源



答案:


动态不适合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 类型。


15
2017-07-15 08:16



它适用于强类型视图。我尝试将Index-View更改为不使用强类型视图:已删除 @model IList<Criterion> 和使用 @{ var model = (IList<Criterion>)Model; }。然后我试试 @Html.Editor("model[" + i + "].Value", "Criterion_" + model[i].Value.GetType().Name),但它给了我一个错误 - 传递到字典中的模型项为null,但此字典需要“System.Int32”类型的非null模型项。 我究竟做错了什么?调试告诉我GetType()。Name是 Int32 在索引视图中。 - toni
顺便说一下 - 我们需要使用非强类型视图,因为我们使用的是第三方框架(来自Orchard的ContentShape)。 - toni
我们最终使用了Darins方法 object。我们只需要转换动态ViewModel并将其传递给强类型编辑器模板。感谢Darin的宝贵意见。 - toni
EditorTemplates 是要走的路。 - Oliver


答案:


动态不适合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 类型。


15
2017-07-15 08:16



它适用于强类型视图。我尝试将Index-View更改为不使用强类型视图:已删除 @model IList<Criterion> 和使用 @{ var model = (IList<Criterion>)Model; }。然后我试试 @Html.Editor("model[" + i + "].Value", "Criterion_" + model[i].Value.GetType().Name),但它给了我一个错误 - 传递到字典中的模型项为null,但此字典需要“System.Int32”类型的非null模型项。 我究竟做错了什么?调试告诉我GetType()。Name是 Int32 在索引视图中。 - toni
顺便说一下 - 我们需要使用非强类型视图,因为我们使用的是第三方框架(来自Orchard的ContentShape)。 - toni
我们最终使用了Darins方法 object。我们只需要转换动态ViewModel并将其传递给强类型编辑器模板。感谢Darin的宝贵意见。 - toni
EditorTemplates 是要走的路。 - Oliver