.NET 中的相依性插入Dependency injection in .NET

.NET 支援 (DI) 軟體設計模式中的相依性插入,這項技術可讓您在類別與其相依性之間進行反向 控制 (IoC) .NET supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies. .NET 中的相依性插入是一流的 公民,以及設定、記錄和選項模式。Dependency injection in .NET is a first-class citizen, along with configuration, logging, and the options pattern.

相依 性是另 一個物件所依存的物件。A dependency is an object that another object depends on. MessageWriter使用其他類別所依存的方法檢查下列類別 WriteExamine the following MessageWriter class with a Write method that other classes depend on:

public class MessageWriter
{
    public void Write(string message)
    {
        Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
    }
}

類別可以建立類別的實例 MessageWriter ,以使用其 Write 方法。A class can create an instance of the MessageWriter class to make use of its Write method. 在下列範例中, MessageWriter 類別是類別的相依性 WorkerIn the following example, the MessageWriter class is a dependency of the Worker class:

public class Worker : BackgroundService
{
    private readonly MessageWriter _messageWriter = new MessageWriter();

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
            await Task.Delay(1000, stoppingToken);
        }
    }
}

類別會建立和直接相依于 MessageWriter 類別。The class creates and directly depends on the MessageWriter class. 硬式編碼的相依性(例如在先前的範例中)有問題,因此應該避免因下列原因:Hard-coded dependencies, such as in the previous example, are problematic and should be avoided for the following reasons:

  • 若要以 MessageWriter 不同的實作為取代, Worker 必須修改該類別。To replace MessageWriter with a different implementation, the Worker class must be modified.
  • 如果 MessageWriter 有相依性,則也必須由類別設定 WorkerIf MessageWriter has dependencies, they must also be configured by the Worker class. 在具有多個相依於 MessageWriter 之多個類別的大型專案中,設定程式碼在不同的應用程式之間會變得鬆散。In a large project with multiple classes depending on MessageWriter, the configuration code becomes scattered across the app.
  • 此實作難以進行單元測試。This implementation is difficult to unit test. 應用程式應該使用模擬 (Mock) 或虛設常式 (Stub) MessageWriter 類別,這在使用此方法時無法使用。The app should use a mock or stub MessageWriter class, which isn't possible with this approach.

相依性插入可透過下列方式解決這些問題:Dependency injection addresses these problems through:

  • 使用介面或基底類別來將相依性資訊抽象化。The use of an interface or base class to abstract the dependency implementation.
  • 在服務容器中註冊相依性。Registration of the dependency in a service container. .NET 提供內建的服務容器 IServiceProvider.NET provides a built-in service container, IServiceProvider. 服務通常會在應用程式啟動時註冊,並附加至 IServiceCollectionServices are typically registered at the app's start-up, and appended to an IServiceCollection. 新增所有服務之後,您就可以使用 BuildServiceProvider 來建立服務容器。Once all services are added, you use BuildServiceProvider to create the service container.
  • 將服務「插入」到服務使用位置之類別的建構函式。Injection of the service into the constructor of the class where it's used. 架構會負責建立相依性的執行個體,並在不再需要時將它捨棄。The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

例如, IMessageWriter 介面會定義 Write 方法:As an example, the IMessageWriter interface defines the Write method:

namespace DependencyInjection.Example
{
    public interface IMessageWriter
    {
        void Write(string message);
    }
}

這個介面是由具象型別 MessageWriter 所實作:This interface is implemented by a concrete type, MessageWriter:

using System;

namespace DependencyInjection.Example
{
    public class MessageWriter : IMessageWriter
    {
        public void Write(string message)
        {
            Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
        }
    }
}

範例程式碼會 IMessageWriter 使用具象類型來註冊服務 MessageWriterThe sample code registers the IMessageWriter service with the concrete type MessageWriter. AddScoped方法會使用範圍存留期(單一要求的存留期)來註冊服務。The AddScoped method registers the service with a scoped lifetime, the lifetime of a single request. 本文稍後會說明服務存留期Service lifetimes are described later in this article.

