From 5329939391ce03ae4ab875fbd4ca30f60679220c Mon Sep 17 00:00:00 2001 From: Rainer Stropek Date: Tue, 3 Apr 2018 07:56:19 +0200 Subject: [PATCH 01/10] Add chapter about dependency injection in Blazor --- docs/dependency-injection.md | 190 +++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 docs/dependency-injection.md diff --git a/docs/dependency-injection.md b/docs/dependency-injection.md new file mode 100644 index 0000000..3bce52e --- /dev/null +++ b/docs/dependency-injection.md @@ -0,0 +1,190 @@ +--- +title: Dependency injection in Blazor +author: rstropek +description: Learn how Blazor apps can use built-in services by having them injected into components. +manager: wpickett +ms.author: riande +ms.custom: mvc +ms.date: 04/09/2018 +ms.prod: asp.net-core +ms.technology: aspnet +ms.topic: article +uid: client-side/blazor/dependency-injection +--- +# Dependency injection in Blazor + +By [Rainer Stropek](https://www.timecockpit.com) + +[!INCLUDE[](~/includes/blazor-preview-notice.md)] + +Blazor has [dependency injection (DI)](https://docs.microsoft.com/aspnet/core/fundamentals/dependency-injection) built-in. Blazor apps can use built-in services by having them injected into components. Blazor apps can also define custom services and make them available via DI. + +## What is dependency injection? + +DI is a technique for achieving loose coupling between objects. The goal is to have high-level components depend on abstractions (for example, [interfaces](https://docs.microsoft.com/dotnet/csharp/programming-guide/interfaces/)) and not on low level components. + +Imagine you want to create an application service, `DataAccess`, for data access. Blazor components use this service to obtain data from backend servers using web APIs. The component should *not* create an instance of `DataAccess`. Instead, the app should create an abstraction, `IDataAccess`, that `DataAccess` implements. Blazor components that require data access features use the `IDataAccess` abstraction. Components never directly reference `DataAccess`. Therefore, components that depend on data access features are independent of the concrete implementation, and they can work with different implementations of `IDataAccess` (for example, a [mock object](https://wikipedia.org/wiki/Mock_object) in a unit test). + +Blazor's DI system is responsible for connecting component abstractions with their concrete implementations. To connect the abstractions with the implementations, DI is configured during startup of the app. An example is shown later in this topic. + +## Use of existing .NET mechanisms + +DI in Blazor is based on .NET's [System.IServiceProvider](https://docs.microsoft.com/dotnet/api/system.iserviceprovider) interface. It defines a generic mechanism for retrieving a service object in .NET apps. + +Blazor's implementation of `System.IServiceProvider` obtains its services from an underlying [Microsoft.Extensions.DependencyInjection.IServiceCollection](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.iservicecollection). + +## Add services to DI + +After creating a new Blazor app, examine the `Main` method in *Program.cs*: + +```csharp +static void Main(string[] args) +{ + var serviceProvider = new BrowserServiceProvider(configure => + { + // Add any custom services here + }); + + new BrowserRenderer(serviceProvider).AddComponent("app"); +} +``` + +`BrowserServiceProvider` receives an action with which you can add your app services to Blazor's DI. `configure` is referencing the underlying `IServiceCollection`, which is a list of service descriptor objects ([Microsoft.Extensions.DependencyInjection.ServiceDescriptor](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.servicedescriptor)). Services are added by providing service descriptors to the service collection. The following is a code sample demonstrating the concept: + +```csharp +static void Main(string[] args) +{ + var serviceProvider = new BrowserServiceProvider(configure => + { + configure.Add(ServiceDescriptor.Singleton()); + }); + + new BrowserRenderer(serviceProvider).AddComponent("app"); +} +``` + +`ServiceDescriptor` offers various overloads of three different functions that can be used to add services to Blazor's DI: + +| Method | Description | +| ----------- | ----------- | +| `Singleton` | Blazor creates a *single instance* of your service. All components requiring this service receive a reference to this instance. | +| `Transient` | Whenever a component requires this service, it receives a *new instance* of the service. | +| `Scoped` | Blazor doesn't currently have the concept of DI scopes. `Scoped` behaves like `Singleton`. Therefore, prefer `Singleton` and avoid `Scoped`. | + +## System services + +Blazor provides default services that are automatically added to the service collection of a Blazor app. The following table shows a list of the default services currently provided by Blazor. + +| Method | Description | +| ------------ | ----------- | +| `IUriHelper` | Helpers for working with URIs and navigation state (singleton). | +| `HttpClient` | Provides methods for sending HTTP requests and receiving HTTP responses from a resource identified by a URI (singleton). Note that this instance of [System.Net.Http.HttpClient](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient) is using the browser for handling the HTTP traffic in the background. Its [BaseAddress](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient.baseaddress) is automatically set to the base URI prefix of the Blazor app. | + +## Request a service in a component + +Once services are added to the service collection, they can be injected into the components' Razor templates using the `@inject` Razor keyword. `@inject` has two parameters: + +* Type name: The type of the service to inject. +* Property name: The name of the property receiving the injected app service. Note that the property doesn't require manual creation. The Blazor compiler creates the property. + +Multiple `@inject` statements can be used to inject different services. + +The following example shows how to use `@inject`. The service implementing `Services.IDataAccess` is injected into the component's property `DataRepository`. Note how the code is only using the `IDataAccess` abstraction: + +```csharp +@page "/customer-list" +@using Services +@inject IDataAccess DataRepository + +
    + @if (Customers != null) + { + @foreach (var customer in Customers) + { +
  • @customer.FirstName @customer.LastName
  • + } + } +
