Html.DisplayFor ASP.NET Core 2中的接口视图模型



我很难弄清楚如何为接口类型的视图模型呈现显示模板。假设我有一个像下面这样的视图模型:

public class DashboardViewModel
{
public ISelectedWalletsViewModel Wallets { get; set; }
}
public interface ISelectedWalletsViewModel
{
}
public class OneSelectedWalletViewModel : ISelectedWalletsViewModel
{
public SelectedWalletViewModel SelectedWallet { get; set; }
public IEnumerable<WalletViewModel> OtherWallets { get; set; }
}
public class AllSelectedWalletsViewModel : ISelectedWalletsViewModel
{
public IEnumerable<WalletViewModel> Wallets { get; set; }
}
public interface IWalletViewModel
{
long Id { get; set; }
string Name { get; set; }
string Description { get; set; }
string CurrentBalance { get; set; }
}
public class WalletViewModel : IWalletViewModel
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string CurrentBalance { get; set; }
}
public class SelectedWalletViewModel : IWalletViewModel
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string CurrentBalance { get; set; }
}

因此,我创建了DisplayTemplates文件夹,为每个模型设置相应的.cshtml文件:

<!-- START OneSelectedWalletViewModel.cshtml -->
@using CLSoft.MyWallet.Models.Home
@model OneSelectedWalletViewModel
@Html.DisplayFor(m => m.SelectedWallet)
@Html.DisplayFor(m => m.OtherWallets)
<a asp-action="Index">Select all wallets</a>
<!-- END OneSelectedWalletViewModel.cshtml -->
<!-- START AllSelectedWalletsViewModel.cshtml -->
@using CLSoft.MyWallet.Models.Home
@model AllSelectedWalletsViewModel
@Html.DisplayFor(m => m.Wallets)
<!-- END AllSelectedWalletsViewModel.cshtml -->
<!-- START SelectedWalletViewModel.cshtml -->
@using CLSoft.MyWallet.Models.Home
@model SelectedWalletViewModel
<div class="card border-primary">
<div class="card-body">
<h5 class="card-title">@Model.Name</h5>
<h6 class="card-subtitle mb-2 text-muted">@Model.CurrentBalance</h6>
<p class="card-text">@Model.Description</p>
</div>
</div>
<!-- END SelectedWalletViewModel.cshtml -->
<!-- START WalletViewModel.cshtml -->
@using CLSoft.MyWallet.Models.Home
@model WalletViewModel
<div class="card">
<div class="card-body">
<h5 class="card-title">@Model.Name</h5>
<h6 class="card-subtitle mb-2 text-muted">@Model.CurrentBalance</h6>
<p class="card-text">@Model.Description</p>
<a asp-action="Index" asp-route-wallet-id="@Model.Id">select wallet</a>
</div>
</div>
<!-- END WalletViewModel.cshtml -->

现在我只错过了控制器的操作方法:

[HttpGet]
public async Task<IActionResult> Index(long? walletId)
{
var viewModel = await _service.GetDashboardViewModelAsync(walletId);
return View(viewModel);
}

和Index.chtml视图代码:

@using CLSoft.MyWallet.Models.Home
@model DashboardViewModel
@{
ViewData["Title"] = "Index";
}
<div class="row">
<div class="col">
<h4>Your wallets</h4>
@Html.DisplayFor(m => m.Wallets)
</div>
</div>

现在我应该都设置好了。问题是没有呈现标记。在测试过程中,我试图为界面ISelectedWalletsViewModel 添加一个显示模板

@model ISelectedWalletsViewModel
@Html.DisplayForModel()

在@Html.DisplayForModel((和-ta-dah上放置断点!-调试器停止了。但是,遗憾的是,没有调用具体的DisplayTemplate。我是不是错过了什么?

编辑1:我创建了一个github repo来显示问题。

第2版:我还报告了AspNet/Mvc存储库上的问题

原来这是经过设计的。我将在下面引用NTaylorMullen的答案,以防链接死亡(不太可能,但仍然如此(:

[…]所以关于这个问题,它实际上是经过设计的。当转到Core时,我们发现在遗留的ASP.NET中,我们有一个过度共享的坏习惯(使用意外的视图模型模板(。为了解决这个问题,我们删除了该功能,并要求用户在使用显示模板时明确使用的类型。对不起,我没有更好的答案了!

那么,应该如何重构代码以满足这些规则呢?

  1. [不太好]您可以将ViewModels重构为一个包含所有可能实现的视图模型,并在Views代码中使用if语句来呈现所需的视图
  2. [better]您可以重构控制器操作逻辑,以处理ViewModel的所有可能实现,并提供所需的View

我们在迁移到ASP.NET Core时遇到了同样的问题,最终使用了以下helper方法作为解决方法:

public static IHtmlContent MagicDisplayFor<TModel, TResult>(
this IHtmlHelper<TModel> helper,
Expression<Func<TModel, TResult>> expression)
{
// run the expression to get the actual object we want to render
var targetObject = expression.Compile()(helper.ViewData.Model);
// get the runtime type of the object (as opposed to the declared type TResult)
var actualTargetType = targetObject.GetType();
// render the object as usual, but override the template name using the target type
return helper.DisplayFor(expression, templateName: actualTargetType.Name);
}

然后,我们在一个需要这种行为的地方用@Html.MagicDisplayFor替换了@Html.DisplayFor

与直接使用@Html.DisplayFor相比,它增加了一些开销,可能还有一些边缘情况我们没有考虑,但它似乎对我们来说已经足够好了

相关内容

  • 没有找到相关文章

最新更新