Skip to content

Commit 9271252

Browse files
Merge pull request #34752 from dotnet/main
Merge to Live
2 parents 051c691 + dd546b6 commit 9271252

File tree

12 files changed

+171
-31
lines changed

12 files changed

+171
-31
lines changed

aspnetcore/blazor/call-web-api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ The preceding example sets the base address with `builder.HostEnvironment.BaseAd
379379
The most common use cases for using the client's own base address are:
380380

381381
* The client project (`.Client`) of a Blazor Web App (.NET 8 or later) makes web API calls from WebAssembly components or code that runs on the client in WebAssembly to APIs in the server app.
382-
* The client project (**:::no-loc text="Client":::**) of a hosted Blazor WebAssembly app makes web API calls to the server project (**:::no-loc text="Server":::**). Note that the hosted Blazor WebAssembly project template is no longer available in .NET 8 or later. However, hosted Blazor WebAssembly apps remain supported for .NET 8.
382+
* The client project (**:::no-loc text="Client":::**) of a hosted Blazor WebAssembly app makes web API calls to the server project (**:::no-loc text="Server":::**). Note that the Hosted Blazor WebAssembly project template is no longer available in .NET 8 or later. However, hosted Blazor WebAssembly apps remain supported for .NET 8.
383383

384384
If you're calling an external web API (not in the same URL space as the client app), set the URI to the web API's base address. The following example sets the base address of the web API to `https://localhost:5001`, where a separate web API app is running and ready to respond to requests from the client app:
385385

