AddTransient, AddScoped 및 AddSingleton Services의 차이점


937

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>();
}

92
@tmg 문서에서 '일시적인 평생 서비스는 요청 될 때마다 생성됩니다.' ‘범위 된 평생 서비스는 요청 당 한 번 생성됩니다.’ 영어에 대한 이해력이 약하지 않으면 실제로 똑같은 것을 의미한다고 생각했습니다.
Neutrino

70
@tmg 알고 있습니다. 나는이 시점에서 문서가 전혀 명확하지 않다는 것을 지적하고 있으므로 사람들에게 문서를 가리키는 것은별로 도움이되지 않습니다.
Neutrino

13
@ Neutrino, 그래서 나는이 질문을했습니다.
Elvin Mammadov

5
파티에 늦게 의견을 읽었지만 나중에 그 기사를 인쇄하여 읽은 다음 @Neutrino가 여기에서 만든 것과 동일한 관찰 결과를 얻었습니다. 이 기사는 그 분석을 제공하는 데 완전히 애매했습니다. 고맙게도 그 예는 덜 혼란 스러웠습니다.
Wellspring

5
내가 아는 한 : 일시적인 평생 서비스는 요청 될 때마다 생성됩니다 . 여기에 요청 된 단어 는 무언가를 요구하는 일상적인 영어 의미,이 경우 서비스입니다. 단어 반면 요청따라 요청하면 HTTP 요청을 의미합니다. 그러나 나는 그 혼란을 이해한다.
Memet Olsen

답변:


1648

TL; DR

일시적인 객체는 항상 다릅니다. 모든 컨트롤러와 모든 서비스에 새로운 인스턴스가 제공됩니다.

범위가 지정된 개체는 요청 내에서는 동일하지만 요청마다 다릅니다.

싱글 톤 객체는 모든 객체와 모든 요청에 ​​대해 동일합니다.

더 명확히하기 위해이 예제는 ASP.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각 유형의 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();
        }
    }
}

이제이 제어기 조치에 대해 두 개의 개별 요청이 작성됩니다.

첫 번째 요청

두 번째 요청

OperationId요청 내에서 그리고 요청간에 어떤 값이 달라지는 지 확인 하십시오 .

  • 일시적인 객체는 항상 다릅니다. 모든 컨트롤러와 모든 서비스에 새로운 인스턴스가 제공됩니다.

  • 범위가 지정된 객체는 요청 내에서는 동일하지만 요청마다 다릅니다.

  • 싱글 개체 (무관 인스턴스가 설치되어 있는지의 각 개체마다 요청에 대해 동일하다 ConfigureServices)


14
나는 각각의 기능을 이해했지만 누군가가 다른 것을 사용하는 것의 영향을 설명 할 수 있습니다. 올바르게 사용하지 않거나 다른 것을 선택하면 어떤 문제가 발생할 수 있습니다.
pawan nepal

2
싱글 톤 범위로 요청 컨텍스트 관련 객체 (현재 사용자와 같은)를 만들고 있다고 가정하면 원하지 않는 모든 http 요청에서 동일한 인스턴스를 유지합니다. IOC는 인스턴스 생성에 관한 것이므로 생성 된 인스턴스의 범위를 지정해야합니다.
akazemis

1
그것은 주제의 상단에 링크를 언급했습니다! 샘플 코드는 MS 문서에서 복사 / 붙여 넣기
akazemis

1
감사. 예. 싱글 톤은 세션 / 사용자에 관계없이 앱 전체에서 동일합니다. 앱이 microservices 아키텍처와 별도의 프로세스에서 각 서비스의 실행을 사용하는 경우 분명히, 싱글은 각 공정에서 동일합니다
akazemis

1
addTransient 사용의 예를 알려주시겠습니까? 리소스를 너무 많이 사용하는 동안 유틸리티를 찾지 못했기 때문에
Terai

318

.NET의 의존성 주입에는 세 가지 주요 수명이 있습니다.

