Perbedaan Layanan AddTransient, AddScoped, dan AddSingleton

938

Saya ingin menerapkan injeksi ketergantungan (DI) di ASP.NET Core. Jadi setelah menambahkan kode ini ke ConfigureServicesmetode, kedua cara kerjanya.

Apa perbedaan antara metode services.AddTransientdan service.AddScopeddalam ASP.NET Core?

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

    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddScoped<IEmailSender, AuthMessageSender>();
}
Elvin Mammadov
sumber
92
@tmg Dokumen mengatakan 'Layanan seumur hidup sementara dibuat setiap kali diminta.' dan 'Layanan seumur hidup yang tercakup dibuat sekali per permintaan.' yang kecuali pemahaman bahasa Inggris saya lebih lemah dari yang saya kira benar-benar berarti hal yang sama persis.
Neutrino
70
@ tmg saya tahu. Saya hanya menunjukkan bahwa dokumen sama sekali tidak jelas tentang hal ini, jadi mengarahkan orang ke dokumen tidak sangat membantu.
Neutrino
13
@Neutrino, itu sebabnya saya menanyakan pertanyaan ini.
Elvin Mammadov
5
Terlambat ke pesta, membaca komentar bahkan kemudian, tetapi saya mencetak artikel itu, membacanya, dan menuliskan pengamatan yang sama di margin yang sekarang saya lihat @Nutrino dibuat di sini. Artikel itu BENAR-BENAR samar dalam menawarkan analisis itu. Untungnya, contohnya kurang membingungkan.
Mata air
5
Sejauh yang saya mengerti: Layanan seumur hidup sementara dibuat setiap kali mereka diminta . Kata yang diminta di sini adalah makna bahasa Inggris sehari-hari untuk meminta sesuatu, dalam hal ini layanan. Sedangkan permintaan kata dalam sekali per permintaan mengacu pada Permintaan HTTP. Tapi saya mengerti masalahnya.
Memet Olsen

Jawaban:

1655

TL; DR

Objek transien selalu berbeda; contoh baru disediakan untuk setiap pengontrol dan setiap layanan.

Objek yang dilingkupi sama dalam permintaan, tetapi berbeda di antara permintaan yang berbeda.

Objek singleton adalah sama untuk setiap objek dan setiap permintaan.

Untuk klarifikasi lebih lanjut, contoh ini dari dokumentasi ASP.NET menunjukkan perbedaan:

Untuk menunjukkan perbedaan antara opsi masa hidup dan pendaftaran ini, pertimbangkan antarmuka sederhana yang mewakili satu atau beberapa tugas sebagai operasi dengan pengidentifikasi unik OperationId,. Bergantung pada bagaimana kami mengonfigurasi masa pakai untuk layanan ini, wadah akan memberikan contoh layanan yang sama atau berbeda untuk kelas yang meminta. Untuk memperjelas masa hidup yang diminta, kami akan membuat satu jenis opsi seumur hidup:

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

Kami mengimplementasikan antarmuka ini menggunakan kelas tunggal Operation,, yang menerima GUID dalam konstruktornya, atau menggunakan GUID baru jika tidak ada yang disediakan:

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

Selanjutnya, dalam ConfigureServices, setiap jenis ditambahkan ke wadah sesuai dengan masa pakai namanya:

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

Perhatikan bahwa IOperationSingletonInstancelayanan ini menggunakan instance khusus dengan ID yang diketahui Guid.Empty, sehingga akan menjadi jelas saat jenis ini digunakan. Kami juga telah mendaftarkan OperationServiceyang tergantung pada masing-masing Operationjenis lainnya , sehingga akan menjadi jelas dalam permintaan apakah layanan ini mendapatkan instance yang sama dengan controller, atau yang baru, untuk setiap jenis operasi. Semua layanan ini lakukan adalah mengekspos dependensi sebagai properti, sehingga mereka dapat ditampilkan dalam tampilan.

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

Untuk menunjukkan masa hidup objek di dalam dan di antara permintaan individu yang terpisah ke aplikasi, sampel menyertakan OperationsControlleryang meminta setiap jenis IOperationjenis serta OperationService. The Indexaksi kemudian menampilkan semua controller dan layanan ini OperationIdnilai-nilai.

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

Sekarang dua permintaan terpisah dibuat untuk tindakan pengontrol ini:

Permintaan Pertama

Permintaan kedua

