dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.48k stars 10.03k forks source link

Blazor DeserializeNoConstructor #48745

Closed chenryIDSTC closed 1 year ago

chenryIDSTC commented 1 year ago

Is there an existing issue for this?

Describe the bug

Blazor .NET 7 web assembly:

I get the following exception when in Azure app service-hosted app. I do not get this exception when running locally in debug or release mode.

Here is the class being deserialized: namespace Flight.UI.Models.Product;

public class ProductTypeManagerDTO {

    public ProductTypeManagerDTO() {
        ProductTypeID = 0;
        Name = string.Empty;
        Description = string.Empty;
        AllowDelete = false;
    }

    public int ProductTypeID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public bool AllowDelete { get; set; }
}

referenced in razor/blazor page:

private ObservableCollection<ProductTypeManagerDTO>? productTypes;

protected override async Task OnInitializedAsync() {
  productTypes = await Http.GetFromJsonAsync<ObservableCollection<ProductTypeManagerDTO>> ("api/Products/GetProductTypesToManage");
}

Expected Behavior

The ObservableCollection is deserialized correctly.

Steps To Reproduce

No response

Exceptions (if any)

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: DeserializeNoConstructor, JsonConstructorAttribute, System.Collections.ObjectModel.ObservableCollection`1[Flight.UI.Models.Product.ProductTypeManagerDTO] Path: $ | LineNumber: 0 | BytePositionInLine: 1.

.NET Version

7.0.203

Anything else?

dotnet --info .NET SDK: Version: 7.0.203 Commit: 5b005c19f5

Runtime Environment: OS Name: Windows OS Version: 10.0.14393 OS Platform: Windows RID: win10-x86 Base Path: C:\Program Files (x86)\dotnet\sdk\7.0.203\

Host: Version: 7.0.5 Architecture: x86 Commit: 8042d61b17

.NET SDKs installed: 1.1.14 [C:\Program Files (x86)\dotnet\sdk] 2.1.526 [C:\Program Files (x86)\dotnet\sdk] 2.2.109 [C:\Program Files (x86)\dotnet\sdk] 3.1.118 [C:\Program Files (x86)\dotnet\sdk] 3.1.426 [C:\Program Files (x86)\dotnet\sdk] 5.0.408 [C:\Program Files (x86)\dotnet\sdk] 6.0.406 [C:\Program Files (x86)\dotnet\sdk] 6.0.408 [C:\Program Files (x86)\dotnet\sdk] 7.0.102 [C:\Program Files (x86)\dotnet\sdk] 7.0.203 [C:\Program Files (x86)\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.All 2.1.30 [C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.2.14 [C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.30 [C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.2.14 [C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.0.3 [C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.32 [C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.17 [C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.14 [C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.16 [C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.2 [C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.5 [C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 1.0.16 [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 1.1.13 [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.0.9 [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.30 [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.2.14 [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.0.3 [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.32 [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.17 [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.14 [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.16 [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.2 [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.5 [C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App]

Other architectures found: x64 [C:\Program Files (x86)\dotnet]

javiercn commented 1 year ago

@chenryIDSTC thanks for contacting us.

Your constructor is very likely being trimmed. You can disable trimming to confirm that's the case.

ghost commented 1 year ago

Hi @chenryIDSTC. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

chenryIDSTC commented 1 year ago

@chenryIDSTC thanks for contacting us.

Your constructor is very likely being trimmed. You can disable trimming to confirm that's the case.

Hi Javier,

Thanks for the quick feedback. You were correct. I had tried setting BlazorWebAssemblyEnableLinking to false, but that did not work. Disabling trimming worked by adding this to the blazor client project file:

<PublishTrimmed>false</PublishTrimmed>

I was also able to isolate the issue to the ObservableCollection by swapping it out for a List. I could not replicate the issue even with trimming on if I used a List as opposed to the ObservableCollection. Is there a more elegant solution I should be looking for to still allow trimming but not lose the deserialization of ObservableCollection? I am new to Blazor (loving it so far!) and I don't want to cut any corners on my new project.

ghost commented 1 year ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

surayya-MS commented 1 year ago

@eerhardt what do you think about this?

eerhardt commented 1 year ago

Is there a more elegant solution I should be looking for to still allow trimming but not lose the deserialization of ObservableCollection?

You should use the Json Source Generator to do JSON serialization/deserialization. It is the only reliable way of doing JSON serialization/deserialization in a trimmed app.

See https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/source-generation. In your code above:

protected override async Task OnInitializedAsync() {
  productTypes = await Http.GetFromJsonAsync<ObservableCollection<ProductTypeManagerDTO>> ("api/Products/GetProductTypesToManage");
}

You would pass in the JsonTypeInfo from the source generator context into the GetFromJsonAsync method. See this overload: https://learn.microsoft.com/en-us/dotnet/api/system.net.http.json.httpclientjsonextensions.getfromjsonasync?view=net-8.0#system-net-http-json-httpclientjsonextensions-getfromjsonasync-1(system-net-http-httpclient-system-string-system-text-json-serialization-metadata-jsontypeinfo((-0))-system-threading-cancellationtoken).

chenryIDSTC commented 1 year ago

Thanks for the direction! I was able to get the ObservableCollection to compile without disabling trimming with this :

[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ObservableCollection<ProductAttributeManagerDTO>))]
public partial class JsonTypeContext: JsonSerializerContext {

}