using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace DependencyInjection.Example
{
    class Program
    {
        static Task Main(string[] args) =>
            CreateHostBuilder(args).Build().RunAsync();

        static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((_, services) =>
                    services.AddHostedService<Worker>()
                            .AddScoped<IMessageWriter, MessageWriter>());
    }
}

在範例應用程式中, IMessageWriter 會要求服務並用來呼叫 Write 方法:In the sample app, the IMessageWriter service is requested and used to call the Write method:

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;

namespace DependencyInjection.Example
{
    public class Worker : BackgroundService
    {
        private readonly IMessageWriter _messageWriter;

        public Worker(IMessageWriter messageWriter) =>
            _messageWriter = messageWriter;

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
                await Task.Delay(1000, stoppingToken);
            }
        }
    }
}

使用 DI 模式時,背景工作角色服務:By using the DI pattern, the worker service:

  • 不會使用具象型別 MessageWriter ,而只會使用實作為的 IMessageWriter 介面。Doesn't use the concrete type MessageWriter, only the IMessageWriter interface that implements it. 這可讓您輕鬆地變更控制器所使用的執行,而不需要修改控制器。That makes it easy to change the implementation that the controller uses without modifying the controller.
  • 不會建立的實例 MessageWriter ,而是由 DI 容器所建立。Doesn't create an instance of MessageWriter, it's created by the DI container.

IMessageWriter 可以使用內建的記錄 API 來改善介面的實作為:The implementation of the IMessageWriter interface can be improved by using the built-in logging API:

using Microsoft.Extensions.Logging;

namespace DependencyInjection.Example
{
    public class LoggingMessageWriter : IMessageWriter
    {
        private readonly ILogger<LoggingMessageWriter> _logger;

        public LoggingMessageWriter(ILogger<LoggingMessageWriter> logger) =>
            _logger = logger;

        public void Write(string message) =>
            _logger.LogInformation(message);
    }
}

更新的 ConfigureServices 方法會註冊新的 IMessageWriter 實作為:The updated ConfigureServices method registers the new IMessageWriter implementation:

static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices((_, services) =>
            services.AddHostedService<Worker>()
                    .AddScoped<IMessageWriter, LoggingMessageWriter>());

LoggingMessageWriter 相依于 ILogger<TCategoryName> 它在函式中要求的。LoggingMessageWriter depends on ILogger<TCategoryName>, which it requests in the constructor. ILogger<TCategoryName>架構提供的服務ILogger<TCategoryName> is a framework-provided service.

以鏈結方式使用相依性插入並非不尋常。It's not unusual to use dependency injection in a chained fashion. 每個要求的相依性接著會要求其自己的相依性。Each requested dependency in turn requests its own dependencies. 容器會解決圖形中的相依性,並傳回完全解析的服務。The container resolves the dependencies in the graph and returns the fully resolved service. 必須先解析的相依性集合組通常稱為「相依性樹狀結構」、「相依性圖形」或「物件圖形」。The collective set of dependencies that must be resolved is typically referred to as a dependency tree, dependency graph, or object graph.

容器會 ILogger<TCategoryName> 利用 (泛型) 開放式型別來解析,因此不需要註冊每個 (泛型) 結構類型The container resolves ILogger<TCategoryName> by taking advantage of (generic) open types, eliminating the need to register every (generic) constructed type.

在相依性插入術語中,服務:In dependency injection terminology, a service:

  • 通常是為其他物件(例如服務)提供服務的物件 IMessageWriterIs typically an object that provides a service to other objects, such as the IMessageWriter service.
  • 與 web 服務無關,但服務可能會使用 web 服務。Is not related to a web service, although the service may use a web service.

架構會提供健全的記錄系統。The framework provides a robust logging system. IMessageWriter上述範例中所示的執行是為了示範基本 DI 而撰寫的,而不是用來執行記錄。The IMessageWriter implementations shown in the preceding examples were written to demonstrate basic DI, not to implement logging. 大部分的應用程式都不需要撰寫記錄器。Most apps shouldn't need to write loggers. 下列程式碼示範如何使用預設記錄,這只需要在 Worker 中註冊 ConfigureServices 為託管服務 AddHostedServiceThe following code demonstrates using the default logging, which only requires the Worker to be registered in ConfigureServices as a hosted service AddHostedService:

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    public Worker(ILogger<Worker> logger) =>
        _logger = logger;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(1000, stoppingToken);
        }
    }
}

