在视图模型中使用继承在 MVC 中抽象视图

本文关键字:视图 MVC 抽象 继承 模型 | 更新日期: 2023-09-27 18:34:34

我需要一些关于在这种情况下如何进行的建议。假设我有一个视图,它呈现一个子部分视图和一个孙子部分视图。一个部分视图称为_CommentsContainer,而另一个称为_Comment_CommentsContainer是循环访问IPagedList<IComment>并为每个视图模型吐出_Comment的子项。

/// <summary>
/// Represents an object that is able to hold comments or can be commented on. E.G. a photo, a video, a bulletin
/// , or even another comment
/// </summary>
public interface ICommentable
{
   IPagedList<IComment> Comments { get; set; }
}  

public interface IComment
{
    Guid Id { get; set; }
    string CommentingUsername { get; set; }
    Guid CommentingUserId { get; set; }
    DateTime? PostDate { get; set; }
    string Content { get; set; }
    Guid? InResposeToCommentId { get; set; }
}  

如果我想在我的_CommentsContainer中有一个大的switch语句,我该如何解释继承此ICommentable但实现自己的属性集的视图模型,这些属性不属于它继承的接口的一部分?

@model Jdmxchange_V3.Models.Abstractions.Interfaces.ICommentable
@if (Model.GetType() == typeof(Jdmxchange_V3.Models.BulletinViewModels.BulletinDetailsViewModel))
{
<div class="row">
    <div class="panel panel-default recent-listings">
        <div class="panel-heading">Conversation</div>
        <div class="panel-body">
            @foreach (var comment in Model.Comments)
            {
                @Html.Partial("_Comment", comment)
            }
        </div>
    </div>
</div>
}
else if (Model.GetType() == typeof(Jdmxchange_V3.Models.EventDetailsViewModel))
{
<div class="row">
    <div class="panel panel-default recent-listings">
        <div class="panel-heading">Conversation @Model.EventSubTitle@*<< this property is not in ICommentable*@ </div>
        <div class="panel-body">
            @foreach (var comment in Model.Comments)
            {
                @Html.Partial("_Comment", comment)
            }
        </div>
    </div>
</div>
}  

如您所见,最后一个else if引用了ICommentable上找不到的属性。我可以简单地将此属性添加到ICommentable并使所有派生类都需要它,但是如果我不需要它在每个派生类上并且只需要在一个类上使用它怎么办?我的问题是

在这种情况下,如何在分部视图中提供某种类型的泛型类型,检查其类型,并引用该特定类型的属性?

最初的想法是这不可能做到,我从未见过它完成,也想不出类似的情况可以做到。

在视图模型中使用继承在 MVC 中抽象视图

我根本不会使用这种方法。 相反,我会使用编辑器/显示模板,因为它们处理模型/属性的实际类型,而不是@model语句中定义的内容。

例如,您可以在此处创建两个试探:

~/views/Shared/DisplayTemplates/BulletinDetailsViewModel.cshtml

@model BulletinViewModels.BulletinDetailsViewModel
<div class="row">
    <div class="panel panel-default recent-listings">
        <div class="panel-heading">Conversation</div>
        <div class="panel-body">
            @foreach (var comment in Model.Comments)
            {
                @Html.Partial("_Comment", comment)
            }
        </div>
    </div>
</div>

~/views/Shared/DisplayTemplates/EventDetailsViewModel.cshtml

@model BulletinViewModels.EventDetailsViewModel
<div class="row">
    <div class="panel panel-default recent-listings">
        <div class="panel-heading">Conversation @Model.EventSubTitle@</div>
        <div class="panel-body">
            @foreach (var comment in Model.Comments)
            {
                @Html.Partial("_Comment", comment)
            }
        </div>
    </div>
</div>

然后,您只需在主视图中看到:

@model Jdmxchange_V3.Models.Abstractions.Interfaces.ICommentable
@Html.DisplayForModel()

或者你可以使用

@Html.DisplayFor(m => m)

您还可以使用编辑器模板(而不是部分(进行注释。 EditorTemplates 会自动遍历 IEnumerables,因此您只需要执行以下操作:

@Html.DisplayFor(m => m.Comments)

不幸的是,类型没有switch语句。 最好的办法是像你正在做的那样比较类型,尽管通过is比较来做并利用多态性可能更具可读性,如下所示:

@using Jdmxchange_V3.Models
@if (Model is BulletinViewModels.BulletinDetailsViewModel)
{
    var bdvm = Model as BulletinViewModels.BulletinDetailsViewModel;
    // do things specific to this type
}
else if(Model is EventDetailsViewModel)
{
    var edvm = Model as EventDetailsViewModel;
    // do things specific to this type
}

你没有要求这一点,但我还是会把它放在这里:我建议你可能真的不需要孙子部分。您可以通过消除孙视图并使_CommentsContainer部分视图呈现所有内容来节省一些分配(和垃圾回收负载(。 如果您的网站没有很多用户或长评论线程,那么它可能对性能无关紧要,您应该继续保持原样。