web-dev-qa-db-ja.com

コントローラークラス以外のクラスでの依存性注入

この時点で、私は簡単にコントローラーに物を注入しています。場合によっては、独自のResolverServicesクラスを作成しています。 人生は良い

どうすればよいかわからないのは、フレームワークを非コントローラークラスに自動的に挿入することです。動作するのは、フレームワークをコントローラーIOptionsに自動的に挿入することです。これは、私のプロジェクトの構成です。

public class MessageCenterController : Controller
{
    private readonly MyOptions _options;

    public MessageCenterController(IOptions<MyOptions> options)
    {
        _options = options.Value;
    }
}

私は自分のクラスでも同じことが起こると思うし、コントローラを模倣するときに近くにいると仮定すると、次のようになります:

public class MyHelper
{
    private readonly ProfileOptions _options;

    public MyHelper(IOptions<ProfileOptions> options)
    {
        _options = options.Value;
    }

    public bool CheckIt()
    {
        return _options.SomeBoolValue;
    }
}

私が失敗しているのは、私がこのように呼ぶときだと思います:

public void DoSomething()
{
    var helper = new MyHelper(??????);

    if (helper.CheckIt())
    {
        // Do Something
    }
}

私がこれを追跡している問題は、実際にはDIについて話すすべてがコントローラーレベルでそれについて話すことです。 Controllerオブジェクトソースコードのどこで発生するかを探してみましたが、そこはちょっとおかしくなります。

IOptionsのインスタンスを手動で作成してMyHelperコンストラクターに渡すことができることは知っていますが、Controllersで機能するため、フレームワークにそれをさせることができるようです。

あなたの助けに感謝します。

53
Robert Paulsen

以下は、MVCコントローラーに関係するものなしでDIを使用する実際の例です。これはプロセスを理解するために私がしなければならないことなので、おそらく他の誰かに役立つでしょう。

ShoppingCartオブジェクトは、DIを介してINotifierのインスタンスを取得します(顧客に注文を通知します)。

using Microsoft.Extensions.DependencyInjection;
using System;

namespace DiSample
{
    // STEP 1: Define an interface.
    /// <summary>
    /// Defines how a user is notified. 
    /// </summary>
    public interface INotifier
    {
        void Send(string from, string to, string subject, string body);
    }

    // STEP 2: Implement the interface
    /// <summary>
    /// Implementation of INotifier that notifies users by email.
    /// </summary>
    public class EmailNotifier : INotifier
    {
        public void Send(string from, string to, string subject, string body)
        {
            // TODO: Connect to something that will send an email.
        }
    }

    // STEP 3: Create a class that requires an implementation of the interface.
    public class ShoppingCart
    {
        INotifier _notifier;

        public ShoppingCart(INotifier notifier)
        {
            _notifier = notifier;
        }

        public void PlaceOrder(string customerEmail, string orderInfo)
        {
            _notifier.Send("[email protected]", customerEmail, $"Order Placed", $"Thank you for your order of {orderInfo}");
        }

    }

    public class Program
    {
        // STEP 4: Create console app to setup DI
        static void Main(string[] args)
        {
            // create service collection
            var serviceCollection = new ServiceCollection();

            // ConfigureServices(serviceCollection)
            serviceCollection.AddTransient<INotifier, EmailNotifier>();

            // create service provider
            var serviceProvider = serviceCollection.BuildServiceProvider();

            // This is where DI magic happens:
            var myCart = ActivatorUtilities.CreateInstance<ShoppingCart>(serviceProvider);

            myCart.PlaceOrder("[email protected]", "2 Widgets");

            System.Console.Write("Press any key to end.");
            System.Console.ReadLine();
        }
    }
}
24
Robert Paulsen

MyHelperMyServiceによって使用され、それがコントローラーによって使用されているとします。