aspnetcore/blazor/fundamentals/environments.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ Using the `environment` property overrides the environment set by the [`Blazor-E
115115

116116
The preceding approach sets the client's environment without changing the `Blazor-Environment` header's value, nor does it change the server project's console logging of the startup environment for a Blazor Web App that has adopted global Interactive WebAssembly rendering.
117117

118-
To log the environment to the console in either a standalone Blazor WebAssembly project or the `.Client` project of a Blazor Web App, place the following C# code in the `Program` file after the <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHost> is created with <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.CreateDefault%2A?displayProperty=nameWithType> and before the line that builds and runs the project (`await builder.Build().RunAsync();`):
118+
To log the environment to the console in either a standalone Blazor WebAssembly app or the `.Client` project of a Blazor Web App, place the following C# code in the `Program` file after the <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHost> is created with <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.CreateDefault%2A?displayProperty=nameWithType> and before the line that builds and runs the project (`await builder.Build().RunAsync();`):
119119

120120
```csharp
121121
Console.WriteLine(

aspnetcore/blazor/host-and-deploy/configure-trimmer.md

Lines changed: 135 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ This article explains how to control the Intermediate Language (IL) Trimmer when
1616

1717
Blazor WebAssembly performs [Intermediate Language (IL)](/dotnet/standard/glossary#il) trimming to reduce the size of the published output. Trimming occurs when publishing an app.
1818

19-
Trimming may have detrimental effects for the published app. In apps that use [reflection](/dotnet/csharp/advanced-topics/reflection-and-attributes/), the IL Trimmer often can't determine the required types for runtime reflection and trim them away. For example, complex framework types for JS interop, such as <xref:System.Collections.Generic.KeyValuePair>, might be trimmed and not available at runtime for JS interop calls. In these cases, we recommend creating your own custom types instead. The IL Trimmer is also unable to react to an app's dynamic behavior at runtime. To ensure the trimmed app works correctly once deployed, test published output frequently while developing.
20-
2119
## Configuration
2220

2321
To configure the IL Trimmer, see the [Trimming options](/dotnet/core/deploying/trimming/trimming-options) article in the .NET Fundamentals documentation, which includes guidance on the following subjects:
@@ -42,6 +40,141 @@ The default trimmer granularity for Blazor apps is `partial`. To trim all assemb
4240

4341
For more information, see [Trimming options (.NET documentation)](/dotnet/core/deploying/trimming/trimming-options#trimming-granularity).
4442

43+
## Failure to preserve types used by a published app
44+
45+
Trimming may have detrimental effects for a published app leading to runtime errors. In apps that use [reflection](/dotnet/csharp/advanced-topics/reflection-and-attributes/), the IL Trimmer often can't determine the required types for runtime reflection and trims them away or trims away parameter names from methods. This can happen with complex framework types used for JS interop, JSON serialization/deserialization, and other operations.
46+
47+
The IL Trimmer is also unable to react to an app's dynamic behavior at runtime. To ensure the trimmed app works correctly once deployed, test published output frequently while developing.
48+
49+
Consider the following client-side component in a Blazor Web App (ASP.NET Core 8.0 or later) that deserializes a <xref:System.Collections.Generic.KeyValuePair> collection (`List<KeyValuePair<string, string>>`):
50+
51+
```razor
52+
@rendermode @(new InteractiveWebAssemblyRenderMode(false))
53+
@using System.Diagnostics.CodeAnalysis
54+
@using System.Text.Json
55+
56+
<dl>
57+
@foreach (var item in @items)
58+
{
59+
<dt>@item.Key</dt>
60+
<dd>@item.Value</dd>
61+
}
62+
</dl>
63+
64+
@code {
65+
private List<KeyValuePair<string, string>> items = [];
66+
67+
[StringSyntax(StringSyntaxAttribute.Json)]
68+
private const string data =
69+
"""[{"key":"key 1","value":"value 1"},{"key":"key 2","value":"value 2"}]""";
70+
71+
protected override void OnInitialized()
72+
{
73+
JsonSerializerOptions options = new() { PropertyNameCaseInsensitive = true };
74+
75+
items = JsonSerializer
76+
.Deserialize<List<KeyValuePair<string, string>>>(data, options)!;
77+
}
78+
}
79+
```
80+
81+
The preceding component executes normally when the app is run locally and produces the following rendered definition list (`<dl>`):
82+
83+
> **:::no-loc text="key 1":::**
84+
> :::no-loc text="value 1":::
85+
> **:::no-loc text="key 2":::**
86+
> :::no-loc text="value 2":::
87+
88+
When the app is published, <xref:System.Collections.Generic.KeyValuePair> is trimmed from the app, even in spite of setting the [`<PublishTrimmed>` property](#configuration) to `false` in the project file. Accessing the component throws the following exception:
89+
90+
> :::no-loc text="Unhandled exception rendering component: ConstructorContainsNullParameterNames, System.Collections.Generic.KeyValuePair`2[System.String,System.String]":::
91+
92+
To address lost types, consider the following approaches.
93+
94+
### Preserve the type as a dynamic dependency
95+
96+
We recommend creating a dynamic dependency to preserve the type with the [`[DynamicDependency]` attribute](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute).
97+
98+
If not already present, add an `@using` directive for <xref:System.Diagnostics.CodeAnalysis?displayProperty=fullName>:
99+
100+
```razor
101+
@using System.Diagnostics.CodeAnalysis
102+
```
103+
104+
Add a [`[DynamicDependency]` attribute](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute) to preserve the <xref:System.Collections.Generic.KeyValuePair>:
105+
106+
```diff
107+
+ [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(KeyValuePair<string, string>))]
108+
private List<KeyValuePair<string, string>> items = [];
109+
```
110+
111+
<!-- UPDATE 10.0 - Hold this for https://github.com/dotnet/aspnetcore/issues/52947
112+
113+
### Use a Root Descriptor
114+
115+
A [Root Descriptor](/dotnet/core/deploying/trimming/trimming-options#root-descriptors) can preserve the type.
116+
117+
Add an `ILLink.Descriptors.xml` file to the root of the app&dagger; with the type:
118+
119+
```xml
120+
<linker>
121+
<assembly fullname="System.Runtime">
122+
<type fullname="System.Collections.Generic.KeyValuePair`2">
123+
<method signature="System.Void .ctor(TKey,TValue)" />
124+
</type>
125+
</assembly>
126+
</linker>
127+
```
128+
129+
&dagger;The root of the app refers to the root of the Blazor WebAssembly app or the root of the `.Client` project of a Blazor Web App (.NET 8 or later).
130+
131+
Add a `TrimmerRootDescriptor` item to the app's project file&Dagger; referencing the `ILLink.Descriptors.xml` file:
132+
133+
```xml
134+
<ItemGroup>
135+
<TrimmerRootDescriptor Include="$(MSBuildThisFileDirectory)ILLink.Descriptors.xml" />
136+
</ItemGroup>
137+
```
138+
139+
&Dagger;The project file is either the project file of the Blazor WebAssembly app or the project file of the `.Client` project of a Blazor Web App (.NET 8 or later).
140+
141+
-->
142+
143+
### Custom types
144+
145+
<!-- UPDATE 10.0 - We'll hold this for when the file descriptor approach comes back.
146+
147+
Custom types aren't trimmed by Blazor when an app is published, but we recommend [preserving types as dynamic dependencies](#preserve-the-type-as-a-dynamic-dependency) instead of creating custom types.
148+
149+
-->
150+
151+
The following modifications create a `StringKeyValuePair` type for use by the component.
152+
153+
`StringKeyValuePair.cs`:
154+
155+
```csharp
156+
[method: SetsRequiredMembers]
157+
public sealed class StringKeyValuePair(string key, string value)
158+
{
159+
public required string Key { get; init; } = key;
160+
public required string Value { get; init; } = value;
161+
}
162+
```
163+
164+
The component is modified to use the `StringKeyValuePair` type:
165+
166+
```diff
167+
- private List<KeyValuePair<string, string>> items = [];
168+
+ private List<StringKeyValuePair> items = [];
169+
```
170+
171+
```diff
172+
- items = JsonSerializer.Deserialize<List<KeyValuePair<string, string>>>(data, options)!;
173+
+ items = JsonSerializer.Deserialize<List<StringKeyValuePair>>(data, options)!;
174+
```
175+
176+
Because custom types are never trimmed by Blazor when an app is published, the component works as designed after the app is published.
177+
45178
## Additional resources
46179

47180
* [Trim self-contained deployments and executables](/dotnet/core/deploying/trimming/trim-self-contained)

aspnetcore/blazor/javascript-interoperability/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ Blazor uses <xref:System.Text.Json?displayProperty=fullName> for serialization w
201201
* Global default serialization isn't customizable to avoid breaking existing component libraries, impacts on performance and security, and reductions in reliability.
202202
* Serializing .NET member names results in lowercase JSON key names.
203203
* JSON is deserialized as <xref:System.Text.Json.JsonElement> C# instances, which permit mixed casing. Internal casting for assignment to C# model properties works as expected in spite of any case differences between JSON key names and C# property names.
204-
* Complex framework types, such as <xref:System.Collections.Generic.KeyValuePair>, might be [trimmed away by the IL Trimmer on publish](xref:blazor/host-and-deploy/configure-trimmer) and not present for JS interop. We recommend creating custom types for types that the IL Trimmer trims away.
204+
* Complex framework types, such as <xref:System.Collections.Generic.KeyValuePair>, might be [trimmed away by the IL Trimmer on publish](xref:blazor/host-and-deploy/configure-trimmer#failure-to-preserve-types-used-by-a-published-app) and not present for JS interop or JSON serialization/deserialization. We recommend creating custom types for types that the IL Trimmer trims away.
205205
* Blazor always relies on [reflection for JSON serialization](/dotnet/standard/serialization/system-text-json/reflection-vs-source-generation), including when using C# [source generation](/dotnet/csharp/roslyn-sdk/source-generators-overview). Setting `JsonSerializerIsReflectionEnabledByDefault` to `false` in the app's project file results in an error when serialization is attempted.
206206

207207
<xref:System.Text.Json.Serialization.JsonConverter> API is available for custom serialization. Properties can be annotated with a [`[JsonConverter]` attribute](xref:System.Text.Json.Serialization.JsonConverterAttribute) to override default serialization for an existing data type.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
> [!IMPORTANT]
2+
> The Hosted Blazor WebAssembly project template was removed from the framework with the release of .NET 8 (November, 2023). The guidance in this article is only supported for .NET 7 or earlier. Hosted Blazor WebAssembly apps that are upgraded each release continue to receive product support. Alternatively, refactor the app into either a standalone Blazor WebAssembly app or a Blazor Web App.

aspnetcore/blazor/security/webassembly/hosted-with-azure-active-directory-b2c.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ uid: blazor/security/webassembly/hosted-with-azure-active-directory-b2c
1010
---
1111
# Secure a hosted ASP.NET Core Blazor WebAssembly app with Azure Active Directory B2C
1212

13+
[!INCLUDE[](~/blazor/security/includes/hosted-blazor-webassembly-notice.md)]
14+
1315
This article explains how to create a [hosted Blazor WebAssembly solution](xref:blazor/hosting-models#blazor-webassembly) that uses [Azure Active Directory (AAD) B2C](/azure/active-directory-b2c/overview) for authentication.
1416

1517
For additional security scenario coverage after reading this article, see <xref:blazor/security/webassembly/additional-scenarios>.

aspnetcore/blazor/security/webassembly/hosted-with-identity-server.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ uid: blazor/security/webassembly/hosted-with-identity-server
1010
---
1111
# Secure a hosted ASP.NET Core Blazor WebAssembly app with Identity Server
1212

13+
[!INCLUDE[](~/blazor/security/includes/hosted-blazor-webassembly-notice.md)]
14+
1315
This article explains how to create a [hosted Blazor WebAssembly solution](xref:blazor/hosting-models#blazor-webassembly) that uses [Duende Identity Server](https://docs.duendesoftware.com) to authenticate users and API calls.
1416

1517
:::moniker range=">= aspnetcore-6.0"

aspnetcore/blazor/security/webassembly/hosted-with-microsoft-entra-id.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ uid: blazor/security/webassembly/hosted-with-microsoft-entra-id
1010
---
1111
# Secure a hosted ASP.NET Core Blazor WebAssembly app with Microsoft Entra ID
1212

13+
[!INCLUDE[](~/blazor/security/includes/hosted-blazor-webassembly-notice.md)]
14+
1315
This article explains how to create a [hosted Blazor WebAssembly solution](xref:blazor/hosting-models#blazor-webassembly) that uses [Microsoft Entra ID (ME-ID)](https://azure.microsoft.com/services/active-directory/) for authentication. This article focuses on a single tenant app with a single tenant Azure app registration.
1416

1517
This article doesn't cover a *multi-tenant ME-ID registration*. For more information, see [Making your application multi-tenant](/entra/identity-platform/howto-convert-app-to-be-multi-tenant).

aspnetcore/blazor/security/webassembly/microsoft-entra-id-groups-and-roles-net-5-to-7.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ zone_pivot_groups: blazor-groups-and-roles
1111
---
1212
# Microsoft Entra (ME-ID) groups, Administrator Roles, and App Roles (.NET 5 to .NET 7)
1313

14-
> [!NOTE]
14+
> [!IMPORTANT]
1515
> This isn't the latest version of this article. For the current ASP.NET Core release, see the latest version of <xref:blazor/security/webassembly/meid-groups-roles>.
1616
17+
[!INCLUDE[](~/blazor/security/includes/hosted-blazor-webassembly-notice.md)]
18+
1719
This article explains how to configure Blazor WebAssembly to use Microsoft Entra ID groups and roles.
1820

1921
Microsoft Entra (ME-ID) provides several authorization approaches that can be combined with ASP.NET Core Identity:

aspnetcore/blazor/tooling.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ Select **Next**.
6565
:::moniker range=">= aspnetcore-8.0"
6666

6767
> [!NOTE]
68-
> The hosted Blazor WebAssembly project template isn't available in ASP.NET Core 8.0 or later. To create a hosted Blazor WebAssembly app, a **Framework** option earlier than .NET 8.0 must be selected with the **ASP.NET Core Hosted** checkbox.
68+
> The Hosted Blazor WebAssembly project template isn't available in ASP.NET Core 8.0 or later. To create a hosted Blazor WebAssembly app, a **Framework** option earlier than .NET 8.0 must be selected with the **ASP.NET Core Hosted** checkbox.
6969
7070
:::moniker-end
7171

@@ -329,7 +329,7 @@ Create a new project:
329329
```
330330

331331
> [!NOTE]
332-
> The hosted Blazor WebAssembly project template isn't available in ASP.NET Core 8.0 or later. To create a hosted Blazor WebAssembly app using a .NET 8.0 or later SDK, pass the `-f|--framework` option with a 7.0 target framework (`net7.0`):
332+
> The Hosted Blazor WebAssembly project template isn't available in ASP.NET Core 8.0 or later. To create a hosted Blazor WebAssembly app using a .NET 8.0 or later SDK, pass the `-f|--framework` option with a 7.0 target framework (`net7.0`):
333333
>
334334
> ```dotnet cli
335335
> dotnet new blazorwasm -o BlazorApp -ho -f net7.0

0 commit comments

Comments
 (0)