AddTransient, AddScoped ve AddSingleton Hizmet Farkları


937

ASP.NET Core'da bağımlılık enjeksiyonu (DI) uygulamak istiyorum . Yani bu kodu ConfigureServicesyönteme ekledikten sonra , her iki yol da çalışır.

ASP.NET Core'daki services.AddTransientve service.AddScopedyöntemleri arasındaki fark nedir ?

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

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

92
@tmg Dokümanlar, 'Her istendiğinde geçici yaşam boyu hizmet oluşturulur' diyor. ve 'Kapsamlı ömür boyu hizmetler istek başına bir kez oluşturulur.' İngilizce kavranışım düşündüğümden daha zayıf olmadığı sürece aslında aynı şey demek.
Neutrino

70
@tmg biliyorum. Sadece dokümanların bu noktada hiç net olmadığına dikkat çekiyorum, bu yüzden insanları dokümanlara yönlendirmek çok yararlı değil.
Neutrino

13
@Neutrino, bu yüzden bu soruyu sordum.
Elvin Mammadov

5
Partiye geç, daha sonra yorumları okudum, ama o makaleyi yazdırdım, okudum ve şimdi deNutrino'nun burada yaptığı marjda aynı gözlemi not ettim. Makale, bu analizi sunarken tamamen belirsizdi. Neyse ki, örnek daha az kafa karıştırıcıydı.
Wellspring

5
Anladığım kadarıyla: Geçici ömür boyu hizmetler her talep edildiğinde oluşturulur . Burada talep edilen kelime , bir şey istemenin günlük İngilizce anlamıdır, bu durumda bir hizmettir. Kelime ise isteğe içinde başına isteği bir kez bir HTTP isteğinde belirtmektedir. Ama karışıklığı anlıyorum.
Memet Olsen

Yanıtlar:


1651

TL; DR

Geçici nesneler her zaman farklıdır; her denetleyiciye ve her hizmete yeni bir örnek sağlanır.

Kapsamlı nesneler bir istek içinde aynıdır, ancak farklı isteklerde farklıdır.

Singleton nesneleri her nesne ve her istek için aynıdır.

Daha fazla açıklama için ASP.NET belgelerindeki bu örnek farkı gösterir:

Bu kullanım ömrü ve kayıt seçenekleri arasındaki farkı göstermek için, bir veya daha fazla görevi benzersiz bir tanımlayıcıyla işlem olarak temsil eden basit bir arabirimi düşünün OperationId. Bu hizmetin ömrünü nasıl yapılandırdığımıza bağlı olarak, kapsayıcı, istekte bulunan sınıfa hizmetin aynı veya farklı örneklerini sağlayacaktır. Hangi kullanım ömrünün talep edildiğini netleştirmek için, yaşam boyu seçenek başına bir tür oluşturacağız:

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
    {
    }
}

Bu arayüzleri Operation, yapıcısında bir GUID kabul eden veya hiçbiri yoksa yeni bir GUID kullanan tek bir sınıf kullanarak uygularız :

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

Daha sonra, içinde ConfigureServicesher tür, adlandırılan ömrüne göre kaba eklenir:

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

IOperationSingletonInstanceHizmetin bilinen bir kimliği olan belirli bir örneği kullandığını unutmayın Guid.Empty, bu nedenle bu tür kullanıldığında açık olacaktır. Ayrıca OperationService, diğer hizmetlerin her birine bağlı olan bir kaydı da kaydettik Operation, böylece bu hizmetin her işlem türü için denetleyici ile aynı örneği mi yoksa yeni bir tane mi alıp almadığını net olarak göreceksiniz. Bu hizmetin yaptığı tek şey bağımlılıklarını özellikler olarak göstermektir, böylece görünümde görüntülenebilirler.

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

Uygulamaya yapılan ayrı ayrı istekler içinde ve arasındaki nesne yaşamlarını göstermek için, örnek OperationsControllerher tür IOperationtürün yanı sıra bir OperationService. IndexEylem daha sonra kontrolörün ve hizmetin tüm görüntüler OperationIddeğerler.

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

Şimdi bu denetleyici eylemine iki ayrı istek yapılıyor:

İlk Talep

İkinci istek

OperationIdBir istek içinde ve istekler arasında hangi değerlerin değiştiğini gözlemleyin .

  • Geçici nesneler her zaman farklıdır; her denetleyiciye ve her hizmete yeni bir örnek sağlanır.

  • Kapsamlı nesneler bir istek içinde aynıdır, ancak farklı isteklerde farklıdır

  • Singleton nesneleri her nesne ve her istek için aynıdır (bir örnek sağlanmış olsun veya olmasın ConfigureServices)


