AddTransient,AddScoped和AddSingleton服务的差异?

时间:2016-07-01 06:03:03

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

我想在 dependency injection 中实施 Asp.Net Core 。因此,将此代码添加到 ConfigureServices 方法后,两种方式都可以正常工作。

services.AddTransient service.AddScope 方法之间有什么区别Asp.Net Core

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

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

10 个答案:

答案 0 :(得分:1035)

<强> TL; DR

  

瞬态物体总是不同的;提供了一个新实例   每个控制器和每项服务。

     

Scoped对象在请求中是相同的,但不同   不同的要求。

     

Singleton对象对于每个对象和每个请求都是相同的。

有关更多说明,asp.net docs中的此示例显示了不同之处:

为了演示这些生命周期和注册选项之间的区别,请考虑一个简单的界面,它将一个或多个任务表示为具有唯一标识符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

接下来,在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,它请求每种IOperation类型以及OperationService。然后,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();
        }
    }
}

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

Second Request

观察请求中和请求之间OperationId值的变化。

  • 瞬态物体总是不同的;每个控制器和每个服务都提供一个新实例。

  • Scoped对象在请求中是相同的,但在不同的请求中是不同的

  • Singleton对象对于每个对象和每个请求都是相同的(无论ConfigureServices中是否提供了实例)

答案 1 :(得分:248)

在dotnet的依赖注入中,有三个主要的生命周期:

Singleton ,可在整个应用程序中创建单个实例。它首次创建实例,并在所有调用中重用相同的对象。

范围生命周期服务在范围内的每个请求中创建一次。它相当于当前范围内的Singleton。例如。在MVC中,它为每个http请求创建1个实例,但在同一个Web请求中的其他调用中使用相同的实例。

每次请求时都会创建

瞬态生命周期服务。这种生命周期最适合轻量级,无状态服务。

在这里你可以找到和看到差异的例子:

http://dotnetliberty.com/index.php/2015/10/15/asp-net-5-mvc6-dependency-injection-in-6-steps/

https://codewala.net/2015/04/30/your-dependency-injection-ready-asp-net-asp-net-5/

这是官方文档的链接:

https://docs.asp.net/en/latest/fundamentals/dependency-injection.html#service-lifetimes-and-registration-options

答案 2 :(得分:49)

要使用哪个

瞬态

  • 因为它们是每次创建时都会使用的更多内存和资源,并且会对性能产生负面影响
  • 将此功能用于轻量服务,几乎没有或没有无状态

作用域

  • 想要在请求中维持状态的更好选择。

单人

  • 这些服务中的内存泄漏将随着时间的流逝而累积。
  • 由于它们一旦在各处重复使用而创建,因此内存效率也很高。

在需要维护应用程序范围状态的地方使用Singletons。应用程序配置或参数,日志记录服务,数据缓存是可以使用单例的一些示例。

将具有不同生命周期的服务注入另一个生命

  1. 切勿将作用域和临时服务注入到Singleton服务中。(这将瞬变或作用域服务有效地转换为Singleton。)
  2. 切勿将瞬态服务注入范围服务(这会将瞬态服务转换为范围服务。)

答案 3 :(得分:22)

  • Singleton是应用程序生命周期的单个实例 域。
  • Scoped是范围持续时间内的单个实例 请求,表示在ASP.NET中按 HTTP 请求。
  • Transient是每个代码请求的单个实例。

通常,代码请求应该通过构造函数参数进行,如

input_shape

我想在@ akazemis的回答中指出,在DI环境中的“服务”并不意味着RESTful服务;服务是提供功能的依赖项的实现。

答案 4 :(得分:19)

当需要注入多个相同类型的对象时,

瞬态,作用域和单例定义ASP.NET MVC核心DI中的对象创建过程。如果您不熟悉依赖注入,则可以看到此DI IOC video

您可以看到下面的控制器代码,其中我在构造函数中请求了两个“IDal”实例。 Transient,Scoped和Singleton定义是否在“_dal”和“_dal1”中注入相同的实例或不同。

public class CustomerController : Controller
    {
        IDal dal = null;
        public CustomerController(IDal _dal
                                ,IDal _dal1)
        {
            dal = _dal;
            // DI of MVC core
            // inversion of control
        }
}

瞬态: - 在瞬态新对象实例中将注入一个请求和响应。下面是我显示GUID值的快照图像。

enter image description here

Scoped: - 在作用域中,同一个对象实例将在单个请求和响应中注入。

enter image description here

Singleton: - 在Singleton中,将在所有请求和响应中注入相同的对象。在这种情况下,将创建一个对象的全局实例。

下面是一个简单的图表,可以直观地解释上述基本原理。

MVC DI image

以上图片由SBSS团队在我进行ASP.NET MVC training in mumbai培训时绘制,非常感谢SBSS团队创建上述图片。

