在Mvc View脚手架模板中使用视图模型

时间:2015-03-31 19:08:51

标签: asp.net-mvc t4 scaffolding asp.net-mvc-scaffolding

我有viewmodel

public class ClientIndexViewModel
{
    [Key]
    public int SurrogateId { get; set; } // not primary key just used in templating
    public IEnumerable<Client> Clients { get; set; }
}

我创建了一个名为Index.cs.t4的新MvcView t4模板。当我从控制器创建一个新视图并将我的模型类添加为ClientIndexViewModel时,会生成视图模板(index.cshtml),但没有IEnumerable<Client>

的属性

模板化Index.cshtml

<table class="table">
<!-- Client properties should be here .. i think -->
<tr>
    <th></th> <!-- This is all empty because i dont really know what i am doing -->
</tr>

@foreach (var item in Model) {
<tr>
    <td>
        @Html.ActionLink("Edit", "Edit", new { id=item.SurrogateId }) |
        @Html.ActionLink("Details", "Details", new { id=item.SurrogateId }) |
        @Html.ActionLink("Delete", "Delete", new { id=item.SurrogateId })
    </td>
</tr>
}

当我查看模板文件时,这是有意义的。

Index.cs.t4

<#
IEnumerable<PropertyMetadata> properties = ModelMetadata.Properties;
foreach (PropertyMetadata property in properties) {
if (property.Scaffold && !property.IsPrimaryKey && !property.IsAssociation)     {
#>
<#
    if (property.IsAssociation && GetRelatedModelMetadata(property) == null)     {
        continue;
    }
#>
    <th>
        @Html.DisplayNameFor(model => model.<#= GetValueExpression(property)     #>)
    </th>
<#
    }
}
#>

我希望能够专门定位IEnumerable<Client>变量的viewmodel上的properties属性,作为PropertyMetaData

上枚举的属性

我想我可以在ModelMEtadataFunctions.include.t4文件中创建一个方法。只是不太确定如何,或者我是否应该这样做

2 个答案:

答案 0 :(得分:0)

我找到了一个使这项工作成功的黑客。这绝不是任何理想的使用,但考虑到它只是视觉工作室的脚手架代码,而不是项目代码,它似乎并不太糟糕

首先。对于视图,Model Class是我想要枚举的实体类型。所以我从客户端实体获取客户端的所有属性,而不是我的viewmodel。然而,模型是错误的(当然)。因此,在模板文件Index.cs.t4中,我根据ViewDataTypeShortName参数(在Imports.include.t4中找到)更改了控制块。这给了我Client,因为我选择了客户端实体。

因此,例如在foreach中,它通常会显示

 @foreach (var item in Model){
 <tr>
    <td>
        @Html.DisplayFor(modelItem => <#= "item." + GetValueExpression(property) #>) 
    </td>
 </tr>
 }

现在我通过添加

来控制它
@foreach (var item in Model.<#= NewViewDataTypeShortName #>)
// NewViewDataTypeShortName is created with 
// <# string NewViewDataTypeShortName = ViewDataTypeShortName + 's'; #> 
// Based on my naming convention.

Index.cshtml

中生成此内容
@foreach (var item in Model.Clients) {

然后我通过在ModelMetaDataFunctions.cs.include.t4

中创建方法来添加我的@model
string GetViewModelName(string ViewDataTypeName,string   
                       ViewDataTypeShortName,string ViewName){

       string[] names = ViewDataTypeName.Split(new string[]{"."},StringSplitOptions.None);
       return names[0] + ".Domain.ViewModels." + 
       ViewDataTypeShortName + ViewName +"ViewModel";
}

Index.cs.t4中调用哪个

@model <#= GetViewModelName(ViewDataTypeName,ViewDataTypeShortName,ViewName) #>

然后在Index.cshtml中显示为

@model TaskR.Domain.ViewModels.ClientIndexViewModel

这基本上给了我所需要的一切。它不是理想的,并且依赖于命名约定,我不确定我想交给另一个开发人员。例如,如果在主项目中创建了viewmodel,那么这将不会正确支撑,因为它基于数据类的单独项目。此外,实体类型名称和视图模型名称具有非常严格的约定,以实现此功能。

如果您有更好的主意,请发帖,我不认为这是最佳做法

答案 1 :(得分:0)

如果您的Models文件夹与ViewModels文件夹位于同一文件夹根目录中,则可以使用

string GetViewModelName(string ViewDataTypeName,string   
                   ViewDataTypeShortName,string ViewName){

   string[] names = ViewDataTypeName.Split('.');
   names[names.Length-2] = "ViewModels";
   names[names.Length-1] = names[names.Length-1]+"ViewModel";
   return string.Join(".",names);
}

在我之后它更通用了