단일 응용 프로그램 전체에 단일 인스턴스를 만듭니다. 처음으로 인스턴스를 생성하고 모든 호출에서 동일한 객체를 재사용합니다.

범위 수명 서비스는 범위 내에서 요청 당 한 번 작성됩니다. 현재 범위의 싱글 톤과 같습니다. 예를 들어, MVC에서는 각 HTTP 요청에 대해 하나의 인스턴스를 작성하지만 동일한 웹 요청 내의 다른 호출에서 동일한 인스턴스를 사용합니다.

과도 현상 수명 서비스는 요청 될 때마다 생성됩니다. 이 수명은 가벼운 상태 비 저장 서비스에 가장 적합합니다.

차이점을 확인할 수있는 예제와 예제는 다음과 같습니다.

6 단계의 ASP.NET 5 MVC6 종속성 주입 (불량 링크로 인한 웹 아카이브 링크)

의존성 주입 준비 ASP.NET : ASP.NET 5

그리고 이것은 공식 문서에 대한 링크입니다.

ASP.NET Core에서 종속성 주입


22
Transient가 왜 가장 가벼운 지 설명해 주시겠습니까? 나는 Transient가 매번 주입 할 때마다 인스턴스를 만들어야하기 때문에 가장 무거운 작업이라고 생각했습니다.
전문가가

17
네가 옳아. 과도는 가장 가벼운 것이 아닙니다. 나는 단지 가벼운 RESTful 서비스에 적합하다고 말했습니다 :)
akazemis

3
따라서 어떤 시나리오에서 범위 예를 사용할 수 있습니까? 예를 들어 데이터베이스에서 몇 개의 행을 검색하는 경우 컨트롤러 예에서 어떤 과도 상태를 사용할 수 있습니까? 이 경우 범위 대 일시적 사용 시나리오를 이해하려고합니다.
sensei

4
그것은 실제로 당신이 기대하는 논리에 달려 있습니다. 예를 들어, 단일 DB 호출 인 경우 실제로 사용중인 DB 호출과 아무런 차이가 없습니다. 그러나 동일한 요청에서 db를 여러 번 호출하는 경우 메모리에 동일한 저장소 객체를 유지하고 동일한 Http Request 컨텍스트 내에서 여러 번 재사용하므로 범위 수명을 사용할 수 있습니다. 일시적인 객체는 새로운 저장소 객체를 여러 번 생성하고 더 많은 메모리를 소비합니다. 특정 시나리오를 설명하면 어느 것이 더 적합한 지 쉽게 판단 할 수 있습니다.
akazemis

2
여기서 강조해야 할 중요한 점 중 하나는 Singleton, Scoped 및 Transient는 러시아 도일과 같으며 하나는 다른 것입니다. 예를 들어, 중첩시 순서를 반대로 바꿀 수 없습니다. 스코프 또는 싱글 톤은 트랜지언트에 포함될 수 없습니다. 우리는 격리에 반대하는 부모의 수명을 연장 할 것입니다!
DL Narasimhan

34

과도, 범위 및 싱글 톤은 동일한 유형의 여러 개체를 주입해야하는 경우 ASP.NET MVC 코어 DI에서 개체 생성 프로세스를 정의합니다. 의존성 주입을 처음 사용하는 경우이 DI IoC 비디오를 볼 수 있습니다 .

생성자에서 "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 값을 표시 한 스냅 샷 이미지입니다.

여기에 이미지 설명을 입력하십시오

범위 : 범위에서, 동일한 객체 인스턴스가 단일 요청 및 응답으로 주입됩니다.

여기에 이미지 설명을 입력하십시오

싱글 톤 : 싱글 톤에서는 모든 요청과 응답에 동일한 객체가 주입됩니다. 이 경우 객체의 하나의 전역 인스턴스가 생성됩니다.

아래는 위의 기본 사항을 시각적으로 설명하는 간단한 다이어그램입니다.

MVC DI 이미지

위 이미지는 뭄바이에서 ASP.NET MVC 교육을받을 때 SBSS 팀이 그린 것 입니다. 위 이미지를 만든 SBSS 팀에게 큰 감사를드립니다.