この状況を解決する方法は次のとおりです。

  • 登録Startup.ConfigureServicesMyServiceMyHelperの両方。

    services.AddTransient<MyService>();
    services.AddTransient<MyHelper>();
    
  • コントローラーは、コンストラクターでMyServiceのインスタンスを受け取ります。

    public HomeController(MyService service) { ... }
    
  • MyServiceコンストラクターは、MyHelperのインスタンスを順番に受け取ります。

    public MyService(MyHelper helper) { ... }
    

DIフレームワークは、オブジェクトグラフ全体を問題なく解決できます。オブジェクトが解決されるたびに新しいインスタンスが作成されることを心配している場合は、シングルトンやリクエストのライフタイムのような異なる lifetime and registration options について読むことができます。

service locator anti-pattern で終わる可能性があるため、何らかのサービスのインスタンスを手動で作成する必要があると思う場合、本当に疑わしいはずです。オブジェクトの作成はDIコンテナに任せたほうがよいでしょう。そのような状況に本当に気づいた場合(抽象ファクトリを作成するとしましょう)、IServiceProviderを直接使用できます(コンストラクタでIServiceProviderを要求するか、コンストラクタを使用します exposed httpContext )で。

var foo = serviceProvider.GetRequiredService<MyHelper>();

ASP.Net 5 DIフレームワークと一般的な依存関係注入について、特定の documentation を読むことをお勧めします。

21
Daniel J.G.

残念ながら、直接的な方法はありません。私がそれを機能させることができた唯一の方法は、静的クラスを作成し、それを以下のように他の場所で使用することです。

public static class SiteUtils
{

 public static string AppName { get; set; }

    public static string strConnection { get; set; }

}

次に、スタートアップクラスで、次のように入力します。

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    //normal as detauls , removed for space 
    // set my variables all over the site

    SiteUtils.strConnection = Configuration.GetConnectionString("DefaultConnection");
    SiteUtils.AppName = Configuration.GetValue<string>("AppName");
}

これは悪いパターンですが、これはアプリケーションのライフサイクル全体にとどまり、コントローラーの外部で使用するより良い方法を見つけることができなかったためです。

5
Ali

現在の.NET Core 2.2 DIドキュメント here に基づいて、OPの質問に直接答えるより完全な例があります。この回答は、.NET Core DIを初めて使用する人の助けになる可能性があり、この質問がGoogleのトップの検索結果だからです。

まず、MyHelperのインターフェースを追加します。

public interface IMyHelper
{
    bool CheckIt();
}

次に、MyHelperクラスを更新してインターフェイスを実装します(Visual Studioでは、ctrl-。を押してインターフェイスを実装します)。

public class MyHelper : IMyHelper
{
    private readonly ProfileOptions _options;

    public MyHelper(IOptions<ProfileOptions> options)
    {
        _options = options.Value;
    {

    public bool CheckIt()
    {
        return _options.SomeBoolValue;
    }
}

3番目に、フレームワークが提供するサービスとしてインターフェイスをDIサービスコンテナに登録します。これを行うには、Startup.csのConfigureServicesメソッドでIMyHelperサービスを具象型MyHelperに登録します。

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddScoped<IMyHelper, MyHelper>();
    ...
}

4番目に、サービスのインスタンスを参照するプライベート変数を作成します。サービスをコンストラクターの引数としてコンストラクターに渡し(コンストラクターの注入を介して)、変数をサービスインスタンスで初期化します。プライベート変数を介して、カスタムクラスのこのインスタンスでプロパティを参照するか、メソッドを呼び出します。

public class MessageCenterController : Controller
{
    private readonly MyOptions _options;
    private readonly IMyHelper _myHelper;

    public MessageCenterController(
        IOptions<MyOptions> options,
        IMyHelper myHelper
    )
    {
        _options = options.value;
        _myHelper = myHelper;
    }

    public void DoSomething()
    {
        if (_myHelper.CheckIt())
        {
            // Do Something
        }
    }
}
0
sfors