1. ホーム
  2. c#

[解決済み] AddTransient、AddScoped、AddSingleton サービスの相違点

2022-03-15 13:48:07

質問

を実装したい。 依存性注入 (DI) を ASP.NET Core で使用することができます。そこで、このコードを ConfigureServices メソッドを使用すると、両方の方法で動作します。

とはどのような違いがあるのでしょうか。 services.AddTransientservice.AddScoped メソッドを ASP.NET Core で使用できますか?

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

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

解決方法は?

TL;DR

トランジェントオブジェクトは常に異なるもので、新しいインスタンスが提供されます。 は、すべてのコントローラ、すべてのサービスにおいて

スコープ付きオブジェクトは、1つのリクエスト内では同じだが、他のリクエストでは異なる。 異なるリクエスト

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

より明確にするために、この例では .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
    {
    }
}

これらのインターフェースを1つのクラスを使って実装します。 Operation コンストラクタでGUIDを受け取り、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 )