AddTransient、AddScoped、およびAddSingletonサービスの違い


938

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ドキュメントは、「一時的なライフタイムサービスは、要求されるたびに作成される」と述べています。および「スコープライフタイムサービスはリクエストごとに1回作成されます。」私の英語の理解が実際にまったく同じことを意味すると思ったよりも弱い場合を除いて。
ニュートリノ2017年

70
@tmg知っています。この点についてはドキュメントがまったく明確ではないことを指摘しているだけなので、ドキュメントを人々に示すことはあまり役に立ちません。
ニュートリノ2017年

13
@ニュートリノ、それが私がこの質問をした理由です。
Elvin Mammadov 2017年

5
パーティーの後半で、後でコメントを読んでいたが、私はその記事を印刷して読んだ後、@ Neutrinoがここで作成したのを見ているのと同じ余白に同じ観察を書き留めた。記事はその分析を提供する上で完全に曖昧でした。ありがたいことに、この例では混乱が少なくなりました。
Wellspring、2017

5
私が理解している限り:一時的なライフタイムサービスは、要求されるたびに作成されます。ここで要求される単語は、何かを要求するという日常の英語の意味です。この場合はサービスです。一方、リクエストごとに1回というリクエストは、HTTPリクエストを指します。しかし、私は混乱を理解しています。
Memet Olsen

回答:


1655

TL; DR

非定常オブジェクトは常に異なります。すべてのコントローラーとすべてのサービスに新しいインスタンスが提供されます。

スコープ付きオブジェクトはリクエスト内では同じですが、リクエストごとに異なります。

シングルトンオブジェクトは、すべてのオブジェクトとすべてのリクエストで同じです。

より明確にするために、ASP.NETドキュメントの次の例は違いを示しています。

これらの有効期間と登録オプションの違いを示すために、1つ以上のタスクを一意の識別子を持つ操作として表す単純なインターフェースを考えてみましょうOperationId。このサービスの有効期間の構成方法に応じて、コンテナーはサービスの同じまたは異なるインスタンスを要求クラスに提供します。要求されているライフタイムを明確にするために、ライフタイムオプションごとに1つのタイプを作成します。

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

ここで、このコントローラーアクションに対して2つの個別のリクエストが行われます。

最初のリクエスト

第二の要求

OperationIdリクエスト内およびリクエスト間で変化する値を確認します。

  • 非定常オブジェクトは常に異なります。すべてのコントローラーとすべてのサービスに新しいインスタンスが提供されます。

  • スコープ付きオブジェクトはリクエスト内では同じですが、リクエストごとに異なります

  • シングルトンオブジェクトは、すべてのオブジェクトとすべてのリクエストで同じです(インスタンスがで提供されているかどうかに関係なくConfigureServices


14
それぞれの機能は理解しましたが、一方を他方の代わりに使用することの影響を誰かが説明できます。正しく使用しなかったり、別のものを選択したりすると、どのような問題が発生しますか。
パワンネパール2017

2
シングルトンスコープを持つリクエストコンテキスト関連オブジェクト(現在のユーザーなど)を作成しているとすると、すべてのhttpリクエストで同じインスタンスのままになり、望ましくありません。IOCはすべてインスタンスの作成に関するものであるため、作成されたインスタンスのスコープを指定する必要があります。
akazemis 2017

1
それは!、私はトピックの一番上のリンクに言及しました!サンプルコードは、MS docsからコピー/貼り付けされています
akazemis

1
ありがとう。うんシングルトンはセッション/ユーザーに関係なくアプリ全体で同じになります。アプリがマイクロサービスアーキテクチャを使用していて、各サービスが個別のプロセスで実行されている場合、シングルトンは各プロセスで同じになります
akazemis

1
addTransientの使用例を教えていただけますか?あまりにも多くのリソースを使用しているのに、それを使用するためのユーティリティを見つけられなかったため
Terai

319

.NETの依存性注入には、3つの主要なライフタイムがあります。

アプリケーション全体で単一のインスタンスを作成するシングルトン。初めてインスタンスを作成し、すべての呼び出しで同じオブジェクトを再利用します。

スコープ付きライフタイムサービスは、スコープ内のリクエストごとに1回作成されます。現在のスコープのシングルトンに相当します。たとえば、MVCではHTTPリクエストごとに1つのインスタンスを作成しますが、同じWebリクエスト内の他の呼び出しでは同じインスタンスを使用します。

一時的なライフタイムサービスは、要求されるたびに作成されます。この存続期間は、軽量でステートレスなサービスに最適です。

ここでは、違いを確認するための例を見つけることができます。

ASP.NET 5 MVC6 Dependency Injection in 6 Steps(デッドリンクによるウェブアーカイブリンク)

Dependency Injection対応のASP.NET:ASP.NET 5

そして、これは公式ドキュメントへのリンクです:

ASP.NET Coreでの依存性注入


22
トランジェントが最も軽量な理由を説明していただけますか?トランジェントは、注入ごとにインスタンスを作成する必要があるため、最も重い作業だと思いました。
エキスパート

17
あなたが正しい。過渡は最も軽量ではない、私はちょうどそれが軽量RESTfulサービスに適していた:)
akazemis

