Closed
Description
The issue is best demonstrated with code. The response bodies for each route handler are written as comments.
var app = WebApplication.Create(args);
app.MapGet("/", Parent () => new Child()); // { "c": "c", "p": "p" }
app.MapGet("/task", () => Task.FromResult<Parent>(new Child())); // { "p": "p" }
app.MapGet("/valuetask", ValueTask<Parent> () => new(new Child())); // { "p": "p" }
app.MapGet("/taskobj", () => Task.FromResult<object>(new Child())); // { "c": "c", "p": "p" }
app.MapGet("/valuetaskobj", ValueTask<object> () => new(new Child())); // { "c": "c", "p": "p" }
app.Run();
record Parent(string P = "p");
record Child(string C = "c") : Parent;
This shows an inconsistency in how returned objects are serialized in async methods that return a Task<Parent>
or ValueTask<Parent>
vs non-async methods that return Parent
when the runtime type is a child type.
The non-async route handler has the right behavior because it results in a call to WriteAsJsonAsync<object>(...)
instead of WriteAsJsonAsync<Parent>(...)
which is what gets called in the async case. See https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-polymorphism