如何确保模型绑定按预期工作?

时间:2013-07-25 19:52:24

标签: c# asp.net-mvc-4

我正在用C#ASP.NET MVC4(Razor引擎)编程。我需要创建一个局部视图并在多个地方重用它。问题是视图是一个表单,在某些情况下我需要将它与ViewModel一起使用。我的问题是模型绑定将如何工作,因为在ViewModel中它将是属性的属性。例如:

public class PersonModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class OfficeViewModel
{
    public PersonModel Person { get; set; }

    // other properties
}

PersonModel的部分视图是:

@model SomeNameSpace.PersonModel

@Html.TextBoxFor(m => m.FirstName)
@Html.TextBoxFor(m => m.LastName)

渲染此视图时,它看起来像这样:

<input type="textbox" id="FirstName" name="FirstName" />
<input type="textbox" id="LastName" name="LastName" />

现在我想在OfficeViewModel中使用相同的视图。在这种情况下,我会在我的Office视图中执行此操作:

@{ Html.RenderPartial("Person", Model.Person); }

渲染此局部视图时,它将如上所示进行渲染。如果我不重用该视图,我的Office视图将如下所示:

@model SomeNameSpace.OfficeViewModel

@Html.TextBoxFor(m => m.Person.FirstName)
@Html.TextBoxFor(m => m.Person.LastName)

那将呈现为:

<input type="textbox" id="Person_FirstName" name="Person.FirstName" />
<input type="textbox" id="Person_LastName" name="Person.LastName" />

请注意 name 属性如何具有 Person 前缀属性。因此,如果我使用RenderPartial选项并传入Model.Person,模型绑定器是否知道在OfficeViewModel中绑定 FirstName LastName 的位置?

如果模型绑定器足够智能以检查属性的属性,那么当我在OfficeViewModel中有 ManagerModel EmployeeModel 并且它们都具有名为FirstName的属性时会发生什么和姓氏?

我希望我已经清楚了,并提前感谢。

3 个答案:

答案 0 :(得分:4)

不幸的是,在这种情况下,Html.RenderPartial不会传递提供正确字段名称所需的信息。

如果您想以这种方式重用部分视图,请查看使用带有Html.EditorFor的编辑器模板。与其他*一样*对于助手,EditorFor采用lambda表达式,允许它继承传递给模板的属性的名称。

答案 1 :(得分:2)

@Gerald是对的。默认RenderPartial无法理解。虽然您可以编写一个自定义帮助程序来处理该问题:

public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, System.Linq.Expressions.Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
    string name = ExpressionHelper.GetExpressionText(expression);
    object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
    var viewData = new ViewDataDictionary(helper.ViewData)
    {
        TemplateInfo = new System.Web.Mvc.TemplateInfo
        {
            HtmlFieldPrefix = name
        }
    };

    return helper.Partial(partialViewName, model, viewData);
}

在视图中使用它:

@Html.PartialFor(x => x.Person, "Person")

答案 2 :(得分:0)

在这里找到答案:https://stackoverflow.com/a/4807655/78739

基本上我需要设置 ViewData.TemplateInfo.HtmlFieldPrefix

所以我在PersonModel中添加了一个属性,并将其命名为 HtmlFieldPrefix 。例如:

public class PersonModel
{
    public string HtmlFieldPrefix { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

然后在我的部分视图中我这样做:

@model SomeNameSpace.PersonModel

@{
    ViewData.TemplateInfo.HtmlFieldPrefix = Model.HtmlFieldPrefix;
}

@Html.TextBoxFor(m => m.FirstName)
@Html.TextBoxFor(m => m.LastName)

在我的控制器操作中,我可以这样做:

model.HtmlFieldPrefix = "Person";

return View("_Person", model);

现在我的渲染视图将如下所示:

<input type="textbox" id="Person_FirstName" name="Person.FirstName" />
<input type="textbox" id="Person_LastName" name="Person.LastName" />

如果我将model.HtmlFieldPrefix保留为null,则视图将呈现为:

<input type="textbox" id="FirstName" name="FirstName" />
<input type="textbox" id="LastName" name="LastName" />

所以这解决了我的问题,并允许我使用ViewModels的部分视图。