3
たとえば、データベースから数行を取得している場合など、どのシナリオでスコープを使用し、どのトランジェントでコントローラーの例を使用できるでしょうか。この場合、スコープ使用シナリオと一時使用シナリオを理解しようとしています。
センセイ

4
それは本当にあなたが期待しているロジックに依存します。たとえば、それが単一のdb呼び出しである場合、実際にどの呼び出しを使用しても違いはありません。ただし、同じリクエストでdbを複数回呼び出す場合は、スコープ内のライフタイムを使用できます。これにより、同じリポジトリオブジェクトがメモリに保持され、同じHttpリクエストコンテキスト内で複数回再利用されます。一方、一時的なものは新しいリポジトリオブジェクトを複数回作成します(そしてより多くのメモリを消費します)。特定のシナリオを説明すると、どちらが適しているかを簡単に判断できます。
akazemis 2017

3
ここで強調すべき重要な点の1つは、シングルトン、スコープ指定、およびトランジェントは、ロシアのドイルのようであり、一方は他方の内部にあります。たとえば、入れ子のときに順序を逆にすることはできません。スコープまたはシングルトンをTransientに含めることはできません。これは、包含に反する親の存続期間を延長するためです。
DLナラシムハン

34

同じタイプの複数のオブジェクトを注入する必要がある場合の、ASP.NET MVCコアDIでの一時的でスコープ付きのシングルトン定義オブジェクト作成プロセス。依存性注入が初めての場合は、このDI IoCビデオをご覧ください。

コンストラクターで「IDal」の2つのインスタンスを要求した、以下のコントローラーコードを確認できます。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値を表示したスナップショットイメージです。

ここに画像の説明を入力してください

スコープ:スコープでは、同じオブジェクトインスタンスが単一の要求と応答に挿入されます。

ここに画像の説明を入力してください

シングルトン:シングルトンでは、同じオブジェクトがすべてのリクエストとレスポンスに注入されます。この場合、オブジェクトの1つのグローバルインスタンスが作成されます。

以下は、上記の基本を視覚的に説明する簡単な図です。

MVC DIイメージ

上の画像は、ムンバイでASP.NET MVCトレーニングを受けていたときにSBSSチームによって描かれました。上の画像を作成してくれたSBSSチームに感謝します。


9
これは、私が今まで見た一時的なサービスの最も複雑な説明の1つです。Transient =このサービスが解決されるときはいつでも、変数を割り当てることと同じですnew TService。Scopedは、その「スコープ」のために最初の初期化をキャッシュします(ほとんどの場合、httpリクエスト)。シングルトンは、アプリケーションの存続期間中、1つのインスタンスのみをキャッシュします。上記の図は非常に複雑です。
Mardoxx 2018年

2
申し訳ありませんが、図とコードスナップショットを使用してより簡単にするつもりです:-)しかし、私はあなたの要点を理解します。
Shivprasad Koirala 2018

30
  • シングルトンは、アプリケーションドメインの存続​​期間中の単一のインスタンスです。
  • スコープ指定は、スコープ指定された要求の期間中の単一のインスタンスです。つまり、ASP.NETのHTTP要求ごとです。
  • Transientは、コードリクエストごとに1つのインスタンスです。

通常、次のように、コードリクエストはコンストラクタパラメータを介して行う必要があります。

public MyConsumingClass(IDependency dependency)

@akazemisの回答で、DIのコンテキストでの「サービス」はRESTfulなサービスを意味するものではないことを指摘したかったのです。サービスは、機能を提供する依存関係の実装です。


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