使用上述程式碼不需要更新 ConfigureServices ,因為記錄是由架構提供。Using the preceding code, there is no need to update ConfigureServices, because logging is provided by the framework.

使用擴充方法來註冊服務群組Register groups of services with extension methods

Microsoft 延伸模組使用註冊一組相關服務的慣例。Microsoft Extensions uses a convention for registering a group of related services. 慣例是使用單一 Add{GROUP_NAME} 擴充方法來註冊架構功能所需的所有服務。The convention is to use a single Add{GROUP_NAME} extension method to register all of the services required by a framework feature. 例如, AddOptions 擴充方法會註冊使用選項所需的所有服務。For example, the AddOptions extension method registers all of the services required for using options.

架構提供的服務Framework-provided services

ConfigureServices方法會註冊應用程式所使用的服務,包括平臺功能。The ConfigureServices method registers services that the app uses, including platform features. 一開始, IServiceCollection 提供給的是 ConfigureServices 由架構定義的服務,視 主機的設定方式而定。Initially, the IServiceCollection provided to ConfigureServices has services defined by the framework depending on how the host was configured. 針對以 .NET 範本為基礎的應用程式,架構會註冊數百項服務。For apps based on the .NET templates, the framework registers hundreds of services.

下表列出這些架構註冊服務的小型範例:The following table lists a small sample of these framework-registered services:

服務類型Service Type 存留期Lifetime
IHostApplicationLifetime 單一Singleton
Microsoft.Extensions.Logging.ILogger<TCategoryName> 單一Singleton
Microsoft.Extensions.Logging.ILoggerFactory 單一Singleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider 單一Singleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> 暫時性Transient
Microsoft.Extensions.Options.IOptions<TOptions> 單一Singleton
System.Diagnostics.DiagnosticListener 單一Singleton
System.Diagnostics.DiagnosticSource 單一Singleton

服務存留期Service lifetimes

您可以使用下列其中一種存留期來註冊服務:Services can be registered with one of the following lifetimes:

  • 暫時性Transient
  • 具範圍Scoped
  • 單一Singleton

下列各節將說明每個先前的存留期。The following sections describe each of the preceding lifetimes. 為每個已註冊的服務選擇適當的存留期。Choose an appropriate lifetime for each registered service.

暫時性Transient

每次從服務容器要求暫時性存留期服務時都會建立它們。Transient lifetime services are created each time they're requested from the service container. 此存留期最適合用於輕量型的無狀態服務。This lifetime works best for lightweight, stateless services. 使用註冊暫時性服務 AddTransientRegister transient services with AddTransient.

在處理要求的應用程式中,會在要求結束時處置暫時性服務。In apps that process requests, transient services are disposed at the end of the request.

具範圍Scoped

針對 web 應用程式,限域存留期表示每個用戶端要求都會建立服務一次, (連接) 。For web applications, a scoped lifetime indicates that services are created once per client request (connection). 使用註冊已設定範圍的服務 AddScopedRegister scoped services with AddScoped.

在處理要求的應用程式中,會在要求結束時處置範圍服務。In apps that process requests, scoped services are disposed at the end of the request.

使用 Entity Framework Core 時, AddDbContext 擴充方法預設會註冊 DbContext 具有範圍存留期的類型。When using Entity Framework Core, the AddDbContext extension method registers DbContext types with a scoped lifetime by default.

注意

請 *not _ 從 singleton 解析已設定範圍的服務,並小心不要間接執行,例如透過暫時性的服務。Do *not _ resolve a scoped service from a singleton and be careful not to do so indirectly, for example, through a transient service. 處理後續要求時,它可能會導致服務有不正確的狀態。It may cause the service to have incorrect state when processing subsequent requests. 您可以:It's fine to:

  • 從範圍或暫時性服務解析單一服務。Resolve a singleton service from a scoped or transient service.
  • 從另一個範圍或暫時性服務解析已設定範圍的服務。Resolve a scoped service from another scoped or transient service.

