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

Using view models in MvcView scaffolding templates

我有视图模型

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 模板。当我从控制器创建一个新视图并将我的模型 class 添加为 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>
<#
    }
}
#>

我希望能够专门针对 properties 变量的视图模型上的 IEnumerable<Client> 属性,作为 PropertyMetaData 上枚举的属性

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

我找到了使这项工作可行的 hack。这绝不是任何理想的使用方式,但考虑到它只是 visual studio 的脚手架代码而不是项目代码,它似乎还不错

首先。对于视图,模型 Class 是我要枚举的实体类型。所以我从客户端实体而不是我的视图模型获取客户端的所有属性。然而模型是错误的(当然)。所以在模板文件中,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

这基本上满足了我的所有需求。它并不理想,并且严重依赖命名约定,我不确定我是否愿意将其移交给其他开发人员。例如,如果在主项目中创建视图模型,这将无法正确构建脚手架,因为它基于数据 class 的单独项目。实体类型名称和视图模型名称也有非常严格的约定,才能正常工作..

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

如果您的 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);
}

在我之后更通用