9
이것은 내가 본 임시 서비스에 대한 가장 복잡한 단일 설명입니다. 일시적 =이 서비스가 해결 될 때마다 변수를 할당하는 것과 같습니다 new TService. Scoped는 해당 "범위"에 대한 최초 초기화를 캐시합니다 (대부분의 경우 http 요청). Singleton은 애플리케이션 수명 동안 단 하나의 인스턴스 만 캐시합니다. 위의 다이어그램은 매우 복잡합니다.
Mardoxx

2
미안하지만 다이어그램과 코드 스냅 샷으로 더 간단하게 만들 것이라고 생각했습니다. :-)하지만 요점을 알 수 있습니다.
Shivprasad Koirala 2

30
  • 싱글 톤은 애플리케이션 도메인 수명 동안 단일 인스턴스입니다.
  • 범위 지정은 범위 지정된 요청 기간 동안 단일 인스턴스이며 이는 ASP.NET의 HTTP 요청 당을 의미 합니다.
  • 임시는 코드 요청 당 단일 인스턴스입니다 .

일반적으로 코드 요청은 다음과 같이 생성자 매개 변수를 통해 이루어져야합니다.

public MyConsumingClass(IDependency dependency)

나는 DI의 맥락에서 "서비스"가 RESTful 서비스를 의미하지 않는다는 @akazemis의 대답에서 지적하고 싶었다. 서비스는 기능을 제공하는 종속성의 구현입니다.


16

AddSingleton ()

AddSingleton ()은 처음 요청 될 때 서비스의 단일 인스턴스를 작성하고 해당 서비스가 필요한 모든 장소에서 동일한 인스턴스를 재사용합니다.

AddScoped ()

범위가 지정된 서비스에서 모든 HTTP 요청과 함께 새 인스턴스를 얻습니다. 그러나 동일한 HTTP 요청 내에서보기 및 컨트롤러와 같이 여러 위치에서 서비스가 필요한 경우 해당 HTTP 요청의 전체 범위에 대해 동일한 인스턴스가 제공됩니다. 그러나 모든 새로운 HTTP 요청은 서비스의 새로운 인스턴스를 얻게됩니다.

AddTransient ()

임시 서비스를 사용하면 서비스 인스턴스가 동일한 HTTP 요청 범위에 있는지 또는 다른 HTTP 요청에 관계없이 요청 될 때마다 새 인스턴스가 제공됩니다.


5

이 질문에 대한 답을 찾은 후에 나는 당신과 공유하고 싶은 예를 통해 훌륭한 설명을 발견했습니다.

당신은 차이점을 보여 비디오 볼 수 있습니다 여기를

이 예제에는 다음과 같은 코드가 있습니다.

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>();
}

사이보기 및 스위치에서 생성 버튼이 코드를 눌러를 복사 - 붙여 넣기 AddSingleton, AddScoped그리고 AddTransient당신은 때마다이 설명을 이해하는 데 도움이 될 것입니다 다른 결과를 얻을 것이다 :

AddSingleton () -이름에서 알 수 있듯이 AddSingleton () 메서드는 Singleton 서비스를 만듭니다. 싱글 톤 서비스는 처음 요청 될 때 생성됩니다. 이 동일한 인스턴스는 모든 후속 요청에서 사용됩니다. 따라서 일반적으로 Singleton 서비스는 응용 프로그램 당 한 번만 생성되며 해당 단일 인스턴스는 응용 프로그램 수명 기간 동안 사용됩니다.

AddTransient () -이 메소드는 임시 서비스를 작성합니다. Transient 서비스의 새 인스턴스는 요청 될 때마다 작성됩니다.

AddScoped () -이 메소드는 범위가 지정된 서비스를 작성합니다. 범위 내에서 요청 당 한 번씩 범위 서비스의 새 인스턴스가 작성됩니다. 예를 들어, 웹 애플리케이션에서 각 http 요청 당 하나의 인스턴스를 작성하지만 동일한 웹 요청 내의 다른 호출에서 동일한 인스턴스를 사용합니다.