根據預設,在開發環境中,從較長存留期的另一個服務解析服務,會擲回例外狀況。By default, in the development environment, resolving a service from another service with a longer lifetime throws an exception. 如需詳細資訊,請參閱範圍驗證For more information, see Scope validation.

單一Singleton

系統會建立單一存留期服務:Singleton lifetime services are created either:

  • 第一次要求時。The first time they're requested.
  • 由開發人員直接將實實例提供給容器。By the developer, when providing an implementation instance directly to the container. 這種方法很少需要。This approach is rarely needed.

從相依性插入容器每個後續的服務執行要求都會使用相同的實例。Every subsequent request of the service implementation from the dependency injection container uses the same instance. 如果應用程式需要單一行為,請允許服務容器管理服務的存留期。If the app requires singleton behavior, allow the service container to manage the service's lifetime. 請勿實行 singleton 設計模式,並提供程式碼來處置 singleton。Don't implement the singleton design pattern and provide code to dispose of the singleton. 服務絕對不能由從容器解析服務的程式碼處置。Services should never be disposed by code that resolved the service from the container. 如果類型或 factory 註冊為 singleton,則容器會自動處置 singleton。If a type or factory is registered as a singleton, the container disposes the singleton automatically.

使用註冊單一服務 AddSingletonRegister singleton services with AddSingleton. 單一服務必須是安全線程,而且通常用於無狀態服務。Singleton services must be thread safe and are often used in stateless services.

在處理要求的應用程式中,會在應用程式關閉時處置單一服務 ServiceProviderIn apps that process requests, singleton services are disposed when the ServiceProvider is disposed on application shutdown. 因為在關閉應用程式之前不會釋出記憶體,請考慮使用單一服務的記憶體。Because memory is not released until the app is shut down, consider memory use with a singleton service.

警告

請勿 *從 singleton 解析已* 設定範圍的服務。Do not resolve a scoped service from a singleton. 處理後續要求時,它可能會導致服務有不正確的狀態。It may cause the service to have incorrect state when processing subsequent requests. 從範圍或暫時性服務解析單一服務是很好的。It's fine to resolve a singleton service from a scoped or transient service.

服務註冊方法Service registration methods

此架構提供適用于特定案例的服務註冊延伸方法:The framework provides service registration extension methods that are useful in specific scenarios:

方法Method 自動Automatic
物件 (object)object
處置disposal
多個Multiple
實作implementations
傳遞引數Pass args
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()

範例:Example:

services.AddSingleton<IMyDep, MyDep>();
Yes Yes No
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})

範例:Examples:

services.AddSingleton<IMyDep>(sp => new MyDep());
services.AddSingleton<IMyDep>(sp => new MyDep(99));
Yes Yes Yes
Add{LIFETIME}<{IMPLEMENTATION}>()

範例:Example:

services.AddSingleton<MyDep>();
Yes No No
AddSingleton<{SERVICE}>(new {IMPLEMENTATION})

範例:Examples:

services.AddSingleton<IMyDep>(new MyDep());
services.AddSingleton<IMyDep>(new MyDep(99));
No Yes Yes
AddSingleton(new {IMPLEMENTATION})

範例:Examples:

services.AddSingleton(new MyDep());
services.AddSingleton(new MyDep(99));
No No Yes

如需類型處置的詳細資訊,請參閱<服務處置>一節。For more information on type disposal, see the Disposal of services section.

註冊只有實作為型別的服務,相當於向相同的實和服務型別註冊該服務。Registering a service with only an implementation type is equivalent to registering that service with the same implementation and service type. 這就是為什麼無法使用未採用明確服務類型的方法來註冊多個服務的服務。This is why multiple implementations of a service cannot be registered using the methods that don't take an explicit service type. 這些方法可以註冊多個 _instances * 的服務,但它們都有相同的 作為型別。These methods can register multiple _instances* of a service, but they will all have the same implementation type.