+ +@functions { + private IReadOnlyList Customers; + + protected override async Task OnInitAsync() + { + // The property DataRepository has received an implemenation + // of IDataAccess through dependency injection. We can use + // it to get data from our server. + Customers = await DataRepository.GetAllCustomersAsync(); + } +} +``` + +Internally, the Blazor-generated property (`DataRepository`) is decorated with the `Microsoft.AspNetCore.Blazor.Components.InjectAttribute` attribute. Typically, this attribute isn't used directly. If a base class is required for Blazor components and injected properties are also required for the base class, `InjectAttribute` can be manually added: + +```csharp +public class ComponentBase : BlazorComponent +{ + // Note that Blazor's dependency injection works even if using the + // InjectAttribute in a component's base class. + [Inject] + protected IDataAccess DataRepository { get; set; } + ... +} +``` + +In components derived from the base class, the `@inject` directive isn't required. The `InjectAttribute` of the base class is satisfactory: + +```csharp +@page "/demo" +@inherits ComponentBases + +

...

+... +``` + +## Dependency injection in services + +Complex services might require additional services. In the prior example, `DataAccess` might require Blazor's default service `HttpClient`. `@inject` or the `InjectAttribute` can't be used in services. *Constructor injection* must be used instead. Require services are added by adding parameters to the service's constructor. When dependency injection creates the service, it recognizes the services it requires in the constructor and provides them accordingly. + +The following code sample demonstrates the concept: + +```csharp +public class DataAccess : IDataAccess +{ + // Note that the constructor receives an HttpClient via dependency + // injection. HttpClient is a default service offered by Blazor. + public Repository(HttpClient client) + { + ... + } + ... +} +``` + +Note the following prerequisites for constructor injection: + +* There must be one constructor whose arguments can all be fulfilled by dependency injection. Note that additional parameters not covered by DI are allowed if default values are specified for them. +* The applicable constructor must be *public*. +* There must only be one applicable constructor. In case of an ambiguity, DI throws an exception. + +## Service lifetime + +Note that Blazor doesn't automatically dispose injected services that implement `IDisposable`. Blazor components can implement `IDisposable`. Services are disposed when the user navigates away from the component. + +The following code sample demonstrates how to implement `IDisposable` in a Blazor component: + +```csharp +... +@using System; +@implements IDisposable +... + +@functions { + ... + public void Dispose() + { + // Add code for disposing here + ... + } +} +``` From 1af0fc43097fabe0740a419c31b2076d5b8a34d2 Mon Sep 17 00:00:00 2001 From: Rainer Stropek Date: Thu, 5 Apr 2018 11:30:51 +0200 Subject: [PATCH 02/10] Update DI chapter --- docs/dependency-injection.md | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/docs/dependency-injection.md b/docs/dependency-injection.md index 3bce52e..09d87ab 100644 --- a/docs/dependency-injection.md +++ b/docs/dependency-injection.md @@ -17,15 +17,16 @@ By [Rainer Stropek](https://www.timecockpit.com) [!INCLUDE[](~/includes/blazor-preview-notice.md)] -Blazor has [dependency injection (DI)](https://docs.microsoft.com/aspnet/core/fundamentals/dependency-injection) built-in. Blazor apps can use built-in services by having them injected into components. Blazor apps can also define custom services and make them available via DI. +Blazor has [dependency injection (DI)](https://docs.microsoft.com/aspnet/core/fundamentals/dependency-injection) built-in. Apps can use built-in services by having them injected into components. Apps can also define custom services and make them available via DI. ## What is dependency injection? -DI is a technique for achieving loose coupling between objects. The goal is to have high-level components depend on abstractions (for example, [interfaces](https://docs.microsoft.com/dotnet/csharp/programming-guide/interfaces/)) and not on low level components. +DI is a technique for accessing services configured in a central location. This can be useful to: -Imagine you want to create an application service, `DataAccess`, for data access. Blazor components use this service to obtain data from backend servers using web APIs. The component should *not* create an instance of `DataAccess`. Instead, the app should create an abstraction, `IDataAccess`, that `DataAccess` implements. Blazor components that require data access features use the `IDataAccess` abstraction. Components never directly reference `DataAccess`. Therefore, components that depend on data access features are independent of the concrete implementation, and they can work with different implementations of `IDataAccess` (for example, a [mock object](https://wikipedia.org/wiki/Mock_object) in a unit test). +* Share a single instance of a service class across many components (known as a *singleton* service) +* Decouple components from particular concrete service classes and only reference abstractions. For example, there might be an interface `IDataAccess` implemented by a concrete class `DataAccess`. If a component uses DI to receive an `IDataAccess` implementation, it isn't coupled to any concrete type. That means the implementation could easily be swapped, perhaps to a mock implementation in unit tests. -Blazor's DI system is responsible for connecting component abstractions with their concrete implementations. To connect the abstractions with the implementations, DI is configured during startup of the app. An example is shown later in this topic. +Blazor's DI system is responsible for supplying instances of services to components. It also resolves dependencies recursively, so that services themselves can depend on further services, and so on. DI is configured during startup of the app. An example is shown later in this topic. ## Use of existing .NET mechanisms @@ -35,7 +36,7 @@ Blazor's implementation of `System.IServiceProvider` obtains its services from a ## Add services to DI -After creating a new Blazor app, examine the `Main` method in *Program.cs*: +After creating a new app, examine the `Main` method in *Program.cs*: ```csharp static void Main(string[] args) @@ -49,7 +50,7 @@ static void Main(string[] args) } ``` -`BrowserServiceProvider` receives an action with which you can add your app services to Blazor's DI. `configure` is referencing the underlying `IServiceCollection`, which is a list of service descriptor objects ([Microsoft.Extensions.DependencyInjection.ServiceDescriptor](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.servicedescriptor)). Services are added by providing service descriptors to the service collection. The following is a code sample demonstrating the concept: +`BrowserServiceProvider` receives an action with which you can add your app services to DI. `configure` references the underlying `IServiceCollection`, which is a list of service descriptor objects ([Microsoft.Extensions.DependencyInjection.ServiceDescriptor](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.servicedescriptor)). Services are added by providing service descriptors to the service collection. The following is a code sample demonstrating the concept: ```csharp static void Main(string[] args) @@ -67,25 +68,27 @@ static void Main(string[] args) | Method | Description | | ----------- | ----------- | -| `Singleton` | Blazor creates a *single instance* of your service. All components requiring this service receive a reference to this instance. | +| `Singleton` | DI creates a *single instance* of your service. All components requiring this service receive a reference to this instance. | | `Transient` | Whenever a component requires this service, it receives a *new instance* of the service. | | `Scoped` | Blazor doesn't currently have the concept of DI scopes. `Scoped` behaves like `Singleton`. Therefore, prefer `Singleton` and avoid `Scoped`. | -## System services +## Default services -Blazor provides default services that are automatically added to the service collection of a Blazor app. The following table shows a list of the default services currently provided by Blazor. +Blazor provides default services that are automatically added to the service collection of an app. The following table shows a list of the default services currently provided by Blazor's `BrowserServiceProvider`. | Method | Description | | ------------ | ----------- | | `IUriHelper` | Helpers for working with URIs and navigation state (singleton). | -| `HttpClient` | Provides methods for sending HTTP requests and receiving HTTP responses from a resource identified by a URI (singleton). Note that this instance of [System.Net.Http.HttpClient](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient) is using the browser for handling the HTTP traffic in the background. Its [BaseAddress](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient.baseaddress) is automatically set to the base URI prefix of the Blazor app. | +| `HttpClient` | Provides methods for sending HTTP requests and receiving HTTP responses from a resource identified by a URI (singleton). Note that this instance of [System.Net.Http.HttpClient](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient) uses the browser for handling the HTTP traffic in the background. Its [BaseAddress](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient.baseaddress) is automatically set to the base URI prefix of the app. | + +Note that it is possible to use a custom services provider instead of the default `BrowserServiceProvider` which is added by the default template. A custom service provider would not automatically provide the default services mentioned above. They would have to be added to the new service provider explicitly. ## Request a service in a component Once services are added to the service collection, they can be injected into the components' Razor templates using the `@inject` Razor keyword. `@inject` has two parameters: * Type name: The type of the service to inject. -* Property name: The name of the property receiving the injected app service. Note that the property doesn't require manual creation. The Blazor compiler creates the property. +* Property name: The name of the property receiving the injected app service. Note that the property doesn't require manual creation. The compiler creates the property. Multiple `@inject` statements can be used to inject different services. @@ -119,7 +122,7 @@ The following example shows how to use `@inject`. The service implementing `Serv } ``` -Internally, the Blazor-generated property (`DataRepository`) is decorated with the `Microsoft.AspNetCore.Blazor.Components.InjectAttribute` attribute. Typically, this attribute isn't used directly. If a base class is required for Blazor components and injected properties are also required for the base class, `InjectAttribute` can be manually added: +Internally, the generated property (`DataRepository`) is decorated with the `Microsoft.AspNetCore.Blazor.Components.InjectAttribute` attribute. Typically, this attribute isn't used directly. If a base class is required for components and injected properties are also required for the base class, `InjectAttribute` can be manually added: ```csharp public class ComponentBase : BlazorComponent @@ -144,7 +147,7 @@ In components derived from the base class, the `@inject` directive isn't require ## Dependency injection in services -Complex services might require additional services. In the prior example, `DataAccess` might require Blazor's default service `HttpClient`. `@inject` or the `InjectAttribute` can't be used in services. *Constructor injection* must be used instead. Require services are added by adding parameters to the service's constructor. When dependency injection creates the service, it recognizes the services it requires in the constructor and provides them accordingly. +Complex services might require additional services. In the prior example, `DataAccess` might require Blazor's default service `HttpClient`. `@inject` or the `InjectAttribute` can't be used in services. *Constructor injection* must be used instead. Required services are added by adding parameters to the service's constructor. When dependency injection creates the service, it recognizes the services it requires in the constructor and provides them accordingly. The following code sample demonstrates the concept: @@ -169,9 +172,9 @@ Note the following prerequisites for constructor injection: ## Service lifetime -Note that Blazor doesn't automatically dispose injected services that implement `IDisposable`. Blazor components can implement `IDisposable`. Services are disposed when the user navigates away from the component. +Note that Blazor doesn't automatically dispose injected services that implement `IDisposable`. Components can implement `IDisposable`. Services are disposed when the user navigates away from the component. -The following code sample demonstrates how to implement `IDisposable` in a Blazor component: +The following code sample demonstrates how to implement `IDisposable` in a component: ```csharp ... From 1f893e561b9b5e0c8beb851fbb596f69e47dd16f Mon Sep 17 00:00:00 2001 From: Rainer Stropek Date: Thu, 5 Apr 2018 16:10:07 +0200 Subject: [PATCH 03/10] Fix typo --- docs/dependency-injection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dependency-injection.md b/docs/dependency-injection.md index 09d87ab..6ee312d 100644 --- a/docs/dependency-injection.md +++ b/docs/dependency-injection.md @@ -139,7 +139,7 @@ In components derived from the base class, the `@inject` directive isn't require ```csharp @page "/demo" -@inherits ComponentBases +@inherits ComponentBase

...

... From 87e38986a52b2bb1eb251088bb66a6a431995ece Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Thu, 5 Apr 2018 11:06:12 -0500 Subject: [PATCH 04/10] Minor grammatical updates --- docs/dependency-injection.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/dependency-injection.md b/docs/dependency-injection.md index 6ee312d..e9d2f98 100644 --- a/docs/dependency-injection.md +++ b/docs/dependency-injection.md @@ -23,14 +23,14 @@ Blazor has [dependency injection (DI)](https://docs.microsoft.com/aspnet/core/fu DI is a technique for accessing services configured in a central location. This can be useful to: -* Share a single instance of a service class across many components (known as a *singleton* service) -* Decouple components from particular concrete service classes and only reference abstractions. For example, there might be an interface `IDataAccess` implemented by a concrete class `DataAccess`. If a component uses DI to receive an `IDataAccess` implementation, it isn't coupled to any concrete type. That means the implementation could easily be swapped, perhaps to a mock implementation in unit tests. +* Share a single instance of a service class across many components (known as a *singleton* service). +* Decouple components from particular concrete service classes and only reference abstractions. For example, an interface `IDataAccess` is implemented by a concrete class `DataAccess`. When a component uses DI to receive an `IDataAccess` implementation, the component isn't coupled to the concrete type. The implementation can be swapped, perhaps to a mock implementation in unit tests. -Blazor's DI system is responsible for supplying instances of services to components. It also resolves dependencies recursively, so that services themselves can depend on further services, and so on. DI is configured during startup of the app. An example is shown later in this topic. +Blazor's DI system is responsible for supplying instances of services to components. DI also resolves dependencies recursively so that services themselves can depend on further services. DI is configured during startup of the app. An example is shown later in this topic. ## Use of existing .NET mechanisms -DI in Blazor is based on .NET's [System.IServiceProvider](https://docs.microsoft.com/dotnet/api/system.iserviceprovider) interface. It defines a generic mechanism for retrieving a service object in .NET apps. +DI in Blazor is based on .NET's [System.IServiceProvider](https://docs.microsoft.com/dotnet/api/system.iserviceprovider) interface. The interface defines a generic mechanism for retrieving a service object in .NET apps. Blazor's implementation of `System.IServiceProvider` obtains its services from an underlying [Microsoft.Extensions.DependencyInjection.IServiceCollection](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.iservicecollection). @@ -64,11 +64,11 @@ static void Main(string[] args) } ``` -`ServiceDescriptor` offers various overloads of three different functions that can be used to add services to Blazor's DI: +`ServiceDescriptor` offers several overloads of three methods that are used to add services to Blazor's DI: | Method | Description | | ----------- | ----------- | -| `Singleton` | DI creates a *single instance* of your service. All components requiring this service receive a reference to this instance. | +| `Singleton` | DI creates a *single instance* of the service. All components requiring this service receive a reference to this instance. | | `Transient` | Whenever a component requires this service, it receives a *new instance* of the service. | | `Scoped` | Blazor doesn't currently have the concept of DI scopes. `Scoped` behaves like `Singleton`. Therefore, prefer `Singleton` and avoid `Scoped`. | @@ -81,11 +81,11 @@ Blazor provides default services that are automatically added to the service col | `IUriHelper` | Helpers for working with URIs and navigation state (singleton). | | `HttpClient` | Provides methods for sending HTTP requests and receiving HTTP responses from a resource identified by a URI (singleton). Note that this instance of [System.Net.Http.HttpClient](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient) uses the browser for handling the HTTP traffic in the background. Its [BaseAddress](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient.baseaddress) is automatically set to the base URI prefix of the app. | -Note that it is possible to use a custom services provider instead of the default `BrowserServiceProvider` which is added by the default template. A custom service provider would not automatically provide the default services mentioned above. They would have to be added to the new service provider explicitly. +Note that it is possible to use a custom services provider instead of the default `BrowserServiceProvider` that's added by the default template. A custom service provider doesn't automatically provide the default services listed in the table. Those services must be added to the new service provider explicitly. ## Request a service in a component -Once services are added to the service collection, they can be injected into the components' Razor templates using the `@inject` Razor keyword. `@inject` has two parameters: +Once services are added to the service collection, they can be injected into the components' Razor templates using the `@inject` Razor directive. `@inject` has two parameters: * Type name: The type of the service to inject. * Property name: The name of the property receiving the injected app service. Note that the property doesn't require manual creation. The compiler creates the property. From 451c393e6aea6f3532f411513cc7e6a1d5fd90b6 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Thu, 5 Apr 2018 12:34:11 -0500 Subject: [PATCH 05/10] React to feedback --- docs/dependency-injection.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/dependency-injection.md b/docs/dependency-injection.md index e9d2f98..d638f49 100644 --- a/docs/dependency-injection.md +++ b/docs/dependency-injection.md @@ -28,12 +28,6 @@ DI is a technique for accessing services configured in a central location. This Blazor's DI system is responsible for supplying instances of services to components. DI also resolves dependencies recursively so that services themselves can depend on further services. DI is configured during startup of the app. An example is shown later in this topic. -## Use of existing .NET mechanisms - -DI in Blazor is based on .NET's [System.IServiceProvider](https://docs.microsoft.com/dotnet/api/system.iserviceprovider) interface. The interface defines a generic mechanism for retrieving a service object in .NET apps. - -Blazor's implementation of `System.IServiceProvider` obtains its services from an underlying [Microsoft.Extensions.DependencyInjection.IServiceCollection](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.iservicecollection). - ## Add services to DI After creating a new app, examine the `Main` method in *Program.cs*: @@ -53,18 +47,20 @@ static void Main(string[] args) `BrowserServiceProvider` receives an action with which you can add your app services to DI. `configure` references the underlying `IServiceCollection`, which is a list of service descriptor objects ([Microsoft.Extensions.DependencyInjection.ServiceDescriptor](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.servicedescriptor)). Services are added by providing service descriptors to the service collection. The following is a code sample demonstrating the concept: ```csharp +@using Microsoft.Extensions.DependencyInjection + static void Main(string[] args) { var serviceProvider = new BrowserServiceProvider(configure => { - configure.Add(ServiceDescriptor.Singleton()); + configure.AddSingleton(); }); new BrowserRenderer(serviceProvider).AddComponent("app"); } ``` -`ServiceDescriptor` offers several overloads of three methods that are used to add services to Blazor's DI: +Services can be configured with the following lifetimes: | Method | Description | | ----------- | ----------- | @@ -135,7 +131,7 @@ public class ComponentBase : BlazorComponent } ``` -In components derived from the base class, the `@inject` directive isn't required. The `InjectAttribute` of the base class is satisfactory: +In components derived from the base class, the `@inject` directive isn't required. The `InjectAttribute` of the base class is sufficient: ```csharp @page "/demo" @@ -172,7 +168,7 @@ Note the following prerequisites for constructor injection: ## Service lifetime -Note that Blazor doesn't automatically dispose injected services that implement `IDisposable`. Components can implement `IDisposable`. Services are disposed when the user navigates away from the component. +Note that Blazor doesn't automatically dispose injected services that implement `IDisposable`. Components can implement `IDisposable`. Components are disposed when the user navigates away from the component, then the component can dispose any transient services. The following code sample demonstrates how to implement `IDisposable` in a component: @@ -191,3 +187,7 @@ The following code sample demonstrates how to implement `IDisposable` in a compo } } ``` + +## Additional resources + +* [Dependency injection in ASP.NET Core](https://docs.microsoft.com/aspnet/core/fundamentals/dependency-injection) From 4f3198edfe95921ff4dbb9c22a1114214601cb5a Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Thu, 5 Apr 2018 12:46:42 -0500 Subject: [PATCH 06/10] Updates --- docs/dependency-injection.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/dependency-injection.md b/docs/dependency-injection.md index d638f49..7cec240 100644 --- a/docs/dependency-injection.md +++ b/docs/dependency-injection.md @@ -35,9 +35,9 @@ After creating a new app, examine the `Main` method in *Program.cs*: ```csharp static void Main(string[] args) { - var serviceProvider = new BrowserServiceProvider(configure => + var serviceProvider = new BrowserServiceProvider(services => { - // Add any custom services here + // Add custom services here }); new BrowserRenderer(serviceProvider).AddComponent("app"); @@ -51,9 +51,9 @@ static void Main(string[] args) static void Main(string[] args) { - var serviceProvider = new BrowserServiceProvider(configure => + var serviceProvider = new BrowserServiceProvider(services => { - configure.AddSingleton(); + services.AddSingleton(); }); new BrowserRenderer(serviceProvider).AddComponent("app"); @@ -168,7 +168,7 @@ Note the following prerequisites for constructor injection: ## Service lifetime -Note that Blazor doesn't automatically dispose injected services that implement `IDisposable`. Components can implement `IDisposable`. Components are disposed when the user navigates away from the component, then the component can dispose any transient services. +Note that Blazor doesn't automatically dispose injected services that implement `IDisposable`. Components can implement `IDisposable`. Components and their transient services are disposed when the user navigates away from the component. The following code sample demonstrates how to implement `IDisposable` in a component: From ba62fb7cd606d85073bde42519feff67d2c91ead Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Thu, 5 Apr 2018 12:48:11 -0500 Subject: [PATCH 07/10] Touch-up --- docs/dependency-injection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dependency-injection.md b/docs/dependency-injection.md index 7cec240..34ecc41 100644 --- a/docs/dependency-injection.md +++ b/docs/dependency-injection.md @@ -44,7 +44,7 @@ static void Main(string[] args) } ``` -`BrowserServiceProvider` receives an action with which you can add your app services to DI. `configure` references the underlying `IServiceCollection`, which is a list of service descriptor objects ([Microsoft.Extensions.DependencyInjection.ServiceDescriptor](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.servicedescriptor)). Services are added by providing service descriptors to the service collection. The following is a code sample demonstrating the concept: +`BrowserServiceProvider` receives an action with which you can add your app services to DI. `services` references the underlying `IServiceCollection`, which is a list of service descriptor objects ([Microsoft.Extensions.DependencyInjection.ServiceDescriptor](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.servicedescriptor)). Services are added by providing service descriptors to the service collection. The following is a code sample demonstrating the concept: ```csharp @using Microsoft.Extensions.DependencyInjection From b6bdd0811ad4a0ada4963170231a9cc117d4b74b Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Thu, 5 Apr 2018 12:50:08 -0500 Subject: [PATCH 08/10] Purge the last bit of 2nd person --- docs/dependency-injection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dependency-injection.md b/docs/dependency-injection.md index 34ecc41..2401fe8 100644 --- a/docs/dependency-injection.md +++ b/docs/dependency-injection.md @@ -44,7 +44,7 @@ static void Main(string[] args) } ``` -`BrowserServiceProvider` receives an action with which you can add your app services to DI. `services` references the underlying `IServiceCollection`, which is a list of service descriptor objects ([Microsoft.Extensions.DependencyInjection.ServiceDescriptor](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.servicedescriptor)). Services are added by providing service descriptors to the service collection. The following is a code sample demonstrating the concept: +`BrowserServiceProvider` receives an action where app services are added to DI. `services` references the underlying `IServiceCollection`, which is a list of service descriptor objects ([Microsoft.Extensions.DependencyInjection.ServiceDescriptor](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.servicedescriptor)). Services are added by providing service descriptors to the service collection. The following is a code sample demonstrating the concept: ```csharp @using Microsoft.Extensions.DependencyInjection From 4163a756a8b176b6a548fcce35c04f8bef5d7172 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Thu, 5 Apr 2018 14:29:23 -0500 Subject: [PATCH 09/10] Remove service lifetime section --- docs/dependency-injection.md | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/docs/dependency-injection.md b/docs/dependency-injection.md index 2401fe8..20bbf67 100644 --- a/docs/dependency-injection.md +++ b/docs/dependency-injection.md @@ -166,28 +166,6 @@ Note the following prerequisites for constructor injection: * The applicable constructor must be *public*. * There must only be one applicable constructor. In case of an ambiguity, DI throws an exception. -## Service lifetime - -Note that Blazor doesn't automatically dispose injected services that implement `IDisposable`. Components can implement `IDisposable`. Components and their transient services are disposed when the user navigates away from the component. - -The following code sample demonstrates how to implement `IDisposable` in a component: - -```csharp -... -@using System; -@implements IDisposable -... - -@functions { - ... - public void Dispose() - { - // Add code for disposing here - ... - } -} -``` - ## Additional resources * [Dependency injection in ASP.NET Core](https://docs.microsoft.com/aspnet/core/fundamentals/dependency-injection) From d8431e69a0c8a97f4d3271966ede5c3a5bc807aa Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Thu, 5 Apr 2018 14:36:04 -0500 Subject: [PATCH 10/10] Small nits --- docs/dependency-injection.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/dependency-injection.md b/docs/dependency-injection.md index 20bbf67..b528a0f 100644 --- a/docs/dependency-injection.md +++ b/docs/dependency-injection.md @@ -44,7 +44,7 @@ static void Main(string[] args) } ``` -`BrowserServiceProvider` receives an action where app services are added to DI. `services` references the underlying `IServiceCollection`, which is a list of service descriptor objects ([Microsoft.Extensions.DependencyInjection.ServiceDescriptor](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.servicedescriptor)). Services are added by providing service descriptors to the service collection. The following is a code sample demonstrating the concept: +`BrowserServiceProvider` receives an action where app services are added to DI. `services` references the underlying `IServiceCollection`, which is a list of service descriptor objects ([Microsoft.Extensions.DependencyInjection.ServiceDescriptor](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.servicedescriptor)). Services are added by providing service descriptors to the service collection. The following code sample demonstrates the concept: ```csharp @using Microsoft.Extensions.DependencyInjection @@ -75,7 +75,7 @@ Blazor provides default services that are automatically added to the service col | Method | Description | | ------------ | ----------- | | `IUriHelper` | Helpers for working with URIs and navigation state (singleton). | -| `HttpClient` | Provides methods for sending HTTP requests and receiving HTTP responses from a resource identified by a URI (singleton). Note that this instance of [System.Net.Http.HttpClient](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient) uses the browser for handling the HTTP traffic in the background. Its [BaseAddress](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient.baseaddress) is automatically set to the base URI prefix of the app. | +| `HttpClient` | Provides methods for sending HTTP requests and receiving HTTP responses from a resource identified by a URI (singleton). Note that this instance of [System.Net.Http.HttpClient](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient) uses the browser for handling the HTTP traffic in the background. [HttpClient.BaseAddress](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient.baseaddress) is automatically set to the base URI prefix of the app. | Note that it is possible to use a custom services provider instead of the default `BrowserServiceProvider` that's added by the default template. A custom service provider doesn't automatically provide the default services listed in the table. Those services must be added to the new service provider explicitly. @@ -110,9 +110,9 @@ The following example shows how to use `@inject`. The service implementing `Serv protected override async Task OnInitAsync() { - // The property DataRepository has received an implemenation - // of IDataAccess through dependency injection. We can use - // it to get data from our server. + // The property DataRepository received an implemenation + // of IDataAccess through dependency injection. Use + // DataRepository to obtain data from the server. Customers = await DataRepository.GetAllCustomersAsync(); } } @@ -123,7 +123,7 @@ Internally, the generated property (`DataRepository`) is decorated with the `Mic ```csharp public class ComponentBase : BlazorComponent { - // Note that Blazor's dependency injection works even if using the + // Blazor's dependency injection works even if using the // InjectAttribute in a component's base class. [Inject] protected IDataAccess DataRepository { get; set; } @@ -150,7 +150,7 @@ The following code sample demonstrates the concept: ```csharp public class DataAccess : IDataAccess { - // Note that the constructor receives an HttpClient via dependency + // The constructor receives an HttpClient via dependency // injection. HttpClient is a default service offered by Blazor. public Repository(HttpClient client) {