【问题标题】:Polymorphic ViewModel collection and rendering in MVC partial ViewsMVC 部分视图中的多态 ViewModel 集合和渲染
【发布时间】:2015-06-15 01:49:03
【问题描述】:

我的 MVC 应用程序中的 ViewModel 多态集合存在问题。我是通过 Web 服务调用收到的,我需要遍历它们并根据对象类型为它们提供自己的局部视图。

public abstract class ProvinceViewModel
{
    public string Code { get; set; }
}

public sealed class OntarioViewModel : ProvinceViewModel { }

public sealed class QuebecViewModel : ProvinceViewModel {}

在我看来,我正在尝试遍历它们并分配一个局部视图。我必须在这里进行大量类型转换才能使其正常工作。如果我尝试将其移至控制器操作并传入抽象类型,我将收到一个错误,即我们无法创建抽象类的实例。

ICollection<ProvinceViewModel>  ProvinceList; // collection receive via service

@for (int i = 0, c = ProvinceList.Count; i < c; i++)
{
    var currentProvince = this.Model.ElementAt(i);
    @switch (additionalRegistry.Code)
    {
        case "QC":
            @Html.Partial("AlbertaDetail", (QuebecViewModel)currentProvince)
            break;                              
        case "ON":
            @Html.Partial("OntarioDetail", (OntarioViewModel)currentProvince)
            break;
        default:
            @Html.Partial("ProvinceDetail", ProvinceViewModel)
            break;
    }
}

我有强类型 View,所以我可以访问不同的属性。

我将如何以更优雅的方式解决这个问题?我是否需要为抽象类创建一个新的代理基类以便更轻松地创建它的实例?

【问题讨论】:

  • 您可以让ProvinceDetail 页面进行切换以适当地对待不同的省份吗?如果您在其他地方使用这些,这将使您保留该页面上的逻辑,而不必重复它
  • 这个不清楚:你需要为每个concrete type的ProvinceDetail,还是每个instantiation实例化一个局部视图?例如,您会有多个部分 QuebecDetail 视图,这似乎很奇怪。
  • 对不起,我试图简化示例。我需要为每个具有不同属性的省份实例化一个局部视图。例如安大略省、魁北克省、马尼托巴省、不列颠哥伦比亚省都有一个特定的和不同的局部视图,其他一切都得到一个标准的默认通用视图,即。省详情。结果将是主页上的省份列表及其详细信息。

标签: c# asp.net-mvc polymorphism abstract-class


【解决方案1】:

您可以使用显示模板来实现此目的。在 Controller 的 Views 目录中的 DisplayTemplates 文件夹中为每种类型创建一个显示模板:

+-- Views
    +-- Provinces
        +-- DisplayTemplates
            +-- OntarioViewModel.cshtml
            +-- QuebecViewModel.cshtml

在视图中使用 DisplayFor 帮助器显示每个模型:

@model ICollection<ProvinceViewModel>

@foreach (var province in Model)
{
    @Html.DisplayFor(_ => province)
}

【讨论】:

    【解决方案2】:

    在过去遇到同样的问题时,我创建了以下解决方案:

    首先,用ExportMetadata 属性装饰您的(具体)视图模型,该属性表示要使用的视图名称。例如:

    [ExportMetadata("View", "Ontario")]
    public sealed class OntarioViewModel : ProvinceViewModel { }
    
    [ExportMetadata("View", "Quebec")]
    public sealed class QuebecViewModel : ProvinceViewModel {}
    

    然后使用以下Partial 方法扩展您的HtmlHelper

    public static MvcHtmlString Partial<T>(this HtmlHelper htmlHelper, T model, string prefix = null)
    {
        var modelType = typeof (T);
        var partialAttr = modelType.GetCustomAttributes<ExportMetadataAttribute>().SingleOrDefault(x => x.Name == "View");
    
        if (partialAttr == null)
            throw new Exception(modelType.Name + " doesn't define any view to be used");
    
        var partialName = (prefix ?? String.Empty) + partialAttr.Value;
    
        return htmlHelper.Partial(partialName, model, htmlHelper.ViewData);
    }
    

    然后使用它:

    @Html.Partial(currentProvince);
    

    如果您的部分文件位于某个子目录中:

    @Html.Partial(currentProvince, "Partials/")
    

    (如果您在注册自定义 HTML 帮助程序时需要帮助,请参阅 https://stackoverflow.com/a/5052790

    【讨论】:

      【解决方案3】:

      我有类似的要求,这就是我设法解决此问题的方法。 我的视图模型(BusinessEventEmailViewModel)有一个在运行时统一解析的接口列表(IBusinessEventEmail)。 IBusinessEventEmail 有一个 EventCode 属性。

      public class BusinessEventEmailViewModel : MailHeaderViewModel
      {
          #region members
          public List<IBusinessEventEmail> Events { get; set; }
      

      在我看来,我使用命名约定来渲染局部视图:

      Html.RenderPartial("~/Views/Shared/Email/_" + businessEvent.EventCode + ".cshtml", businessEvent);
      

      然后,我有一个 XXXEventEmail 使用 EventCode XXX 和一个部分视图 _XXX.cshtml 实现 IBusinessEventEmail

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-18
        • 2018-01-18
        • 1970-01-01
        相关资源
        最近更新 更多