dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.47k stars 4.76k forks source link

.NET 9 Blazor WebAssembly: Rendering exception due to recursive type definition #109931

Open zyreon-sg opened 2 weeks ago

zyreon-sg commented 2 weeks ago

Is there an existing issue for this?

Describe the bug

Upgrading my .NET 8 Blazor WebAssembly application to .NET 9 lead to frontend rendering issues. In my specific case, I'm using a type derived from Ardalis.SmartEnum to display certain information. The SmartEnum class by Ardalis uses a recursive type where it references the derived class itself as a type parameter.

The result of this behavior is a completely defective rendering of the respective page. The console states the following error:

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: Could not resolve field token 0x04000001, due to: Could not set up parent class, due to: Recursive type definition detected Ardalis.SmartEnum.SmartEnum`1 assembly:Ardalis.SmartEnum.dll type:SmartEnum`1 member:(null) assembly:Demo.dll type:TestEnum member:(null)
System.BadImageFormatException: Could not resolve field token 0x04000001, due to: Could not set up parent class, due to: Recursive type definition detected Ardalis.SmartEnum.SmartEnum`1 assembly:Ardalis.SmartEnum.dll type:SmartEnum`1 member:(null) assembly:Demo.dll type:TestEnum member:(null)
File name: 'Demo'
   at Microsoft.AspNetCore.Components.ComponentBase.<.ctor>b__7_0(RenderTreeBuilder builder)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)

Expected Behavior

Rendering of the page succeeds as with .NET 8.

Steps To Reproduce

  1. Create a Blazor WebAssembly Standalone App (using .NET 9 PWA with example pages)
  2. Include a reference to Ardalis.SmartEnum via NuGet
    <PackageReference Include="Ardalis.SmartEnum" Version="8.1.0" />
  3. Create a new class derived from SmartEnum
    
    using Ardalis.SmartEnum;

public class TestEnum(string name, int value) : SmartEnum(name, value) { public static readonly TestEnum Default = new(nameof(Default), 0); }

4. Append the following code to the `Home.razor` example page
```cs
@if (Test == TestEnum.Default)
{
    <p>Value of test enum is default</p>
}

@code {
    public TestEnum Test => TestEnum.Default;
}
  1. Build and start the application, which will result in a rending exception on load

Exceptions (if any)

System.BadImageFormatException

.NET Version

9.0

Anything else?

Issue at Ardalis.SmartEnum: https://github.com/ardalis/SmartEnum/issues/556

soenneker commented 2 weeks ago

This problem has stopped our .NET 8 -> 9 migration.

soenneker commented 2 weeks ago

@javiercn Could you or your team please take a look at this? This is blocking a large migration effort we have in progress. Thanks.

Or even some sort of workaround would be good...

DataByte-James commented 2 weeks ago

I have posted a temporary fix in the smartenum repository. The issue is the declaration of the array

static readonly Lazy<TEnum[]> _enumOptions = new Lazy<TEnum[]>(GetAllOptions, LazyThreadSafetyMode.ExecutionAndPublication);

I changed it to the below and it now works. Something odd there.

static readonly Lazy<List<TEnum>> _enumOptions = new Lazy<List<TEnum>>(GetAllOptions, LazyThreadSafetyMode.ExecutionAndPublication);

javiercn commented 1 week ago

@soenneker thanks for contacting us.

Does this happen during development or does it happen after you publish?

soenneker commented 1 week ago

@javiercn thanks for getting back. This happens during development (debug etc), and not only when published.

zyreon-sg commented 1 week ago

@javiercn Same for me. I can confirm that this issue exists with both local debugging and published applications (as tested with a solution being published to a Docker image using mcr.microsoft.com/dotnet/aspnet:9.0 as base).

javiercn commented 1 week ago

@slowfight @soenneker thanks for the additional details.

This seems like a runtime issue, as this worked in 8.0. I can't think of a reason why we would be able to cause a BadImage exception.

dotnet-policy-service[bot] commented 1 week ago

Tagging subscribers to 'arch-wasm': @lewing See info in area-owners.md if you want to be subscribed.

pavelsavara commented 5 days ago

Probably related to https://github.com/mono/mono/issues/15760

cc @BrzVlad

BrzVlad commented 5 days ago

Not convinced they are related. This reproduces on desktop and I confirm it is a runtime regression from net8.

BrzVlad commented 5 days ago

Regression caused by https://github.com/dotnet/runtime/commit/e5f0c361f5baea5e2b56e1776143d841b0cc6e6c