具有复合ViewModel的MVC和实体框架Html.DisplayNameFor

时间:2013-04-12 16:35:49

标签: c# asp.net-mvc-4 .net-4.5 razor-2

我对使用WPF / Silverlight的MVVM感到相当满意,但这是我第一次尝试使用MVC Web应用程序...这只是我的背景。

我创建了一个名为TestSitesController的控制器,该控制器是从我的Entity Framework Model(生成读/写操作和视图的模板)中的“Site”模型类自动生成的。我修改的唯一的东西是3个点,对于某些方法,有一个默认参数Guid id = null。我只是摆脱了“= null”一切正常。这是我改变的一个例子

public ActionResult Delete(Guid id = null)
{
    //....
}

这已改为

public ActionResult Delete(Guid id)
{
    //....
}

站点模型没有什么特别的SiteId,缩写和DisplayName ...试图让这个问题尽可能简单。好的,所以我运行网站并转到htpp://.../TestSites/,一切都很完美。

我注意到我的所有视图(创建,删除,详细信息和编辑)都使用了@model MVCWeb.MyEntities.Site,我现在完全可以使用它;但是在Index.cshtml视图中,我注意到它正在使用

@model IEnumerable<MVCWeb.MyEntities.Site>

这适用于生成的模板,但我想使用“复合视图模型”,也许这就是我试图混合我的MVVM知识但是如果可能的话就会坚持下去。在我看来,复合视图模型只是一个特定于一个视图的模型,该视图由一个或多个实体模型以及像SelectedSiteId等其他属性组成。

所以我创建了一个非常简单的ViewModel,名为TestSitesViewModel

public class TestSitesViewModel
{
    //Eventually this will be added to a base ViewModel to get rid
    //of the ViewBag dependencies
    [Display(Name = "Web Page Title")]
    public string WebPageTitle;

    public IEnumerable<MVCWeb.MyEntities.Site> Sites;

    //Other Entities, IEnumberables, or properties go here
    //Not important for this example
}

然后在我的控制器中添加了一个名为IndexWithViewModel

的动作方法
public ActionResult IndexWithViewModel()
{
    var vm = new TestSitesViewModel();
    vm.WebPageTitle = "Sites With Composite View Model";
    vm.Sites = db.Sites.ToList();

    return View(vm);
}

然后我制作了一份Index.cshtml副本并将其命名为IndexWithModel.cshtml以匹配我的新ActionResult方法名称。我改变了

的顶线
@model IEnumerable<MVCWeb.MyEntities.Site>

@model MVCWeb.Models.TestSitesViewModel

我在表部分之前添加了这个来测试DisplayNameFor和DisplayFor

@Html.DisplayNameFor(model => model.WebPageTitle)
&nbsp;
@Html.DisplayFor(model => model.WebPageTitle)
<br />

更改了

@foreach (var item in Model) {

@foreach (var item in Model.Sites) {

并注释掉包含所有表头的tr部分。除了@ Html.DisplayNameFor显示变量名称“WebPageTitle”而不是使用“网页标题”(如

中数据注释的显示属性所示)之外,一切都运行正常
[Display(Name = "Web Page Title")]
public string WebPageTitle;

此外,如果我在包含表头信息的tr部分中回复。我不能为我的生活弄清楚放入什么我已经尝试过model.Sites.Abbreviation,model.Sites [0]。缩写,以及其他各种组合但是会出错。

<tr>
    <th>
        @Html.DisplayNameFor(model => model.Abbreviation)
    </th>
    <th>
        @Html.DisplayNameFor(model => model.DisplayName)
    </th>
    <th></th>
</tr>

那我应该在那里使用什么?我不太清楚为什么model.Sites.Abbreviation不起作用,因为Sites是与原始Index.cshtml中的模型完全相同的类型

2 个答案:

答案 0 :(得分:21)

我在弄乱了几个小时之后终于弄明白了。

为了在Data Anotations中使用Display arrtibute,第一个问题是必须添加get和set acessor方法,即使它们只是默认方法。我假设这是因为它们只能用于属性,而不能用于类的公共数据成员。这是获取

的代码
@Html.DisplayNameFor(model => model.WebPageTitle)

正常工作

public class TestSitesViewModel
{
    //Eventually this will be added to a base ViewModel to get rid of
    //the ViewBag dependencies
    [Display(Name = "Web Page Title")]
    public string WebPageTitle { get; set; }

    //PUBLIC DATA MEMBER WON'T WORK
    //IT NEEDS TO BE PROPERTY AS DECLARED ABOVE
    //public string WebPageTitle;

    public IEnumerable<MVCWeb.MyEntities.Site> Sites;

    //Other Entities, IEnumberables, or properties go here
    //Not important for this example
}

问题的第二部分是访问IEnumerable变量中的元数据。我声明了名为headerMetadata的临时变量并使用它而不是尝试通过IEnumerable访问属性

@{var headerMetadata = Model.Sites.FirstOrDefault();}
<tr>
    <th>            
        @Html.DisplayNameFor(model => headerMetadata.Abbreviation)
    </th>
    <th>
        @Html.DisplayNameFor(model => headerMetadata.DisplayName)
    </th>
    ...

这确实提出了headerMetadata变量何时超出范围的问题?它可用于整个视图还是仅限于html表标签?我想这是另一个约会的另一个问题。

答案 1 :(得分:1)

我将接受新学习的MVVM ASP.NET MVC经验。

不要拨打@Html.DisplayNameFor(model => model.WebPageTitle),而是尝试使用@Html.DisplayFor(model => model.WebPageTitle)。您似乎在IndexWithViewModel控制器中设置WebPageTitle值,该值应显示在您的视图中。

查看ASP.NET MVC的共享templates。您可以在/Views/Shared/EditorTemplates/Site.cshtml/Views/Shared/DisplayTemplates/Site.cshtml为DisplayFor / EditorFor帮助程序设置自定义模板或部分。使用DisplayFor和EditorFor渲染Site对象时,ASP.NET MVC将自动获取这些模板。

祝你好运!