Amati OperationIdnilai mana yang bervariasi dalam satu permintaan, dan antara permintaan.

  • Objek transien selalu berbeda; contoh baru disediakan untuk setiap pengontrol dan setiap layanan.

  • Objek yang dilingkupi sama dalam permintaan, tetapi berbeda di antara permintaan yang berbeda

  • Objek singleton adalah sama untuk setiap objek dan setiap permintaan (terlepas dari apakah instance disediakan di ConfigureServices)

akazemis
sumber
14
Saya mengerti fungsi masing-masing, tetapi dapatkah seseorang menjelaskan dampak dari penggunaan satu daripada yang lain. Masalah apa yang mungkin terjadi jika tidak digunakan dengan benar atau memilih salah satu dari yang lain.
pawan nepal
2
Katakanlah Anda sedang membuat objek terkait konteks permintaan (seperti pengguna saat ini) dengan lingkup tunggal maka akan tetap menjadi contoh yang sama di semua permintaan http yang tidak diinginkan. IOC adalah tentang membuat instance, jadi kita perlu menentukan apa ruang lingkup instance yang dibuat.
akazemis
1
itu !, saya telah menyebutkan tautan di bagian atas topik! kode sampel disalin / disisipkan dari MS docs
akazemis
1
Terima kasih. ya singleton akan sama di seluruh aplikasi terlepas dari sesi / pengguna. jelas jika aplikasi Anda menggunakan arsitektur layanan mikro dan setiap layanan berjalan dalam proses terpisah, singleton akan sama dalam setiap proses
akazemis
1
Bisakah Anda memberi kami contoh penggunaan addTransient? karena saya tidak menemukan utilitas untuk menggunakannya saat menggunakan terlalu banyak sumber daya
Terai
319

Dalam injeksi dependensi .NET ada tiga masa hidup utama:

Singleton yang menciptakan satu instance di seluruh aplikasi. Ini menciptakan instance untuk pertama kalinya dan menggunakan kembali objek yang sama di semua panggilan.

Layanan seumur hidup yang tercakup dibuat sekali per permintaan dalam ruang lingkup. Ini setara dengan singleton dalam lingkup saat ini. Misalnya, dalam MVC ia membuat satu instance untuk setiap permintaan HTTP, tetapi ia menggunakan instance yang sama dalam panggilan lain dalam permintaan web yang sama.

Layanan seumur hidup sementara dibuat setiap kali mereka diminta. Seumur hidup ini bekerja paling baik untuk layanan yang ringan dan tanpa kewarganegaraan.

Di sini Anda dapat menemukan dan contoh untuk melihat perbedaannya:

ASP.NET 5 MVC6 Ketergantungan Injeksi dalam 6 Langkah (tautan arsip web karena tautan mati)

Injeksi Ketergantungan Anda siap ASP.NET: ASP.NET 5

Dan ini adalah tautan ke dokumentasi resmi:

Ketergantungan injeksi dalam ASP.NET Core

akazemis
sumber
22
Bisakah Anda jelaskan mengapa Transient adalah yang paling ringan? Saya pikir Transient adalah pekerjaan yang paling berat karena perlu membuat contoh setiap waktu untuk setiap injeksi.
Pakar ingin jadi
17
Kamu benar. Transient bukan yang paling ringan, saya hanya mengatakan itu cocok untuk layanan
RESTful yang
3
Jadi dalam skenario mana kita bisa menggunakan scoped dan transient dalam contoh controller misalnya jika kita mengambil beberapa baris dari database? Saya mencoba memahami skenario penggunaan scoped vs transient dalam kasus ini.
sensei
4
itu benar-benar tergantung pada logika yang Anda harapkan. Misalnya, jika itu panggilan db tunggal, sebenarnya tidak ada bedanya yang mana yang Anda gunakan. tetapi jika Anda menelepon db beberapa kali dalam permintaan yang sama, maka Anda dapat menggunakan scoped Seumur Hidup, karena menyimpan objek repositori yang sama dalam memori dan menggunakan kembali beberapa kali dalam konteks Permintaan Http yang sama. Sedangkan yang sementara membuat objek repositori baru beberapa kali (dan menghabiskan lebih banyak memori). Jika Anda menjelaskan skenario spesifik Anda, akan mudah untuk menilai mana yang lebih cocok.
akazemis
3
Satu poin penting untuk disoroti di sini adalah Singleton, Scoped dan Transient seperti doill Rusia, satu di dalam yang lain. Tidak mungkin membalik urutannya saat bersarang, misalnya. scoped atau singleton tidak dapat dimuat dalam Transient, karena kita akan memperpanjang umur induknya yang bertentangan dengan penahanan!
DL Narasimhan
34

