c# - AddTransient、AddScoped 和 AddSingleton 服务差异

标签 c# asp.net-core .net-core

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

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

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

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

最佳答案

长话短说

Transient objects are always different; a new instance is provided to every controller and every service.

Scoped objects are the same within a request, but different across different requests.

Singleton objects are the same for every object and every request.

为了更清楚,这个例子来自.NET documentation显示差异:

为了演示这些生命周期和注册选项之间的区别,请考虑一个简单的接口(interface),该接口(interface)将一个或多个任务表示为具有唯一标识符 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 实现这些接口(interface),该类在其构造函数中接受 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 类型,因此在请求中可以清楚地知道该服务是否正在获取与 Controller 相同的实例,或一个新的,对于每种操作类型。该服务所做的只是将其依赖项作为属性公开,以便它们可以显示在 View 中。

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,它请求每种 IOperation 类型以及一个 操作服务Index 操作然后显示所有 Controller 和服务的 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();
        }
    }
}

现在对这个 Controller 操作发出了两个单独的请求:

First Request

Second Request

观察哪些 OperationId 值在请求中和请求之间发生变化。

  • transient 对象总是不同的;为每个 Controller 和每个服务提供一个新实例。

  • Scoped objects 在一个请求中是相同的,但在不同的请求中是不同的

  • 单例对象对于每个对象和每个请求都是相同的(不管ConfigureServices中是否提供实例)

关于c# - AddTransient、AddScoped 和 AddSingleton 服务差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38138100/

相关文章:

c# - WPF 组合框绑定(bind) : can't change selection

c# - MVC Jquery 全局化 - 验证小数

c# - dotnet 核心方法 'ValidateOptions' ... 没有实现

c# - 模拟扩展方法导致 System.NotSupportedException

c# - C#条件编译中有OR运算符吗?

c# - 为什么我的 C# 中的 List.Sort 方法颠倒了列表的顺序?

c# - 可以知道 asp.net mvc 3 中的 OutputCache 大小吗?

c# - 防止 AddRedirectToWwwPermanent() 在 ASP.NET Core 2.1 中将 "www"添加到 *.azurewebsites.net 前面

c# - 将 Entity Framework Core SQLite 文件存储在项目相关的子目录中

c# - ASP.NET Core 1.0如何在DI的Startup类中添加IHttpContextAccessor?