How to use DisplayTemplates in ASP.Net Core Razor Pages with Model Inheritance?

4.3k Views Asked by At

Trying to get DisplayTemplates in ASP.Net Core 2.2 to work with classes that inherit from a base class similar to this question How to handle an entity model with inheritance in a view?

The Principal DisplayTemplate is being used for all items in the list, what am I missing?

PageModel

public class IndexModel : PageModel
{
    public List<Principal> Principals { get; set; } = new List<Principal>();

    public void OnGet()
    {
        Principals.Add(new Principal { Id = 1, Name = "Principal 1" });
        Principals.Add(new UserPrincipal { Id = 1, Name = "User 1", Age = 30 });
        Principals.Add(new GroupPrincipal { Id = 1, Name = "Group 1", Members = 5 });
        Principals.Add(new UserPrincipal { Id = 1, Name = "User 2", Age = 40 });
        Principals.Add(new GroupPrincipal { Id = 1, Name = "Group 2", Members = 3 });
    }
}

public class Principal
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class UserPrincipal : Principal
{
    public int Age { get; set; }
}

public class GroupPrincipal : Principal
{
    public int Members { get; set; }
}

RazorPage

@page
@model IndexModel

@foreach(var principal in Model.Principals)
{
    @Html.DisplayFor(p => principal)
}

~/Pages/Shared/DisplayTemplates/Principal.cshtml

@model Principal
<div>
    <h4>Principal</h4>
    @Model.Name
</div>

~/Pages/Shared/DisplayTemplates/UserPrincipal.cshtml

@model UserPrincipal
<div>
    <h4>User</h4>
    @Model.Name, Age @Model.Age
</div>

~/Pages/Shared/DisplayTemplates/GroupPrincipal.cshtml

@model GroupPrincipal
<div>
    <h4>Group</h4>
    @Model.Name, Members @Model.Members
</div>

1

There are 1 best solutions below

0
itminus On BEST ANSWER

The reason

The Principal DisplayTemplate is being used for all items in the list,

That's because the expression in @Html.DisplayFor(expression) won't be executed at all. They are parsed statically instead.

For example, if we have an expression m.a.b.c where a is null at runtime, the @Html.DisplayFor(m => m.a.b.c) is still able to know the template for c.

Since you're declaring the Principals as type List<Principal> , even if you make the Model.Principals hold UserPrincipal or GroupPrincipal at runtime, the "parser" still treat the principal as a base Principal type: They don't inspect the real type of instance. They just parse the type statically.

How to fix

Pass a template name when invoking Html.DisplayFor() so that the Html Helper knows the real template you want to use:

@page
@model IndexModel

@foreach (var principal in Model.Principals)
{
    @Html.DisplayFor(p => principal, principal.GetType().Name)
}

Demo

enter image description here