Transient, scoped, dan singleton mendefinisikan proses pembuatan objek dalam ASP.NET MVC core DI ketika beberapa objek dari jenis yang sama harus disuntikkan. Jika Anda baru mengenal injeksi ketergantungan, Anda dapat melihat video DI IoC ini .

Anda dapat melihat kode pengontrol di bawah ini di mana saya telah meminta dua contoh "IDal" di konstruktor. Transient, Scoped, dan Singleton menentukan apakah instance yang sama akan disuntikkan dalam "_dal" dan "_dal1" atau berbeda.

public class CustomerController : Controller
{
    IDal dal = null;

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

Transient: Dalam transient, instance objek baru akan disuntikkan dalam satu permintaan dan respons. Di bawah ini adalah gambar snapshot tempat saya menampilkan nilai-nilai GUID.

Masukkan deskripsi gambar di sini

Scoped: Dalam scoped, instance objek yang sama akan disuntikkan dalam satu permintaan dan respons.

Masukkan deskripsi gambar di sini

Singleton: Dalam singleton, objek yang sama akan disuntikkan di semua permintaan dan tanggapan. Dalam hal ini satu instance global objek akan dibuat.

Di bawah ini adalah diagram sederhana yang menjelaskan dasar secara visual di atas.

Gambar MVC DI

Gambar di atas dibuat oleh tim SBSS ketika saya mengikuti pelatihan ASP.NET MVC di Mumbai . Terima kasih banyak kepada tim SBSS untuk membuat gambar di atas.

Shivprasad Koirala
sumber
9
Ini adalah satu-satunya penjelasan paling rumit dari layanan sementara yang pernah saya lihat. Transient = Setiap kali layanan ini diselesaikan sama dengan menugaskan variabel Anda new TService. Scoped akan melakukan cache inisialisasi pertama untuk "ruang lingkup" (permintaan http dalam kebanyakan kasus). Singleton hanya akan melakukan cache satu kali selama masa aplikasi, Sederhana seperti itu. Diagram di atas sangat berbelit-belit.
Mardoxx
2
Maaf saya pikir saya akan membuatnya lebih sederhana dengan diagram dan snapshot kode :-) Tapi saya mengerti maksud Anda.
Shivprasad Koirala
30
  • Singleton adalah contoh tunggal untuk masa berlaku domain aplikasi.
  • Scoped adalah contoh tunggal untuk durasi permintaan scoped, yang berarti per permintaan HTTP di ASP.NET.
  • Transient adalah contoh tunggal per permintaan kode .

Biasanya permintaan kode harus dilakukan melalui parameter konstruktor, seperti pada

public MyConsumingClass(IDependency dependency)

Saya ingin menunjukkan jawaban @ akazemis bahwa "layanan" dalam konteks DI tidak menyiratkan layanan tenang; layanan adalah implementasi dari dependensi yang menyediakan fungsionalitas.

pengguna1969177
sumber
16

AddSingleton ()

AddSingleton () membuat satu instance dari layanan ketika pertama kali diminta dan menggunakan kembali instance yang sama di semua tempat di mana layanan itu diperlukan.

AddScoped ()

Dalam layanan mencakup, dengan setiap permintaan HTTP, kami mendapatkan contoh baru. Namun, dalam permintaan HTTP yang sama, jika layanan diperlukan di banyak tempat, seperti dalam tampilan dan di controller, maka contoh yang sama disediakan untuk seluruh ruang lingkup permintaan HTTP itu. Tetapi setiap permintaan HTTP baru akan mendapatkan layanan baru.

AddTransient ()

Dengan layanan sementara, instance baru disediakan setiap kali instance layanan diminta apakah itu dalam lingkup permintaan HTTP yang sama atau melintasi permintaan HTTP yang berbeda.

Yasser Shaikh
sumber
5

Setelah mencari jawaban untuk pertanyaan ini, saya menemukan penjelasan yang cemerlang dengan contoh yang ingin saya bagikan kepada Anda.

Anda dapat menonton video yang menunjukkan perbedaan DI SINI

Dalam contoh ini kita memiliki kode yang diberikan ini:

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

Buat Tampilan

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

Salin-tempel kode ini dan tekan tombol buat di tampilan dan beralih di antara AddSingleton, AddScopeddan AddTransientAnda akan mendapatkan hasil berbeda setiap kali yang akan membantu Anda memahami penjelasan ini:

