Skip to content

Commit

Permalink
Language updates (#33587)
Browse files Browse the repository at this point in the history
  • Loading branch information
guardrex authored Sep 11, 2024
1 parent 7ea657c commit 1b1fa24
Show file tree
Hide file tree
Showing 74 changed files with 194 additions and 196 deletions.
2 changes: 1 addition & 1 deletion aspnetcore/blazor/advanced-scenarios.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ This is a trivial example. In more realistic cases with complex and deeply neste
### Guidance and conclusions

* App performance suffers if sequence numbers are generated dynamically.
* The framework can't create its own sequence numbers automatically at runtime because the necessary information doesn't exist unless it's captured at compile time.
* The necessary information doesn't exist to permit the framework to generate sequence numbers automatically at runtime unless the information is captured at compile time.
* Don't write long blocks of manually-implemented <xref:Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder> logic. Prefer `.razor` files and allow the compiler to deal with the sequence numbers. If you're unable to avoid manual <xref:Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder> logic, split long blocks of code into smaller pieces wrapped in <xref:Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.OpenRegion%2A>/<xref:Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.CloseRegion%2A> calls. Each region has its own separate space of sequence numbers, so you can restart from zero (or any other arbitrary number) inside each region.
* If sequence numbers are hardcoded, the diff algorithm only requires that sequence numbers increase in value. The initial value and gaps are irrelevant. One legitimate option is to use the code line number as the sequence number, or start from zero and increase by ones or hundreds (or any preferred interval).
* For loops, the sequence numbers should increase in your source code, not in terms of runtime behavior. The fact that, at runtime, the numbers repeat is how the diffing system realises you're in a loop.
Expand Down
6 changes: 3 additions & 3 deletions aspnetcore/blazor/blazor-ef-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,15 @@ The grid, add, and view components use the "context-per-operation" pattern, wher
## Database access

EF Core relies on a <xref:Microsoft.EntityFrameworkCore.DbContext> as the means to [configure database access](/ef/core/miscellaneous/configuring-dbcontext) and act as a [*unit of work*](https://martinfowler.com/eaaCatalog/unitOfWork.html). EF Core provides the <xref:Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.AddDbContext%2A> extension for ASP.NET Core apps that registers the context as a *scoped* service by default. In server-side Blazor apps, scoped service registrations can be problematic because the instance is shared across components within the user's circuit. <xref:Microsoft.EntityFrameworkCore.DbContext> isn't thread safe and isn't designed for concurrent use. The existing lifetimes are inappropriate for these reasons:
EF Core relies on a <xref:Microsoft.EntityFrameworkCore.DbContext> as the means to [configure database access](/ef/core/miscellaneous/configuring-dbcontext) and act as a [*unit of work*](https://martinfowler.com/eaaCatalog/unitOfWork.html). EF Core provides the <xref:Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.AddDbContext%2A> extension for ASP.NET Core apps that registers the context as a *scoped* service. In server-side Blazor apps, scoped service registrations can be problematic because the instance is shared across components within the user's circuit. <xref:Microsoft.EntityFrameworkCore.DbContext> isn't thread safe and isn't designed for concurrent use. The existing lifetimes are inappropriate for these reasons:

* **Singleton** shares state across all users of the app and leads to inappropriate concurrent use.
* **Scoped** (the default) poses a similar issue between components for the same user.
* **Transient** results in a new instance per request; but as components can be long-lived, this results in a longer-lived context than may be intended.

The following recommendations are designed to provide a consistent approach to using EF Core in server-side Blazor apps.

* By default, consider using one context per operation. The context is designed for fast, low overhead instantiation:
* Consider using one context per operation. The context is designed for fast, low overhead instantiation:

```csharp
using var context = new MyContext();
Expand Down Expand Up @@ -135,7 +135,7 @@ The following recommendations are designed to provide a consistent approach to u

Place operations after the `Loading = true;` line in the `try` block.

Loading logic doesn't require locking database records because thread safety isn't a concern. The loading logic is used to disable UI controls so that users don't inadvertently select buttons or update fields while data is fetched.
Thread safety isn't a concern, so loading logic doesn't require locking database records. The loading logic is used to disable UI controls so that users don't inadvertently select buttons or update fields while data is fetched.

* If there's any chance that multiple threads may access the same code block, [inject a factory](#scope-to-the-component-lifetime) and make a new instance per operation. Otherwise, injecting and using the context is usually sufficient.

Expand Down
8 changes: 4 additions & 4 deletions aspnetcore/blazor/call-web-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Calls a todo list web API from a Blazor WebAssembly app:

Server-based components call external web APIs using <xref:System.Net.Http.HttpClient> instances, typically created using <xref:System.Net.Http.IHttpClientFactory>. For guidance that applies to server-side apps, see <xref:fundamentals/http-requests>.

A server-side app doesn't include an <xref:System.Net.Http.HttpClient> service by default. Provide an <xref:System.Net.Http.HttpClient> to the app using the [`HttpClient` factory infrastructure](xref:fundamentals/http-requests).
A server-side app doesn't include an <xref:System.Net.Http.HttpClient> service. Provide an <xref:System.Net.Http.HttpClient> to the app using the [`HttpClient` factory infrastructure](xref:fundamentals/http-requests).

In the `Program` file:

Expand Down Expand Up @@ -188,7 +188,7 @@ For an additional working example, see the server-side file upload example that
When using the interactive WebAssembly and Auto render modes, components are prerendered by default. Auto components are also initially rendered interactively from the server before the Blazor bundle downloads to the client and the client-side runtime activates. This means that components using these render modes should be designed so that they run successfully from both the client and the server. If the component must call a server project-based API or transform a request to an external web API (one that's outside of the Blazor Web App) when running on the client, the recommended approach is to abstract that API call behind a service interface and implement client and server versions of the service:

* The client version calls the web API with a preconfigured <xref:System.Net.Http.HttpClient>.
* The server version can typically access the server-side resources directly. Injecting an <xref:System.Net.Http.HttpClient> on the server that makes calls back to the server is ***not*** recommended, as the network request is typically unnecessary. Alternatively, the API might be external to the server project, but a service abstraction for the server is required to transform the request in some way, for example to add an access token to a proxied request.
* The server version can typically access the server-side resources directly. Injecting an <xref:System.Net.Http.HttpClient> on the server that makes calls back to the server isn't recommended, as the network request is typically unnecessary. Alternatively, the API might be external to the server project, but a service abstraction for the server is required to transform the request in some way, for example to add an access token to a proxied request.

When using the WebAssembly render mode, you also have the option of disabling prerendering, so the components only render from the client. For more information, see <xref:blazor/components/render-modes#prerendering>.

Expand All @@ -214,11 +214,11 @@ Use ***either*** of the following approaches:
builder.Services.AddHttpClient();
```

No explicit package reference is required because <xref:System.Net.Http.HttpClient> services are provided by the shared framework.
<xref:System.Net.Http.HttpClient> services are provided by the shared framework, so a package reference in the app's project file isn't required.

Example: Todo list web API in the `BlazorWebAppCallWebApi` [sample app](#sample-apps)

* If prerendering isn't required for a WebAssembly component that calls the web API, disable prerendering by following the guidance in <xref:blazor/components/render-modes#prerendering>. If you adopt this approach, you don't need to add <xref:System.Net.Http.HttpClient> services to the main project of the Blazor Web App because the component won't be prerendered on the server.
* If prerendering isn't required for a WebAssembly component that calls the web API, disable prerendering by following the guidance in <xref:blazor/components/render-modes#prerendering>. If you adopt this approach, you don't need to add <xref:System.Net.Http.HttpClient> services to the main project of the Blazor Web App because the component isn't prerendered on the server.

For more information, see [Client-side services fail to resolve during prerendering](xref:blazor/components/render-modes#client-side-services-fail-to-resolve-during-prerendering).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ Blazor Web Apps provide alternative approaches for cascading values that apply m
```

> [!NOTE]
> Wrapping the `Routes` component instance in the `App` component (`Components/App.razor`) with a [`CascadingValue`](xref:Microsoft.AspNetCore.Components.CascadingValue%601) component is ***not*** supported.
> Wrapping the `Routes` component instance in the `App` component (`Components/App.razor`) with a [`CascadingValue`](xref:Microsoft.AspNetCore.Components.CascadingValue%601) component isn't supported.
* Specify a *root-level cascading value* as a service by calling the <xref:Microsoft.Extensions.DependencyInjection.CascadingValueServiceCollectionExtensions.AddCascadingValue%2A> extension method on the service collection builder.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Static SSR is a mode in which components run when the server handles an incoming

The benefit of this mode is cheaper, more scalable hosting, because no ongoing server resources are required to hold component state, no ongoing connection must be maintained between browser and server, and no WebAssembly payload is required in the browser.

By default, all existing components can still be used with static SSR. However, the cost of this mode is that event handlers, such as `@onclick`&dagger;, can't be run for the following reasons:
All existing components can still be used with static SSR. However, the cost of this mode is that event handlers, such as `@onclick`&dagger;, can't be run for the following reasons:

* There's no .NET code in the browser to run them.
* The server has immediately discarded any component and renderer state that would be needed to execute event handlers or to rerender the same component instances.
Expand Down Expand Up @@ -123,7 +123,7 @@ The <xref:Microsoft.AspNetCore.Components.Forms.EditForm.Enhance%2A>, <xref:Micr

## Avoid APIs that are specific to static SSR

To make a reusable component that works across all render modes, don't rely on <xref:Microsoft.AspNetCore.Http.HttpContext> because it's only available during static SSR. The <xref:Microsoft.AspNetCore.Http.HttpContext> API doesn't make sense to use during interactive rendering because there's no active HTTP request in flight at those times. It's meaningless to think about setting a status code or writing to the response.
<xref:Microsoft.AspNetCore.Http.HttpContext> is only available during static SSR, so don't rely on <xref:Microsoft.AspNetCore.Http.HttpContext> when creating components that work across render modes. The <xref:Microsoft.AspNetCore.Http.HttpContext> API doesn't make sense to use during interactive rendering either because there's no active HTTP request in flight at those times. It's meaningless to think about setting a HTTP status code or writing to the HTTP response.

Reusable components are free to receive an <xref:Microsoft.AspNetCore.Http.HttpContext> when available, as follows:

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/components/class-libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Just as components are regular .NET types, components provided by an RCL are nor
1. Create a new project.
1. In the **Create a new project** dialog, select **Razor Class Library** from the list of ASP.NET Core project templates. Select **Next**.
1. In the **Configure your new project** dialog, provide a project name in the **Project name** field. Examples in this topic use the project name `ComponentLibrary`. Select **Next**.
1. In the **Additional information** dialog, do ***not*** select **Support pages and views**. Select **Create**.
1. In the **Additional information** dialog, don't select **Support pages and views**. Select **Create**.
1. Add the RCL to a solution:
1. Open the solution.
1. Right-click the solution in **Solution Explorer**. Select **Add** > **Existing Project**.
Expand Down
10 changes: 5 additions & 5 deletions aspnetcore/blazor/components/css-isolation.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ h1 {
## CSS isolation bundling

CSS isolation occurs at build time. Blazor rewrites CSS selectors to match markup rendered by the component. The rewritten CSS styles are bundled and produced as a static asset. The stylesheet is referenced inside the `<head>` tag ([location of `<head>` content](xref:blazor/project-structure#location-of-head-and-body-content)). The following `<link>` element is added by default to an app created from the Blazor project templates:
CSS isolation occurs at build time. Blazor rewrites CSS selectors to match markup rendered by the component. The rewritten CSS styles are bundled and produced as a static asset. The stylesheet is referenced inside the `<head>` tag ([location of `<head>` content](xref:blazor/project-structure#location-of-head-and-body-content)). The following `<link>` element is added to an app created from the Blazor project templates:

:::moniker range=">= aspnetcore-9.0"

Expand Down Expand Up @@ -112,7 +112,7 @@ At build time, a project bundle is created with the convention `obj/{CONFIGURATI

## Child component support

By default, CSS isolation only applies to the component you associate with the format `{COMPONENT NAME}.razor.css`, where the `{COMPONENT NAME}` placeholder is usually the component name. To apply changes to a child component, use the `::deep` [pseudo-element](https://developer.mozilla.org/docs/Web/CSS/Pseudo-elements) to any descendant elements in the parent component's `.razor.css` file. The `::deep` pseudo-element selects elements that are *descendants* of an element's generated scope identifier.
CSS isolation only applies to the component you associate with the format `{COMPONENT NAME}.razor.css`, where the `{COMPONENT NAME}` placeholder is usually the component name. To apply changes to a child component, use the `::deep` [pseudo-element](https://developer.mozilla.org/docs/Web/CSS/Pseudo-elements) to any descendant elements in the parent component's `.razor.css` file. The `::deep` pseudo-element selects elements that are *descendants* of an element's generated scope identifier.

The following example shows a parent component called `Parent` with a child component called `Child`.

Expand Down Expand Up @@ -168,7 +168,7 @@ However, excluding the `div` element removes the descendant relationship. In the
<Child />
```

The `::deep` pseudo-element affects where the scope attribute is applied to the rule. When you define a CSS rule in a scoped CSS file, the scope is applied to the right most element by default. For example: `div > a` is transformed to `div > a[b-{STRING}]`, where the `{STRING}` placeholder is a ten-character string generated by the framework (for example, `b-3xxtam6d07`). If you instead want the rule to apply to a different selector, the `::deep` pseudo-element allows you do so. For example, `div ::deep > a` is transformed to `div[b-{STRING}] > a` (for example, `div[b-3xxtam6d07] > a`).
The `::deep` pseudo-element affects where the scope attribute is applied to the rule. When you define a CSS rule in a scoped CSS file, the scope is applied to the right most element. For example: `div > a` is transformed to `div > a[b-{STRING}]`, where the `{STRING}` placeholder is a ten-character string generated by the framework (for example, `b-3xxtam6d07`). If you instead want the rule to apply to a different selector, the `::deep` pseudo-element allows you do so. For example, `div ::deep > a` is transformed to `div[b-{STRING}] > a` (for example, `div[b-3xxtam6d07] > a`).

The ability to attach the `::deep` pseudo-element to any HTML element allows you to create scoped CSS styles that affect elements rendered by other components when you can determine the structure of the rendered HTML tags. For a component that renders an hyperlink tag (`<a>`) inside another component, ensure the component is wrapped in a `div` (or any other element) and use the rule `::deep > a` to create a style that's only applied to that component when the parent component renders.

Expand All @@ -189,7 +189,7 @@ CSS isolation is designed to work out-of-the-box but provides configuration for

:::moniker range=">= aspnetcore-8.0"

By default, scope identifiers use the format `b-{STRING}`, where the `{STRING}` placeholder is a ten-character string generated by the framework. To customize the scope identifier format, update the project file to a desired pattern:
Scope identifiers use the format `b-{STRING}`, where the `{STRING}` placeholder is a ten-character string generated by the framework. To customize the scope identifier format, update the project file to a desired pattern:

```xml
<ItemGroup>
Expand Down Expand Up @@ -220,7 +220,7 @@ Use the wildcard (`*`) operator to share scope identifiers across multiple files

:::moniker range="< aspnetcore-8.0"

By default, scope identifiers use the format `b-{STRING}`, where the `{STRING}` placeholder is a ten-character string generated by the framework. To customize the scope identifier format, update the project file to a desired pattern:
Scope identifiers use the format `b-{STRING}`, where the `{STRING}` placeholder is a ten-character string generated by the framework. To customize the scope identifier format, update the project file to a desired pattern:

```xml
<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/components/data-binding.md
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ Consider the following component, where an `<input>` element is bound to an `int

:::moniker-end

By default, binding applies to the element's `onchange` event. If the user updates the value of the text box's entry to `123.45` and changes the focus, the element's value is reverted to `123` when `onchange` fires. When the value `123.45` is rejected in favor of the original value of `123`, the user understands that their value wasn't accepted.
Binding applies to the element's `onchange` event. If the user updates the value of the text box's entry to `123.45` and changes the focus, the element's value is reverted to `123` when `onchange` fires. When the value `123.45` is rejected in favor of the original value of `123`, the user understands that their value wasn't accepted.

For the `oninput` event (`@bind:event="oninput"`), a value reversion occurs after any keystroke that introduces an unparsable value. When targeting the `oninput` event with an `int`-bound type, a user is prevented from typing a dot (`.`) character. A dot (`.`) character is immediately removed, so the user receives immediate feedback that only whole numbers are permitted. There are scenarios where reverting the value on the `oninput` event isn't ideal, such as when the user should be allowed to clear an unparsable `<input>` value. Alternatives include:

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/components/dynamiccomponent.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ The remaining three shared components (`VirginGalactic3`, `UnitedLaunchAlliance3
@inject ILogger<{COMPONENT TYPE}> Logger
```

* Each component implements a `Log` method. There's no need to identify the component in the log message template because the log category written by the logger includes the fully-qualified name of the component type when <xref:Microsoft.Extensions.Logging.LoggerExtensions.LogInformation%2A> is called:
* Each component implements a `Log` method. The log category written by the logger includes the fully-qualified name of the component type when <xref:Microsoft.Extensions.Logging.LoggerExtensions.LogInformation%2A> is called:

```razor
@code {
Expand Down
Loading

0 comments on commit 1b1fa24

Please sign in to comment.