How to make a generic type that must inherit from a class with generic type in Blazor?

Viewed 2725

I'm trying to make a class that have 3 generic types and one of the types must inherit from that other class that have the other 2 generic types.

What I really need is that IForm inherits from FormComponent so it have all of it's methods implemented.

Form.razor

@typeparam TForm
@typeparam TModel
@typeparam TResult

<EditForm Model="@Model" OnValidSubmit="@HandleValidSubmit">
    @ChildContent
</EditForm>

Form.razor.cs

public partial class Form<TForm, TModel, TResult>  
    where TForm : FormComponent<TModel, TResult>
{

    [Parameter]
    public TForm FormRef { get; set; }

    [Parameter]
    public EventCallback<TResult> OnValidSubmit { get; set; }

    [Parameter]
    public TModel Model { get; set; }

    // ... 
    // some other properties
}

FormComponent.cs

public abstract class FormComponent<TModel, TResult> : BaseDomComponent
{        
    [CascadingParameter]
    public TModel Model { get; set; }

    public abstract TResult OnValidSubmit();

    // ... 
    // some other properties   
}

Code from where I use Form

<Form Model="@Entity" FormRef="@_formRef" OnValidSubmit="@HandleValidSubmit">
    <XYZForm @ref="_formRef" />
    <Button ButtonType="ButtonType.Submit">Submit</Button>
</Form>

But this gives me the error

Error CS0314 The type 'TForm' cannot be used as type parameter 'TForm' in the generic type or method 'Form< TForm, TModel, TResult >'. There is no boxing conversion or type parameter conversion from 'TForm' to 'Core.Web.Base.FormComponent< TModel, TResult >'

I searched this error in alot of places but didn't found any case where there was 3 generic types. I also read the docs but couldn't use that to solve my case.

3 Answers

Just find out this issue is about blazor not supporting type constraints yet.

@juharr asked if I was using it in another place. I said no, but acctually I'm using it in the blazor component, and it doesn't have that support yet ;/

Make your generic component a class component only. That way you can add your constraints

I think it's worth sharing the workaround I found - simply add a code-behind file to your component and specify the constraints there.

Credits to this answer that led me to success...

Steps to add the constraint to MyComponent.razor:

  1. Add -> New -> Class, name the file MyComponent.razor.cs and make sure it is created in the same folder as the MyComponent.razor. If all works fine, the file should be added as a sub-file or MyComponent.razor.cs...

  2. Open MyComponent.razor.cs. Make sure that the namespace is the same as the component (should be ok if the class was created in the same folder)

  3. Mark the class as public partial and add your constraints

    // make sure namespaces match public partial class MyComponent where TItem : MyBaseClass, new() {

And that's it. And even better, I can now inject a generic service to my component to the MyComponent.razor component:

@typeparam TItem

@inject MyGenericService<TItem> _genericService;

Note that I registered the service in Startup.cs as a singleton (didn't test scoped but should work as well)...

// --- `Startup.cs` code
services.AddSingleton(new MyGenericService<MyObject>());

Hope this works for others, it made my generic CRUD forms SO MUCH easier to deal with and saved huge amount of duplicate code and weird switches to infert type from generic control!

Related