질문자 :Elvin Mammadov
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>(); }
TL;DR
일시적인 개체는 항상 다릅니다. 모든 컨트롤러와 모든 서비스에 새 인스턴스가 제공됩니다.
범위가 지정된 개체는 요청 내에서 동일하지만 다른 요청에서 다릅니다.
싱글톤 객체는 모든 객체와 모든 요청에 대해 동일합니다.
더 명확한 설명을 위해 .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 { } }
우리는 생성자에서 GUID를 허용하거나 제공되지 않은 경우 새 GUID를 Operation
사용하여 이러한 인터페이스를 구현합니다.
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
인 특정 인스턴스를 사용하고 있으므로 이 유형이 사용 중일 때 명확해질 것입니다. 또한 다른 각각의 Operation
OperationService
를 등록하여 이 서비스가 각 작업 유형에 대해 컨트롤러와 동일한 인스턴스를 가져오는지 아니면 새 인스턴스를 가져오는지 여부를 요청 내에서 명확하게 알 수 있습니다. 이 서비스는 종속성을 속성으로 노출하여 뷰에 표시할 수 있습니다.
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
값이 요청 내에서 그리고 요청 간에 달라지는지 관찰하십시오.
일시적인 개체는 항상 다릅니다. 모든 컨트롤러와 모든 서비스에 새 인스턴스가 제공됩니다.
범위가 지정된 개체는 요청 내에서 동일하지만 다른 요청에서 다릅니다.
Singleton 개체는 모든 개체 및 모든 요청에 대해 동일합니다(인스턴스가 ConfigureServices
에서 제공되는지 여부에 관계 없음).
akazemis.NET의 종속성 주입에는 세 가지 주요 수명이 있습니다.
애플리케이션 전체에 단일 인스턴스를 생성하는 싱글 톤. 처음으로 인스턴스를 만들고 모든 호출에서 동일한 개체를 재사용합니다.
범위가 지정된 수명 서비스는 범위 내에서 요청당 한 번 생성됩니다. 현재 범위의 싱글톤과 동일합니다. 예를 들어 MVC에서는 각 HTTP 요청에 대해 하나의 인스턴스를 생성하지만 동일한 웹 요청 내에서 다른 호출에서 동일한 인스턴스를 사용합니다.
임시 평생 서비스는 요청할 때마다 생성됩니다. 이 수명은 경량의 상태 비저장 서비스에 가장 적합합니다.
여기에서 차이점을 확인하기 위한 예제와 찾을 수 있습니다.
ASP.NET 5 MVC6 6단계 종속성 주입 (데드 링크로 인한 웹 아카이브 링크)
의존성 주입 준비 ASP.NET : ASP.NET 5
그리고 이것은 공식 문서에 대한 링크입니다.
ASP.NET Core의 종속성 주입
akazemis어느 것을 사용할 것인가
과도 현상
- 매번 생성되기 때문에 더 많은 메모리 와 리소스를 사용하고 성능에 부정적인 영향을 미칠 수 있습니다.
- 상태가 거의 또는 전혀 없는 경량 서비스에 이것을 사용합니다.
범위 지정
- 요청 내에서 상태를 유지하려는 경우 더 나은 옵션입니다.
하나씩 일어나는 것
- 이러한 서비스의 메모리 누수는 시간이 지남에 따라 누적됩니다.
- 또한 한 번 생성되면 모든 곳에서 재사용되므로 메모리 효율적입니다.
애플리케이션 전체 상태를 유지해야 하는 경우 싱글톤을 사용하십시오. 애플리케이션 구성 또는 매개변수, 로깅 서비스, 데이터 캐싱은 싱글톤을 사용할 수 있는 몇 가지 예입니다.
수명이 다른 서비스를 다른 서비스에 주입
- Scoped & Transient 서비스를 Singleton 서비스에 주입하지 마십시오. ( 이렇게 하면 일시적 또는 범위가 지정된 서비스를 싱글톤으로 효과적으로 변환합니다.)
- 일시적인 서비스를 범위가 지정된 서비스에 삽입하지 마십시오 (이는 일시적인 서비스를 범위가 지정된 서비스로 변환합니다.)
bereket gebredingleTransient, scoped 및 singleton 은 동일한 유형의 여러 개체를 주입해야 하는 경우 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 값을 표시한 스냅샷 이미지입니다.
범위 지정: 범위 지정에서는 단일 요청 및 응답에 동일한 개체 인스턴스가 주입됩니다.
싱글톤: 싱글톤에서는 모든 요청과 응답에 동일한 객체가 주입됩니다. 이 경우 개체의 전역 인스턴스 하나가 생성됩니다.
다음은 위의 기본 사항을 시각적으로 설명하는 간단한 다이어그램입니다.
위의 이미지는 내가 뭄바이에서 ASP.NET MVC 교육을 받을 때 SBSS 팀에서 그린 것입니다. 위의 이미지를 만들어주신 SBSS팀에 깊은 감사를 드립니다.
Shivprasad Koirala- Singleton은 애플리케이션 도메인의 수명 동안 단일 인스턴스입니다.
- 범위 지정은 범위 지정 요청 기간 동안 단일 인스턴스입니다. 이는 ASP.NET의 HTTP 요청당을 의미합니다.
- Transient는 코드 요청당 단일 인스턴스입니다.
일반적으로 코드 요청은 다음과 같이 생성자 매개변수를 통해 이루어져야 합니다.
public MyConsumingClass(IDependency dependency)
@akazemis의 답변에서 DI의 맥락에서 "서비스"가 RESTful 서비스를 의미하지 않는다는 점을 지적하고 싶었습니다. 서비스는 기능을 제공하는 종속성의 구현입니다.
user1969177AddSingleton()
AddSingleton()은 서비스가 처음 요청될 때 서비스의 단일 인스턴스를 만들고 해당 서비스가 필요한 모든 위치에서 동일한 인스턴스를 재사용합니다.
범위 추가()
범위가 지정된 서비스에서는 모든 HTTP 요청과 함께 새 인스턴스를 얻습니다. 그러나 동일한 HTTP 요청 내에서 보기 및 컨트롤러와 같은 여러 위치에서 서비스가 필요한 경우 해당 HTTP 요청의 전체 범위에 대해 동일한 인스턴스가 제공됩니다. 그러나 모든 새 HTTP 요청은 서비스의 새 인스턴스를 가져옵니다.
AddTransient()
임시 서비스를 사용하면 동일한 HTTP 요청 범위에 있든 다른 HTTP 요청에 걸쳐 있든 서비스 인스턴스가 요청될 때마다 새 인스턴스가 제공됩니다.
Yasser Shaikh이 이미지는 이 개념을 잘 보여줍니다. 불행히도 이 이미지의 원본 소스를 찾을 수 없었지만 누군가가 그것을 만들었습니다. 그는 이 개념을 이미지의 형태로 아주 잘 보여주었습니다.
Hamed Naeemaei이 질문에 대한 답을 찾은 후 여러분과 공유하고 싶은 예가 있는 훌륭한 설명을 찾았습니다.
여기 에서 차이점을 보여주는 비디오를 볼 수 있습니다.
이 예에서는 다음과 같은 코드가 있습니다.
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; } }
홈 컨트롤러
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() - 이 메서드는 일시적인 서비스를 만듭니다. 요청될 때마다 임시 서비스의 새 인스턴스가 생성됩니다.
AddScoped() - 이 메서드는 범위가 지정된 서비스를 만듭니다. 범위 지정 서비스의 새 인스턴스는 범위 내에서 요청당 한 번 생성됩니다. 예를 들어 웹 애플리케이션에서는 각 http 요청당 1개의 인스턴스를 생성하지만 동일한 웹 요청 내의 다른 호출에서는 동일한 인스턴스를 사용합니다.
Offir출처 : http:www.stackoverflow.com/questions/38138100/addtransient-addscoped-and-addsingleton-services-differences