一尘不染

AddTransient、AddScoped 和 AddSingleton 服务的区别

c#

我想在 ASP.NET Core中实现依赖注入(DI)。因此,将此代码添加到ConfigureServices方法后,两种方式都有效。

ASP.NET Core 中的services.AddTransient和方法有什么区别?service.AddScoped

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.

    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddScoped<IEmailSender, AuthMessageSender>();
}

阅读 122

收藏
2022-02-22

共1个答案

一尘不染

Transient objects总是不同的;为每个控制器和每个服务提供一个新实例。

作用域对象在一个请求中是相同的,但在不同的请求中是不同的。

对于每个对象和每个请求,单例对象都是相同的。

为了更清楚地说明,.NET 文档中的这个示例显示了差异:

为了演示这些生命周期和注册选项之间的区别,请考虑一个简单的接口,它将一个或多个任务表示为具有唯一标识符的操作,OperationId. 根据我们如何配置此服务的生命周期,容器将为请求类提供相同或不同的服务实例。为了明确请求哪个生命周期,我们将为每个生命周期创建一个类型选项:

using System;

namespace DependencyInjectionSample.Interfaces
{
    public interface IOperation
    {
        Guid OperationId { get; }
    }

    public interface IOperationTransient : IOperation
    {
    }

    public interface IOperationScoped : IOperation
    {
    }

    public interface IOperationSingleton : IOperation
    {
    }

    public interface IOperationSingletonInstance : IOperation
    {
    }
}

我们使用单个类来实现这些接口,该类Operation在其构造函数中接受 GUID,如果没有提供,则使用新的 GUID:

using System;
using DependencyInjectionSample.Interfaces;
namespace DependencyInjectionSample.Classes
{
    public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton, IOperationSingletonInstance
    {
        Guid _guid;
        public Operation() : this(Guid.NewGuid())
        {

        }

        public Operation(Guid guid)
        {
            _guid = guid;
        }

        public Guid OperationId => _guid;
    }
}

接下来,在 中ConfigureServices,每种类型都根据其命名生命周期添加到容器中:

services.AddTransient<IOperationTransient, Operation>();
services.AddScoped<IOperationScoped, Operation>();
services.AddSingleton<IOperationSingleton, Operation>();
services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
services.AddTransient<OperationService, OperationService>();

请注意,该IOperationSingletonInstance服务正在使用具有已知 ID 的特定实例Guid.Empty,因此在使用此类型时会很清楚。我们还注册了OperationService依赖于其他每个Operation类型的一个,以便在请求中清楚该服务是获取与控制器相同的实例,还是为每个操作类型获取一个新实例。该服务所做的只是将其依赖项作为属性公开,因此它们可以显示在视图中。

using DependencyInjectionSample.Interfaces;

namespace DependencyInjectionSample.Services
{
    public class OperationService
    {
        public IOperationTransient TransientOperation { get; }
        public IOperationScoped ScopedOperation { get; }
        public IOperationSingleton SingletonOperation { get; }
        public IOperationSingletonInstance SingletonInstanceOperation { get; }

        public OperationService(IOperationTransient transientOperation,
            IOperationScoped scopedOperation,
            IOperationSingleton singletonOperation,
            IOperationSingletonInstance instanceOperation)
        {
            TransientOperation = transientOperation;
            ScopedOperation = scopedOperation;
            SingletonOperation = singletonOperation;
            SingletonInstanceOperation = instanceOperation;
        }
    }
}

为了演示对应用程序的单独请求之内和之间的对象生命周期,该示例包括一个OperationsController请求每种类型IOperationOperationService. 然后该Index操作显示所有控制器和服务的OperationId值。

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;
using Microsoft.AspNetCore.Mvc;

namespace DependencyInjectionSample.Controllers
{
    public class OperationsController : Controller
    {
        private readonly OperationService _operationService;
        private readonly IOperationTransient _transientOperation;
        private readonly IOperationScoped _scopedOperation;
        private readonly IOperationSingleton _singletonOperation;
        private readonly IOperationSingletonInstance _singletonInstanceOperation;

        public OperationsController(OperationService operationService,
            IOperationTransient transientOperation,
            IOperationScoped scopedOperation,
            IOperationSingleton singletonOperation,
            IOperationSingletonInstance singletonInstanceOperation)
        {
            _operationService = operationService;
            _transientOperation = transientOperation;
            _scopedOperation = scopedOperation;
            _singletonOperation = singletonOperation;
            _singletonInstanceOperation = singletonInstanceOperation;
        }

        public IActionResult Index()
        {
            // ViewBag contains controller-requested services
            ViewBag.Transient = _transientOperation;
            ViewBag.Scoped = _scopedOperation;
            ViewBag.Singleton = _singletonOperation;
            ViewBag.SingletonInstance = _singletonInstanceOperation;

            // Operation service has its own requested services
            ViewBag.Service = _operationService;
            return View();
        }
    }
}

现在对该控制器操作发出两个单独的请求:

第一个请求

第二个请求

观察OperationId请求中和请求之间的哪些值不同。

  • 瞬态对象总是不同的;为每个控制器和每个服务提供一个新实例。
  • 作用域对象在一个请求中是相同的,但在不同的请求中是不同的
  • 单例对象对于每个对象和每个请求都是相同的(无论是否提供了实例ConfigureServices
2022-02-22