2

사용할 것

과도 현상

  • 매번 생성되므로 더 많은 메모리 와 리소스를 사용하고 성능에 부정적인 영향을 줄 수 있습니다
  • 상태 가 거의 또는 전혀없는 가벼운 서비스에 이것을 사용하십시오 .

범위가 지정된

  • 요청 내에서 상태를 유지하려는 경우 더 나은 옵션입니다.

하나씩 일어나는 것

  • 이러한 서비스에서 메모리 누출은 시간이 지남에 따라 쌓입니다.
  • 또한 언제 어디서나 재사용 할 수있어 메모리 효율성이 뛰어납니다.

애플리케이션 전체 상태를 유지해야하는 경우 싱글 톤을 사용하십시오. 응용 프로그램 구성 또는 매개 변수, 로깅 서비스, 데이터 캐싱은 싱글 톤을 사용할 수있는 몇 가지 예입니다.

수명이 다른 서비스를 다른 서비스에 주입

  1. Scoped & Transient 서비스를 Singleton 서비스에 주입하지 마십시오. (이 기능은 일시적 또는 범위가 지정된 서비스를 싱글 톤으로 효과적으로 변환합니다.)
  2. 일시적 서비스를 범위 서비스에 주입하지 마십시오 (이는 일시적 서비스를 범위 서비스 로 변환합니다).

이것이 가장 좋은 대답입니다. 나는 당신이 예제를 제공하는 부분을 좋아합니다. 그들이 어떻게 작동하는지 이해하는 것은 그리 어렵지 않습니다. 어느 서비스를 어디에, 언제 어떻게 메모리를 정리했는지 생각하기가 더 어렵습니다. 그것에 대해 더 설명하면 좋을 것입니다.
발렌시아

1

여기에 설명 된 것처럼 (이 링크는 매우 유용합니다)

인터페이스와 구체적 유형 간의 이러한 매핑은 IContryService 유형을 요청할 때마다 CountryService의 새 인스턴스를 얻도록 정의합니다. 이것이이 경우 일시적인 의미입니다. 또한 싱글 톤 매핑 (AddSingleton 사용)과 범위 매핑 (AddScoped 사용)을 추가 할 수 있습니다. 이 경우 범위가 지정된 것은 HTTP 요청으로 범위가 지정된 것을 의미하며, 현재 요청이 실행되는 동안 단일 요청임을 의미합니다. AddInstance 메소드를 사용하여 기존 인스턴스를 DI 컨테이너에 추가 할 수도 있습니다. 이것들은 IServiceCollection에 등록하는 거의 완전한 방법입니다


1

AddSingleton과 AddScoped 및 AddTransient의 차이점

서비스 등록

ASP.NET 코어는 종속성 주입 컨테이너에 서비스를 등록하는 다음 3 가지 방법을 제공합니다. 우리가 사용하는 방법은 등록 된 서비스의 수명을 결정합니다.

AddSingleton ()- 이름에서 알 수 있듯이 AddSingleton () 메서드는 Singleton 서비스를 만듭니다. 싱글 톤 서비스는 처음 요청 될 때 생성됩니다. 그런 다음이 같은 인스턴스는 모든 후속 요청에서 사용됩니다. 따라서 일반적으로 Singleton 서비스는 응용 프로그램 당 한 번만 만들어지며 해당 단일 인스턴스는 응용 프로그램 수명 동안 사용됩니다.

AddTransient ()- 이 메소드는 임시 서비스를 작성합니다. Transient 서비스의 새 인스턴스는 요청 될 때마다 작성됩니다.

AddScoped ()- 이 메소드는 범위가 지정된 서비스를 작성합니다. 범위 내에서 요청 당 한 번씩 범위 서비스의 새 인스턴스가 작성됩니다. 예를 들어, 웹 애플리케이션에서 각 http 요청 당 하나의 인스턴스를 작성하지만 동일한 웹 요청 내의 다른 호출에서 동일한 인스턴스를 사용합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.