您可以使用上述任何一種服務註冊方法來註冊相同服務類型的多個服務實例。Any of the above service registration methods can be used to register multiple service instances of the same service type. 在下列範例中, AddSingleton 使用 IMessageWriter 做為服務類型呼叫兩次。In the following example, AddSingleton is called twice with IMessageWriter as the service type. 第二個呼叫會 AddSingleton 在解析為時覆寫上一個 IMessageWriter ,並在透過來解析多個服務時加入至上一個 IEnumerable<IMessageWriter>The second call to AddSingleton overrides the previous one when resolved as IMessageWriter and adds to the previous one when multiple services are resolved via IEnumerable<IMessageWriter>. 服務會依照其在解析時所註冊的順序顯示 IEnumerable<{SERVICE}>Services appear in the order they were registered when resolved via IEnumerable<{SERVICE}>.

using System.Threading.Tasks;
using ConsoleDI.IEnumerableExample;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace ConsoleDI.Example
{
    class Program
    {
        static Task Main(string[] args)
        {
            using IHost host = CreateHostBuilder(args).Build();

            _ = host.Services.GetService<ExampleService>();

            return host.RunAsync();
        }

        static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((_, services) =>
                    services.AddSingleton<IMessageWriter, ConsoleMessageWriter>()
                            .AddSingleton<IMessageWriter, LoggingMessageWriter>()
                            .AddSingleton<ExampleService>());
    }
}

上述範例程式碼會註冊兩個的實作為 IMessageWriterThe preceding sample source code registers two implementations of the IMessageWriter.

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace ConsoleDI.IEnumerableExample
{
    public class ExampleService
    {
        public ExampleService(
            IMessageWriter messageWriter,
            IEnumerable<IMessageWriter> messageWriters)
        {
            Trace.Assert(messageWriter is LoggingMessageWriter);

            var dependencyArray = messageWriters.ToArray();
            Trace.Assert(dependencyArray[0] is ConsoleMessageWriter);
            Trace.Assert(dependencyArray[1] is LoggingMessageWriter);
        }
    }
}

ExampleService 定義兩個函式參數:單一 IMessageWriterIEnumerable<IMessageWriter>The ExampleService defines two constructor parameters; a single IMessageWriter, and an IEnumerable<IMessageWriter>. Single IMessageWriter 是最後一個已註冊的執行,而 IEnumerable<IMessageWriter> 代表所有已註冊的實作為。The single IMessageWriter is the last implementation to have been registered, whereas the IEnumerable<IMessageWriter> represents all registered implementations.

此架構也會提供 TryAdd{LIFETIME} 擴充方法,只有在尚未註冊任何執行時,才會註冊服務。The framework also provides TryAdd{LIFETIME} extension methods, which register the service only if there isn't already an implementation registered.

在下列範例中,呼叫以註冊為的 AddSingleton ConsoleMessageWriter 實作為的 IMessageWriterIn the following example, the call to AddSingleton registers ConsoleMessageWriter as an implementation for IMessageWriter. 的呼叫 TryAddSingleton 沒有任何作用,因為 IMessageWriter 已經有已註冊的實作為:The call to TryAddSingleton has no effect because IMessageWriter already has a registered implementation:

services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
services.TryAddSingleton<IMessageWriter, LoggingMessageWriter>();

沒有 TryAddSingleton 任何作用,因為它已經加入,而且 "try" 將會失敗。The TryAddSingleton has no effect, as it was already added and the "try" will fail. 會判斷提示 ExampleService 如下:The ExampleService would assert the following:

public class ExampleService
{
    public ExampleService(
        IMessageWriter messageWriter,
        IEnumerable<IMessageWriter> messageWriters)
    {
        Trace.Assert(messageWriter is ConsoleMessageWriter);
        Trace.Assert(messageWriters.Single() is ConsoleMessageWriter);
    }
}

如需詳細資訊,請參閱For more information, see:

只有在尚未有 相同類型 的執行時, TryAddEnumerable (ServiceDescriptor)方法才會註冊服務。The TryAddEnumerable(ServiceDescriptor) methods register the service only if there isn't already an implementation of the same type. 多個服務會透過 IEnumerable<{SERVICE}> 解析。Multiple services are resolved via IEnumerable<{SERVICE}>. 註冊服務時,如果尚未加入相同類型的實例,請新增實例。When registering services, add an instance if one of the same types hasn't already been added. 程式庫作者會使用 TryAddEnumerable 來避免在容器中註冊多個執行複本。Library authors use TryAddEnumerable to avoid registering multiple copies of an implementation in the container.