AddSingleton () - Seperti namanya, metode AddSingleton () menciptakan layanan Singleton. Layanan Singleton dibuat ketika pertama kali diminta. Contoh yang sama ini kemudian digunakan oleh semua permintaan berikutnya. Jadi secara umum, layanan Singleton dibuat hanya satu kali per aplikasi dan satu instance digunakan sepanjang waktu aplikasi.

AddTransient () - Metode ini menciptakan layanan Transient. Mesin virtual baru layanan Transient dibuat setiap kali diminta.

AddScoped () - Metode ini menciptakan layanan Scoped. Sebuah instance baru dari layanan Scoped dibuat sekali per permintaan dalam lingkup. Misalnya, dalam aplikasi web itu membuat 1 instance per setiap permintaan http tetapi menggunakan instance yang sama dalam panggilan lain dalam permintaan web yang sama.

Offir Pe'er
sumber
2

Yang mana yang akan digunakan

Sementara

  • karena mereka dibuat setiap kali mereka akan menggunakan lebih banyak memori & Sumber Daya dan dapat berdampak negatif pada kinerja
  • gunakan ini untuk layanan ringan dengan sedikit atau tanpa status .

Lingkup

  • opsi yang lebih baik ketika Anda ingin mempertahankan status dalam permintaan.

Singleton

  • kebocoran memori dalam layanan ini akan menumpuk dari waktu ke waktu.
  • memori juga efisien karena dibuat sekali digunakan kembali di mana-mana.

Gunakan Lajang di mana Anda perlu mempertahankan status lebar aplikasi. Konfigurasi atau parameter aplikasi, Layanan Logging, caching data adalah beberapa contoh di mana Anda dapat menggunakan lajang.

Menyuntikkan layanan dengan masa hidup yang berbeda ke yang lain

  1. Jangan pernah menyuntikkan layanan Scoped & Transient ke layanan Singleton. (Ini secara efektif mengubah layanan sementara atau cakupan menjadi singleton.)
  2. Jangan pernah menyuntikkan layanan Transient ke dalam layanan lingkup (Ini mengubah layanan sementara ke dalam ruang lingkup.)
bereket gebredingle
sumber
Ini jawaban terbaik. Saya suka bagian di mana Anda memberikan contoh. Tidak terlalu sulit untuk memahami cara kerjanya. Adalah jauh lebih sulit untuk memikirkan layanan mana yang harus diletakkan dan bagaimana dan kapan memori dibersihkan dari mereka. Akan lebih bagus jika Anda menjelaskan lebih banyak tentang itu.
valentasm
1

Seperti dijelaskan di sini (tautan ini sangat berguna) dengan contoh,

Pemetaan antara antarmuka dan tipe konkret ini menentukan, bahwa setiap kali Anda meminta jenis IContryService, Anda akan mendapatkan instance baru dari CountryService. Inilah yang sementara artinya dalam hal ini. Anda juga dapat menambahkan pemetaan singleton (menggunakan AddSingleton) dan pemetaan cakupan (menggunakan AddScoped). Mencakup dalam hal ini berarti mencakup untuk permintaan HTTP, yang juga berarti itu adalah singleton ketika permintaan saat ini berjalan. Anda juga bisa menambahkan instance yang ada ke wadah DI menggunakan metode AddInstance. Ini adalah cara yang hampir lengkap untuk mendaftar ke IServiceCollection

Rehmanali Momin
sumber
1

Perbedaan antara AddSingleton vs AddScoped vs AddTransient

Mendaftarkan Layanan

Inti ASP.NET menyediakan 3 metode berikut untuk mendaftarkan layanan dengan wadah injeksi ketergantungan. Metode yang kami gunakan menentukan masa pakai layanan terdaftar.

AddSingleton () - Seperti namanya, metode AddSingleton () menciptakan layanan Singleton. Layanan Singleton dibuat ketika pertama kali diminta. Contoh yang sama ini kemudian digunakan oleh semua permintaan berikutnya. Jadi secara umum, layanan Singleton dibuat hanya satu kali per aplikasi dan satu instance digunakan sepanjang waktu aplikasi.

AddTransient () - Metode ini menciptakan layanan Transient. Mesin virtual baru layanan Transient dibuat setiap kali diminta.

AddScoped () - Metode ini menciptakan layanan Scoped. Sebuah instance baru dari layanan Scoped dibuat sekali per permintaan dalam lingkup. Misalnya, dalam aplikasi web itu membuat 1 instance per setiap permintaan http tetapi menggunakan instance yang sama dalam panggilan lain dalam permintaan web yang sama.

Alexander Zaldostanov
sumber