I'm using the Language-Ext library to create a generic union type that represents one of three states...
[Union]
public interface LoadingOption<T> {
LoadingOption<T> Loading();
LoadingOption<T> Loaded(T value);
LoadingOption<T> NotLoaded();
}
I then have a Blazor component that will show one of three render fragments, depending on which of the three states is represented by the instance of the union...
@typeparam T
@(Data switch {
Loading<T> => Loading,
Loaded<T> t => Loaded(t.Value),
NotLoaded<T> => NotLoaded,
_ => Loading
})
@code {
[Parameter]
public LoadingOption<T> Data { get; set; }
[Parameter]
public RenderFragment<Loading<T>> Loading { get; set; }
[Parameter]
public RenderFragment<Loaded<T>> Loaded { get; set; }
[Parameter]
public RenderFragment<NotLoaded<T>> NotLoaded { get; set; }
}
Note that the default case in the switch statement is needed to prevent an exception Non-exhaustive switch expression failed to match its input when the page firsts loads.
However, when I try to use this component, the HTML is never rendered. Instead, I see Microsoft.AspNetCore.Components.RenderFragment on the page, which sounds like it's treating the RenderFragment as a plain object, rather than as a RenderFragment
Sample usage of the Loader.razor component is as follows...
@page "/LoadingOptionSample"
<h3>Loader sample</h3>
<p><button class="btn btn-primary" @onclick="LoadCustomerFound">Existing customer</button>
<button class="btn btn-secondary" @onclick="LoadCustomerNotFound">No such customer</button></p>
<Loader Data="_customer" Context="customer">
<Loading>
<p>Please wait while we look for the customer...</p>
</Loading>
<Loaded>
<p>Found @customer.Value.Name (id: @customer.Value.Id), who is @customer.Value.Age years old</p>
</Loaded>
<NotLoaded>
<p>Big flop, we can't find him</p>
</NotLoaded>
</Loader>
@code {
private LoadingOption<Customer> _customer;
private async Task LoadCustomerFound() {
_customer = new Loading<Customer>();
// Simulate database access so we can see the Loading HTML...
await Task.Delay(2000);
_customer = new Loaded<Customer>(new Customer(1, "Jim Spriggs", 42));
}
private async Task LoadCustomerNotFound() {
_customer = new Loading<Customer>();
await Task.Delay(2000);
_customer = new NotLoaded<Customer>();
}
}
After clicking one of the buttons, the page looks like this...
Doesn't matter which of the buttons I click, the output is the same, and doesn't change. This supports my suspicion that Blazor isn't treating the RenderFragment as such, but treats it as a plain .NET object and renders it by calling ToString(), which is why I see the type, not the content.
If I do a non-generic version of this, it works fine.
Note that I have repeatedly restarted VS, as well as trying this in a separate project, but the result is always the same, so it seems the problem is encapsulated in the three files shown above.
Anyone any idea what I'm doing wrong? Thanks
Update I tried using a regular switch statement, instead of the switch expression, but it didn't make any difference. However, while testing this, I discovered that if I didn't have the switch at all, and simply did this...
@NotLoaded
...then I still got the type shown on the page.

[union]thing and how switch expressions work. Your return values are not all of the same type. Try a traditional switch statement.@NotLoaded(Data), although that might not compile.NotLoadedandLoadingdon't take a parameter, or at least they shouldn't. OnlyLoadedtakes a parameter, namely the entity that has been loaded. If you look at the[Union]code, you can see this. Not sure if I got theRenderFragmentdeclarations right in the Blazor component though. Maybe that's where it's going wrong. However, if that's the problem, I would have expected eitherNotLoadedandLoadingto work, orLoadedto work. As it is, all three just show theRenderFragment's type, as opposed to rendering it as I want. Thanks again, any more ideas?