コピー&ペーストの間ビューとスイッチで作成ボタンでこのコードとプレスを AddSingletonAddScopedそしてAddTransientあなたがするたびにあなたがこの説明を理解するのに役立つ可能性があるだろう異なる結果を取得します。

AddSingleton() -名前が示すように、AddSingleton()メソッドはシングルトンサービスを作成します。シングルトンサービスは、最初に要求されたときに作成されます。この同じインスタンスは、その後のすべてのリクエストで使用されます。したがって、一般に、シングルトンサービスはアプリケーションごとに1回だけ作成され、その単一のインスタンスはアプリケーションのライフタイム全体で使用されます。

AddTransient() -このメソッドは、一時サービスを作成します。一時サービスの新しいインスタンスは、要求されるたびに作成されます。

AddScoped() -このメソッドは、Scopedサービスを作成します。スコープサービスの新しいインスタンスは、スコープ内のリクエストごとに1回作成されます。たとえば、ウェブアプリケーションでは、httpリクエストごとに1つのインスタンスを作成しますが、同じウェブリクエスト内の他の呼び出しでは同じインスタンスを使用します。


2

どちらを使用するか

一時的

  • それらは、より多くのメモリとリソースを使用するたびに作成され、パフォーマンスに悪影響を及ぼす可能性があるため
  • ほとんどまたはまったく状態のない軽量サービスにこれを使用します。

スコープ

  • リクエスト内で状態を維持したい場合のより良いオプション。

シングルトン

  • これらのサービスのメモリリークは、時間の経過とともに増加します。
  • また、一度どこでも再利用されると作成されるため、メモリ効率も向上します。

アプリケーション全体の状態を維持する必要がある場合は、シングルトンを使用します。アプリケーションの構成またはパラメーター、ロギングサービス、データのキャッシュは、シングルトンを使用できる例の一部です。

ライフタイムの異なるサービスを別のサービスに注入する

  1. スコープサービスと一時サービスをシングルトンサービスに挿入しないでください。(これにより、一時的なサービスまたはスコープされたサービスがシングルトンに効果的に変換されます。)
  2. 一時的なサービスをスコープされたサービスに注入しないでください(これにより、一時的なサービスがスコープされたサービスに変換されます)。

これが最良の答えです。例をあげるところが好きです。それらがどのように機能するかを理解することはそれほど難しくありません。どのサービスをどこに、どのように、いつメモリをクリーンアップするかを考えるのは非常に困難です。それについてもっと説明してもらえたら嬉しいです。
valentasm

1

説明し、ここでの例で(このリンクは非常に便利です)、

インターフェースと具象型の間のこのマッピングは、IContryServiceの型を要求するたびに、CountryServiceの新しいインスタンスを取得することを定義します。この場合、これが一時的な意味です。シングルトンマッピング(AddSingletonを使用)およびスコープマッピング(AddScopedを使用)を追加することもできます。この場合のスコープとは、HTTPリクエストをスコープとすることを意味します。これは、現在のリクエストの実行中はシングルトンであることも意味します。AddInstanceメソッドを使用して、既存のインスタンスをDIコンテナに追加することもできます。これらは、IServiceCollectionに登録するためのほぼ完全な方法です


1

AddSingletonとAddScopedとAddTransientの違い

サービスの登録

ASP.NETコアは、依存関係注入コンテナーにサービスを登録するための次の3つのメソッドを提供します。使用する方法によって、登録されたサービスの存続期間が決まります。

AddSingleton()-名前が示すように、AddSingleton()メソッドはシングルトンサービスを作成します。シングルトンサービスは、最初に要求されたときに作成されます。この同じインスタンスは、その後のすべてのリクエストで使用されます。したがって、一般に、シングルトンサービスはアプリケーションごとに1回だけ作成され、その単一のインスタンスはアプリケーションのライフタイム全体で使用されます。

AddTransient()-このメソッドは、一時サービスを作成します。一時サービスの新しいインスタンスは、要求されるたびに作成されます。

AddScoped()-このメソッドは、Scopedサービスを作成します。スコープサービスの新しいインスタンスは、スコープ内のリクエストごとに1回作成されます。たとえば、ウェブアプリケーションでは、httpリクエストごとに1つのインスタンスを作成しますが、同じウェブリクエスト内の他の呼び出しでは同じインスタンスを使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.