dotnet/aspnetcore

PublishTrimmed causes a runtime error related to source generation

iSeiryu opened this issue · 0 comments

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

A simple self-contained trimmed web app crashes with the following error on both Win and Linux using both dotnet 8 and 9-preview.

System.NotSupportedException: JsonTypeInfo metadata for type 'WeatherForecast[]' was not provided by TypeInfoResolver of type '[]'. If using source generation, ensure that all root types passed to the serializer have been annotated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically.

Removing /p:PublishTrimmed=true from the publish command works as expected.
I'm not sure if it's a bug or an expected behavior that is not properly documented. I found docs like this one https://learn.microsoft.com/en-us/dotnet/core/compatibility/serialization/7.0/reflection-fallback but

  1. I'm not creating any contexts manually
  2. The fallback doesn't seem to be available in dotnet 8 and 9.

Expected Behavior

http://localhost:5000/weatherforecast should return a valid JSON response.

Steps To Reproduce

On Windows

dotnet new webapi -n myapi --no-https
dotnet publish "myapi.csproj" -c Release -o /app/publish -r win-x64 /p:PublishTrimmed=true
cd bin\Release\net8.0\win-x64
.\awesome.exe

Via Docker

FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS publish
WORKDIR /src

RUN dotnet new webapi -n myapi --no-https
RUN dotnet publish "myapi/myapi.csproj" -c Release -o /app/publish -r linux-musl-x64 /p:PublishTrimmed=true

FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine AS prod
WORKDIR /app

EXPOSE 5000
COPY --from=publish /app/publish .

ENTRYPOINT ["./myapi", "--urls", "http://::5000"]

Removing /p:PublishTrimmed=true works as expected.

Instead of creating a new project this sample project can be used.

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

</Project>
var app = WebApplication.CreateBuilder(args).Build();

var summaries = new[]
{
    "Freezing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot"
};

app.MapGet("/weatherforecast", () =>
{
    var forecast =  Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast");

app.Run();

record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

Exceptions (if any)

System.NotSupportedException: JsonTypeInfo metadata for type 'WeatherForecast[]' was not provided by TypeInfoResolver of type '[]'. If using source generation, ensure that all root types passed to the serializer have been annotated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically.
         at System.Text.Json.ThrowHelper.ThrowNotSupportedException_NoMetadataForType(Type, IJsonTypeInfoResolver)
         at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type, Boolean , Nullable`1 , Boolean , Boolean )
         at System.Text.Json.JsonSerializerOptions.GetTypeInfo(Type)
         at Microsoft.AspNetCore.Http.Generated.<GeneratedRouteBuilderExtensions_g>F96C975B3E0B9F18FACA799FFF0501F8E65F4725A296BAA3D4A322AEDFF0D73FE__GeneratedRouteBuilderExtensionsCore.<>c.<MapGet0>b__2_1(Delegate del, RequestDelegateFactoryOptions options, RequestDelegateMetadataResult inferredMetadataResult) in /src/obj/Release/net8.0/linux-musl-x64/Microsoft.AspNetCore.Http.RequestDelegateGenerator/Microsoft.AspNetCore.Http.RequestDelegateGenerator.RequestDelegateGenerator/GeneratedRouteBuilderExtensions.g.cs:line 88
         at Microsoft.AspNetCore.Routing.RouteEndpointDataSource.CreateRouteEndpointBuilder(RouteEntry, RoutePattern , IReadOnlyList`1 , IReadOnlyList`1 )
         at Microsoft.AspNetCore.Routing.RouteEndpointDataSource.get_Endpoints()
         at Microsoft.AspNetCore.Routing.CompositeEndpointDataSource.CreateEndpointsUnsynchronized()
         at Microsoft.AspNetCore.Routing.CompositeEndpointDataSource.EnsureEndpointsInitialized()
         at Microsoft.AspNetCore.Routing.CompositeEndpointDataSource.get_Endpoints()
         at Microsoft.AspNetCore.Routing.DataSourceDependentCache`1.Initialize()
         at System.Threading.LazyInitializer.EnsureInitializedCore[T](T& , Boolean&, Object& , Func`1)
         at System.Threading.LazyInitializer.EnsureInitialized[T](T& , Boolean&, Object& , Func`1)
         at Microsoft.AspNetCore.Routing.DataSourceDependentCache`1.EnsureInitialized()
         at Microsoft.AspNetCore.Routing.Matching.DataSourceDependentMatcher..ctor(EndpointDataSource, Lifetime, Func`1)
         at Microsoft.AspNetCore.Routing.Matching.DfaMatcherFactory.CreateMatcher(EndpointDataSource)
         at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.InitializeCoreAsync()
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.<Invoke>g__AwaitMatcher|10_0(EndpointRoutingMiddleware, HttpContext, Task`1)
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1)

.NET Version

8.0.205

Anything else?

No response