14
Her birinin işlevlerini anladım, ancak biri birini diğerinin yerine kullanmanın etkisini açıklayabilir mi? Doğru kullanılmazsa veya başka biri yerine bir tane seçerseniz hangi sorunlara neden olabilir?
pawan nepal

2
Diyelim ki tekil kapsamda bir istek bağlamıyla ilgili nesne (mevcut kullanıcı gibi) oluşturuyorsunuz, o zaman istenmeyen tüm http isteklerinde aynı örnek olarak kalacaktır. IOC tamamen örnek oluşturmakla ilgilidir, bu nedenle oluşturulan örneğin kapsamının ne olduğunu belirtmemiz gerekir.
akazemis

1
o !, Konunun üst kısmındaki linkten bahsetmiştim! örnek kod MS belgelerinden kopyalandı / yapıştırıldı
akazemis

1
Teşekkürler. evet singleton oturum / kullanıcı ne olursa olsun uygulama boyunca aynı olacaktır. Açıkçası uygulamanız mikro hizmet mimarisini kullanıyorsa ve her hizmet ayrı bir işlemde çalışıyorsa, singleton her işlemde aynı olacaktır
akazemis

1
Bize addTransient kullanımına bir örnek verebilir misiniz? çünkü çok fazla kaynak kullanırken kullanmak için herhangi bir yardımcı program bulamadım
Terai

318

.NET'in bağımlılık enjeksiyonunda üç önemli yaşam süresi vardır:

Uygulama boyunca tek bir örnek oluşturan Singleton . Örneği ilk kez oluşturur ve aynı nesneyi tüm çağrılarda yeniden kullanır.

Kapsamlı ömür boyu hizmetler, kapsam dahilinde her talep için bir kez oluşturulur. Mevcut kapsamdaki bir singletona eşdeğerdir. Örneğin, MVC'de her HTTP isteği için bir örnek oluşturur, ancak aynı web isteğindeki diğer çağrılarda aynı örneği kullanır.

Her talep edildiğinde geçici ömür boyu hizmetler oluşturulur. Bu ömür boyu hafif, vatansız hizmetler için en iyi sonucu verir.

Burada farkı görmek için örnekler ve örnekler bulabilirsiniz:

ASP.NET 5 MVC6 6 Adımda Bağımlılık Enjeksiyonu (ölü bağlantı nedeniyle web arşiv bağlantısı)

Bağımlılık Enjeksiyonunuz hazır ASP.NET: ASP.NET 5

Ve bu resmi belgelere bağlantı:

ASP.NET Core'da bağımlılık enjeksiyonu


22
Geçici'nin neden en hafif olduğunu açıklar mısınız? Transient'in en ağır çalışma olduğunu düşündüm çünkü her enjeksiyon için her seferinde bir örnek oluşturması gerekiyor.
Uzman

17
Haklısın. Geçici en hafif değil, sadece hafif RESTful hizmetleri için uygun olduğunu
söyledim

3
Peki hangi senaryoda scoped kullanabiliriz ve örneğin örneğin veritabanından birkaç satır alıyorsak, denetleyici örneğinde hangi geçici süreyi kullanabiliriz? Bu durumda kapsamlı ve geçici kullanım senaryosunu anlamaya çalışıyorum.
sensei

4
gerçekten beklediğiniz mantığa bağlıdır. Örneğin, tek bir db çağrısı ise, hangisini kullandığınızı fark etmez. ancak aynı istekte db'yi birden çok kez çağırıyorsanız, aynı depo nesnesini bellekte tuttuğu ve aynı Http İsteği bağlamında birden çok kez kullandığı için kapsamlandırılmış ömür boyu kullanabilirsiniz. Geçici olan ise yeni bir havuz nesnesini birçok kez oluşturur (ve daha fazla bellek tüketir). Eğer senaryonuzu açıklarsanız hangisinin daha uygun olduğuna karar vermek kolay olurdu.
akazemis

2
Burada vurgulanması gereken önemli noktalardan biri Singleton, Scoped ve Transient'in birbiri içinde Rus hamuru gibidir. Örneğin, yuvalama sırasında sıralarını tersine çevirmek mümkün değildir. Kapsamlı veya tekil bir Geçici'de yer alamaz, çünkü ebeveynin çevreye aykırı olan ömrünü uzatırız!
DL Narasimhan

34

Aynı türden birden fazla nesnenin enjekte edilmesi gerektiğinde, geçici, kapsamlı ve tektonlu ASP.NET MVC çekirdek DI'de nesne oluşturma işlemini tanımlar. Bağımlılık enjeksiyonunda yeniyseniz, bu DI IoC videosunu görebilirsiniz .

