【问题标题】:Html.DisplayFor interface viewmodel in ASP.NET Core 2ASP.NET Core 2 中的 Html.DisplayFor 接口视图模型
【发布时间】:2018-07-12 09:15:31
【问题描述】:

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

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.cshtml 视图代码:

@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() 上放一个断点,然后 - 哒哒! - 调试器按原样停止。但是,唉,没有调用具体的 DisplayTemplate。我错过了什么吗?

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

EDIT 2:我也在AspNet/Mvc repository上报告了这个问题

【问题讨论】:

  • 您正在使用@Html.DisplayFor 来显示IEnumerable?另外,在控制器的return 行之前添加一个断点并检查viewModel 的内容
  • 我纠正了错字。我已经检查了 viewModel 的内容,这正是它所需要的。
  • 我编辑了代码以反映一个真实的例子
  • 另外,我尝试使用 if (Model is OneSelectedWalletViewModel) 检查 Index 视图中的模型类型,它返回了预期的结果。类型似乎没有在 DisplayFor 链中得到评估。
  • 我一直在 ASP.NET MVC 5 中使用 EditorFor 和 DisplayFor 的接口渲染具体的 ViewModel。对我来说,这是一个重大的退步。

标签: c# razor asp.net-core asp.net-core-mvc viewmodel


【解决方案1】:

原来这是by design。我将在下面引用 NTaylorMullen 的答案,以防链接失效(不太可能,但仍然如此):

[...] 所以关于这个问题,它实际上是设计使然。当迁移到 Core 时,我们发现在旧版 ASP.NET 中我们有过度共享的坏习惯(使用非预期的视图模型模板)。为了解决这个问题,我们删除了该功能,并要求用户在使用显示模板时明确他们使用的类型。对不起,我没有更好的答案给你!

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

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

【讨论】:

    【解决方案2】:

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

    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.DisplayFor 替换为 @Html.MagicDisplayFor

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

    【讨论】:

      猜你喜欢
      • 2011-01-14
      • 1970-01-01
      • 1970-01-01
      • 2022-09-27
      • 2020-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多