Search code examples
c#genericsrazorblazor

Using Generic Types with Inheritance in Blazor Component


I'm trying to make a "generic list" component in Blazor, and want the component to be able to accept any object that derives from a base class. My code currently is as below;

Base Class:

    public class Model
    {

        // PK for the record
        [Key]
        public int Id { get; set; }

        // holds the front-end name for the record
        [Required(ErrorMessage = "Name is required")]
        public string Name { get; set; } = "";

        // holds the date and time the record was created
        [Required(ErrorMessage = "Created Timestamp is required")]
        public DateTime Created { get; set; } = DateTime.Now;

        // holds the username of the person who created the record
        [Required(ErrorMessage = "Creating user is required")]
        public string CreatedBy { get; set; } = "";


        // holds the date and time the record was updated
        public DateTime Updated { get; set; } = new DateTime(0);

        // holds the username of the person who last updated the record
        public string UpdatedBy { get; set; } = "";

    }

Derived Class:

public class ModelDesc: Model
    {

        // holds the description of the stakeholder
        public string Description { get; set; }

    }

The component is defined as below; DisplayGenericList.razor:

@typeparam T 


<h3>DisplayGenericList</h3>

@foreach (T lpObj in ListItems)
{
    <span>@lpObj.Name, @lpObj.Id</span>
}

@code {


    [Parameter]
    /// <summary>
    /// Contains the list of items to be shown in the list
    /// </summary>
    public List<T> ListItems { get; set; }
}

}

with DisplayGenericList.razor.cs as below;

    public partial class DisplayGenericList<T> where T:Model
    {
    }

This alone compiles OK, however, when I try and use the component on a page I get the following error; CS0314 The type 'T' cannot be used as type parameter 'T' in the generic type or method 'DisplayGenericList<T>'. There is no boxing conversion or type parameter conversion from 'T' to 'ProjectName.Data.Interfaces.Model'.

The index.razor is just this;

@page "/"

@using ProjectName.Data.Models

@using ProjectName.Shared.Components.Generics

<h1>Hello, world!</h1>

Welcome to your new app.



<hr />

<DisplayGenericList ListItems="lItems" />


@code
{

    private List<ModelDesc> lItems = new List<ModelDesc>()
    {
        new ModelDesc() {Name = "Item 1", Description = "Description 1", Created = DateTime.Now, CreatedBy = "User 1"},
        new ModelDesc() {Name = "Item 2", Description = "Description 2", Created = DateTime.Now, CreatedBy = "User 1"},
        new ModelDesc() {Name = "Item 3", Description = "Description 3", Created = DateTime.Now, CreatedBy = "User 2"},
        new ModelDesc() {Name = "Item 4", Description = "Description 4", Created = DateTime.Now, CreatedBy = "User 2"},
        new ModelDesc() {Name = "Item 5", Description = "Description 5", Created = DateTime.Now, CreatedBy = "User 3"}
    };

}

I'm fairly new to Blazor, and so i suspect i'm doing something wrong, however can anyone advise on what I should be doing here to enforce (and use) the constraint on the component?


Solution

  • A workaround is to add the TypeParam explicitly in the component usage; so the same code as in the example in the original question, but changing the index.razor implementation of the component as follows;

    <DisplayGenericList ListItems="lItems" T="ModelDesc" />
    

    This has been flagged as a feature request, as @Vencovsky above has found, and while type constraints are supported in the underlying Blazor classes, the compiler is unable to enforce the constraint without the type being explicitly defined as per above.