Yapıcıda iki "IDal" örneği istediğim aşağıdaki denetleyici kodunu görebilirsiniz. Geçici, Kapsamlı ve Tek Ton, aynı örneğin "_dal" ve "_dal1" ya da farklı olarak enjekte edilip edilmeyeceğini tanımlar.

public class CustomerController : Controller
{
    IDal dal = null;

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

Geçici: Geçici olarak, yeni nesne örnekleri tek bir istek ve yanıtta enjekte edilir. Aşağıda GUID değerlerini görüntülediğim bir anlık görüntü var.

Resim açıklamasını buraya girin

Kapsamlı: Kapsamlı olarak aynı nesne örneği tek bir istek ve yanıtta enjekte edilir.

Resim açıklamasını buraya girin

Singleton: Singleton'da, tüm istek ve yanıtlara aynı nesne enjekte edilir. Bu durumda, nesnenin bir global örneği oluşturulur.

Aşağıda, yukarıdaki temelleri görsel olarak açıklayan basit bir diyagram bulunmaktadır.

MVC DI görüntüsü

Yukarıdaki görüntü Mumbai'de ASP.NET MVC eğitimi alırken SBSS ekibi tarafından çizildi . Yukarıdaki görüntüyü yarattığı için SBSS ekibine çok teşekkür ediyoruz.


9
Bu, şimdiye kadar gördüğüm geçici bir hizmetin en karmaşık açıklamasıdır. Transient = Bu hizmet her çözüldüğünde değişkeninizi atamaya eşdeğerdir new TService. Scoped, bu "kapsam" için ilk başlatılmasını önbelleğe alır (çoğu durumda http isteği). Singleton, uygulamanın ömrü boyunca şimdiye kadar sadece bir örneği önbelleğe alır. Yukarıdaki diyagramlar çok kıvrıktır.
Mardoxx

2
Çok üzgünüm ben daha diyagramları ve kod anlık görüntü ile daha basit hale getirecek düşündüm :-) Ama ne demek istiyorsun.
Shivprasad Koirala

30
  • Singleton, uygulama etki alanının ömrü boyunca tek bir örnektir.
  • Kapsamlı, kapsamı alınmış istek süresi için tek bir örnektir ; ASP.NET'teki HTTP isteği başına anlamına gelir .
  • Geçici, kod isteği başına tek bir örnektir .

Normalde kod isteği, aşağıdaki gibi bir yapıcı parametresi aracılığıyla yapılmalıdır.

public MyConsumingClass(IDependency dependency)

@ Akazemis'in cevabında, DI bağlamında “hizmetlerin” RESTful hizmetleri ima etmediğini belirtmek istedim; hizmetler işlevsellik sağlayan bağımlılıkların uygulanmasıdır.


16

AddSingleton ()

AddSingleton (), ilk istendiğinde hizmetin tek bir örneğini oluşturur ve aynı örneği söz konusu hizmetin gerekli olduğu tüm yerlerde yeniden kullanır.

AddScoped ()

Kapsamlı bir hizmette, her HTTP isteğiyle yeni bir örnek alırız. Ancak, aynı HTTP isteğinde, hizmet görünümde ve denetleyicide olduğu gibi birden çok yerde gerekiyorsa, bu HTTP isteğinin tüm kapsamı için aynı örnek sağlanır. Ancak her yeni HTTP isteği, hizmetin yeni bir örneğini alacaktır.

AddTransient ()

Geçici bir hizmette, aynı HTTP isteği kapsamında olsun ya da farklı HTTP istekleri arasında olsun, bir servis örneği her istendiğinde yeni bir örnek sağlanır.


5

Bu sorunun cevabını aradıktan sonra sizinle paylaşmak istediğim bir örnekle harika bir açıklama buldum.

Farklılıkları gösteren bir video izleyebilirsiniz BURAYA

Bu örnekte şu kod var:

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

Görünüm Oluştur

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

Arasındaki görünümü ve anahtarı oluşturmak butonuna bu kod ve basın Kopyalama-yapıştırma AddSingleton, AddScopedve AddTransienther seferinde bu açıklamayı anlamanıza yardımcı olabilecek olacak farklı bir sonuç alırsınız:

AddSingleton () - Adından da anlaşılacağı gibi, AddSingleton () yöntemi bir Singleton hizmeti oluşturur. İlk talep edildiğinde bir Singleton hizmeti oluşturulur. Aynı örnek daha sonra gelen tüm istekler tarafından kullanılır. Bu nedenle, genel olarak, uygulama başına yalnızca bir kez bir Singleton hizmeti oluşturulur ve bu tek uygulama uygulama ömrü boyunca kullanılır.