在下列範例中,第一次呼叫 TryAddEnumerable 註冊為的 MessageWriter 實作為 IMessageWriter1In the following example, the first call to TryAddEnumerable registers MessageWriter as an implementation for IMessageWriter1. 第二個呼叫會註冊 MessageWriter IMessageWriter2The second call registers MessageWriter for IMessageWriter2. 第三個呼叫沒有任何作用,因為 IMessageWriter1 已註冊的實作為 MessageWriterThe third call has no effect because IMessageWriter1 already has a registered implementation of MessageWriter:

public interface IMessageWriter1 { }
public interface IMessageWriter2 { }

public class MessageWriter : IMessageWriter1, IMessageWriter2
{
}

services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1, MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter2, MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1, MessageWriter>());

除非註冊相同類型的多個執行,否則服務註冊通常會獨立排序。Service registration is generally order independent except when registering multiple implementations of the same type.

IServiceCollection 是物件的集合 ServiceDescriptorIServiceCollection is a collection of ServiceDescriptor objects. 下列範例顯示如何藉由建立和新增來註冊服務 ServiceDescriptorThe following example shows how to register a service by creating and adding a ServiceDescriptor:

string secretKey = Configuration["SecretKey"];
var descriptor = new ServiceDescriptor(
    typeof(IMessageWriter),
    _ => new DefaultMessageWriter(secretKey),
    ServiceLifetime.Transient);

services.Add(descriptor);

內建方法會 Add{LIFETIME} 使用相同的方法。The built-in Add{LIFETIME} methods use the same approach. 例如,請參閱 AddScoped 來源程式碼For example, see the AddScoped source code.

建構函式插入行為Constructor injection behavior

您可以使用下列方法來解析服務:Services can be resolved by using:

建構函式可以接受不是由相依性插入提供的引數,但引數必須指派預設值。Constructors can accept arguments that aren't provided by dependency injection, but the arguments must assign default values.

當服務由 IServiceProviderActivatorUtilities 解析時,建構函式插入會要求 public 建構函式。When services are resolved by IServiceProvider or ActivatorUtilities, constructor injection requires a public constructor.

當服務由 ActivatorUtilities 解析時,建構函式插入只要求只能有一個適用的建構函式存在。When services are resolved by ActivatorUtilities, constructor injection requires that only one applicable constructor exists. 支援建構函式多載,但只能有一個多載存在,其引數可以藉由相依性插入而完成。Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection.

範圍驗證Scope validation

當應用程式在環境中執行 Development 並呼叫 >createdefaultbuilder 來建立主機時,預設服務提供者會執行檢查,以確認:When the app runs in the Development environment and calls CreateDefaultBuilder to build the host, the default service provider performs checks to verify that:

  • 範圍服務無法從根服務提供者解析。Scoped services aren't resolved from the root service provider.
  • 範圍服務不會插入 singleton 中。Scoped services aren't injected into singletons.

根服務提供者會在呼叫 BuildServiceProvider 時建立。The root service provider is created when BuildServiceProvider is called. 當提供者啟動應用程式時,根服務提供者的存留期會對應到應用程式的存留期,並在應用程式關閉時處置。The root service provider's lifetime corresponds to the app's lifetime when the provider starts with the app and is disposed when the app shuts down.

範圍服務會由建立這些服務的容器處置。Scoped services are disposed by the container that created them. 如果在根容器中建立範圍服務,服務的存留期會有效地升階為 singleton,因為它只會在應用程式關閉時由根容器處置。If a scoped service is created in the root container, the service's lifetime is effectively promoted to singleton because it's only disposed by the root container when the app shuts down. 當呼叫 BuildServiceProvider 時,驗證服務範圍會攔截到這些情況。Validating service scopes catches these situations when BuildServiceProvider is called.

請參閱See also