答案 5 :(得分:10)

此图很好地说明了这个概念。 不幸的是,我找不到该图像的原始来源,但是有人做到了,他以图像的形式很好地展示了这个概念。 enter image description here

答案 6 :(得分:1)

here(示例中的此链接非常有用)所述,

  

此接口与具体类型之间的映射定义为,每次您请求IContryService类型时,都将获得CountryService的新实例。在这种情况下,这就是瞬态的意思。您还可以添加单例映射(使用AddSingleton)和作用域映射(使用AddScoped)。在这种情况下,作用域是指作用域为HTTP请求,这也意味着它在当前请求运行时是单例。您还可以使用方法AddInstance将现有实例添加到DI容器中。   这些是注册到IServiceCollection的几乎完整的方法

答案 7 :(得分:0)

AddSingleton()

AddSingleton()首次被请求时会创建一个服务实例,并在所有需要该服务的地方重用该实例。

AddScoped()

在范围服务中,每个http请求都获得一个新实例。但是,对于同一个http请求,如果在多个位置(如视图和控制器中)都需要该服务,则将为该http请求的整个范围提供相同的实例。但是每个新的http请求都会获得该服务的新实例。

AddTransient()

对于临时服务,每次请求服务实例时,无论是在同一http请求的范围内还是跨不同的http请求,都会提供一个新实例。

答案 8 :(得分:0)

在寻找了这个问题的答案之后,我找到了一个精妙的解释,并举了一个例子,我想与你分享。

在此示例中,我们具有以下给定代码:

public interface IEmployeeRepository
{
    IEnumerable<Employee> GetAllEmployees();
    Employee Add(Employee employee);
}

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class MockEmployeeRepository : IEmployeeRepository
{
    private List<Employee> _employeeList;

    public MockEmployeeRepository()
    {
        _employeeList = new List<Employee>()
    {
        new Employee() { Id = 1, Name = "Mary" },
        new Employee() { Id = 2, Name = "John" },
        new Employee() { Id = 3, Name = "Sam" },
    };
    }

    public Employee Add(Employee employee)
    {
        employee.Id = _employeeList.Max(e => e.Id) + 1;
        _employeeList.Add(employee);
        return employee;
    }

    public IEnumerable<Employee> GetAllEmployees()
    {
        return _employeeList;
    }
}

HomeController

public class HomeController : Controller
{
    private IEmployeeRepository _employeeRepository;

    public HomeController(IEmployeeRepository employeeRepository)
    {
        _employeeRepository = employeeRepository;
    }

    [HttpGet]
    public ViewResult Create()
    {
        return View();
    }

    [HttpPost]
    public IActionResult Create(Employee employee)
    {
        if (ModelState.IsValid)
        {
            Employee newEmployee = _employeeRepository.Add(employee);
        }

        return View();
    }
}

创建视图

@model Employee
@inject IEmployeeRepository empRepository

<form asp-controller="home" asp-action="create" method="post">
    <div>
        <label asp-for="Name"></label>
        <div>
            <input asp-for="Name">
        </div>
    </div>

    <div>
        <button type="submit">Create</button>
    </div>

    <div>
        Total Employees Count = @empRepository.GetAllEmployees().Count().ToString()
    </div>
</form>

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddSingleton<IEmployeeRepository, MockEmployeeRepository>();
}

复制并粘贴此代码,然后在视图中按创建按钮,然后在 AddSingletonAddScopedAddTransient每次都会得到不同的结果,这可能有助于您理解以下解释:

  

AddSingleton()-顾名思义,AddSingleton()方法创建了一个   单例服务。首次创建Singleton服务   要求。然后,所有后续后续操作都使用同一实例   要求。因此,一般而言,仅创建一次Singleton服务   每个应用程序,并且在整个过程中使用单个实例   应用程序生命周期。

     

AddTransient()-此方法创建一个Transient服务。一个新的   每次请求时都会创建一个瞬态服务实例。

     

AddScoped()-此方法创建一个范围服务。的新实例   在范围内,每个请求都会创建一次范围服务。对于   例如,在Web应用程序中,每个http创建1个实例   请求,但在同一实例中的其他调用中使用相同实例   Web请求。

您可以观看展示HERE差异的视频

答案 9 :(得分:-1)

辛格尔顿是什么意思? 单例是仅允许创建其自身的单个实例并提供对该创建实例的访问权的类。它包含静态变量,可以容纳其自身的唯一实例和私有实例。当用户希望将类的实例化限制为仅一个对象时,可以使用它。通常,这在需要单个对象来协调整个系统中的动作时很有用。在Java和.NET等编程语言中,单例模式用于定义全局变量。跨系统使用的单个对象保持不变,只需定义一次即可,无需多次定义。