AddTransient () - Bu yöntem bir Geçici hizmet oluşturur. Her talep edildiğinde yeni bir Geçici hizmet örneği oluşturulur.

AddScoped () - Bu yöntem bir Kapsamlı hizmet oluşturur. Kapsam kapsamındaki istek başına bir kez Kapsamlı hizmetin yeni bir örneği oluşturulur. Örneğin, bir web uygulamasında her http isteği için 1 örnek oluşturur, ancak aynı örneği aynı web isteği içindeki diğer çağrılarda kullanır.


2

Hangisini Kullanmalı

Geçici

  • her seferinde daha fazla bellek ve Kaynak kullanacakları için oluşturulduklarından ve performans üzerinde olumsuz etkileri olabileceğinden
  • bunu az veya hiç durumu olmayan hafif servisler için kullanın .

Dürbünlü

  • bir istek içinde durumu korumak istediğinizde daha iyi bir seçenek.

Singleton

  • bu hizmetlerde bellek sızıntıları zamanla artar.
  • ayrıca her yerde yeniden kullanıldıklarında oluşturuldukları için bellek verimlidir.

Uygulama genel durumunu korumak istediğiniz yerde Singletons kullanın. Uygulama yapılandırması veya parametreleri, Kayıt Hizmeti, verilerin önbelleğe alınması, tektonları kullanabileceğiniz örneklerden bazılarıdır.

Farklı yaşam sürelerine sahip hizmeti başka birine enjekte etmek

  1. Kapsamlı ve Geçici hizmetleri asla Singleton servisine enjekte etmeyin. (Bu geçici veya kapsamlı hizmeti etkin bir şekilde singletona dönüştürür.)
  2. Geçici hizmetleri asla kapsam kapsamındaki hizmete enjekte etmeyin (Bu, geçici servisi kapsam kapsamına dönüştürür.)

Bu en iyi cevap. Örnek verdiğiniz bölümü seviyorum. Nasıl çalıştıklarını anlamak çok zor değil. Hangi servisi nereye koyacağınızı ve hafızayı nasıl ve ne zaman temizleyeceğini düşünmek çok daha zordur. Bununla ilgili daha fazla açıklama yaparsanız harika olur.
valentasm

1

As açıklanan burada , bir örnekle (bu bağlantının çok yararlıdır)

Arabirim ve somut tür arasındaki bu eşleme, her tür IContryService isteğinde bulunduğunuzda, yeni bir CountryService örneği alacağınızı tanımlar. Bu durumda geçici olan budur. Ayrıca tekli eşlemeler (AddSingleton kullanarak) ve kapsamlı eşlemeler (AddScoped kullanarak) ekleyebilirsiniz. Bu durumda kapsam, bir HTTP isteğinin kapsamı anlamına gelir, bu da geçerli istek çalışırken bunun tek birton olduğu anlamına gelir. AddInstance yöntemini kullanarak DI kapsayıcısına varolan bir örneği de ekleyebilirsiniz. Bunlar, IServiceCollection'a kaydolmak için neredeyse eksiksiz yollardır


1

AddSingleton vs AddScoped vs AddTransient arasındaki fark

Kayıt Hizmetleri

ASP.NET çekirdek hizmetleri bağımlılık enjeksiyon kapsayıcısı ile kaydetmek için aşağıdaki 3 yöntemi sağlar. Kullandığımız yöntem, kayıtlı hizmetin ömrünü belirler.

AddSingleton () - Adından da anlaşılacağı gibi, AddSingleton () yöntemi bir Singleton hizmeti oluşturur. İlk talep edildiğinde bir Singleton hizmeti oluşturulur. Aynı örnek daha sonra gelen tüm istekler tarafından kullanılır. Bu nedenle, genel olarak, uygulama başına yalnızca bir kez bir Singleton hizmeti oluşturulur ve bu tek uygulama uygulama ömrü boyunca kullanılır.

AddTransient () - Bu yöntem bir Geçici hizmet oluşturur. Her talep edildiğinde yeni bir Geçici hizmet örneği oluşturulur.

AddScoped () - Bu yöntem bir Kapsamlı hizmet oluşturur. Kapsam kapsamındaki istek başına bir kez Kapsamlı hizmetin yeni bir örneği oluşturulur. Örneğin, bir web uygulamasında her http isteği için 1 örnek oluşturur, ancak aynı örneği aynı web isteği içindeki diğer çağrılarda kullanır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.