web-dev-qa-db-ja.com

DIを使用してクラスコンストラクターでMicrosoft.AspNet.Http.HttpContextインスタンスを取得する方法

私はMVC6で使い捨てアプリケーションを構築し、依存関係についてさまざまなアーキテクチャを実験しています。

私が直面している問題は、アプリケーションに固有のカスタム 'MyAppContext'オブジェクトを作成する方法です。これには、HttpContextからの情報とデータベースからの情報が必要であり、アプリケーション固有の属性のリクエストスコープのリポジトリになります。 HttpContextのインスタンスを 'MyAppContext'のコンストラクターに渡したい。

DIを使用してDataServiceインターフェイスで 'IDataService'オブジェクトを正常に作成しましたが、これは問題なく機能します。 'MyAppContext'クラスとの違いは、コンストラクターに 'DataService'と_Microsoft.AspNet.Http.HttpContext_の2つのパラメーターがあることです。 MyAppContextクラスは次のとおりです。

_public class MyAppContext : IMyAppContext
{
    public MyAppContext(IDataService dataService, HttpContext httpContext)
    {
       //do stuff here with the httpContext
    }
}
_

スタートアップコードで、DataServiceインスタンスとMyAppContextインスタンスを登録します。

_    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        //adds a singleton instance of the DataService using DI
        services.AddSingleton<IDataService, DataService>();
        services.AddScoped<IMyAppContext, MyAppContext>();    

    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseErrorPage();
        app.UseRequestServices();
        app.UseMvc(routes => /* routes stuff */);
    }
_

コンストラクターのHttpContextパラメーターがDIによって解決されることを期待しています。コードを実行すると、これが返される例外です。

InvalidOperationException:「MyAppContext」をアクティブ化しようとしているときにタイプ「Microsoft.AspNet.Http.HttpContext」のサービスを解決できません

これは、このエラーが発生しているHttpContextの特定のインスタンスがないためだと思いますが、HttpContextインスタンスをDIに登録する方法がわかりません。 'app.UseRequestServices();'という行を追加しましたが、これは何の違いもありません。また、次のバリエーションも試しました。

_services.AddScoped<HttpContext, HttpContext>();
_

しかし、2番目のHttpContextはインスタンスであると想定されているため、これは失敗します。正しくないことはわかっていますが、何であるかを理解できていません。

要約すると、HttpContextオブジェクトをMyAppContextのコンストラクターに渡すにはどうすればよいですか?

18
Bruce Chapman

コンストラクターにIHttpContextAccessorを挿入します

20

コンポーネントにHttpContextを挿入すると、 SOLIDの原則 に違反します。より具体的には、あなたは違反しています:

  • 依存性逆転の原則 (DIP)フレームワークの種類(HttpContext)に依存しているため。
  • Interface Segregation Principle (ISP)は、HttpContextには多くのメソッドがありますが、コンシューマーがそれらすべてを使用することはないためです。

どちらの違反も、コードのテストを非常に困難にします。代わりに@victorが示唆するようにIHttpContextAccessorを挿入できますが、これはフレームワークによって提供される抽象化であり、HttpContextに依存しているため、これはDIPとISPの両方の違反です。 DIPによると、抽象化を定義するのはクライアントです。これにより、コードがフレームワークに不必要に結合されます。

代わりに、狭い役割のインターフェイスを指定するように努める必要があります。アプリケーションのニーズに固有の1つの特定のことを実行するインターフェース。文字列値を使用して大きな辞書を挿入する(HttpContextが何であるかは、決して具体的ではありません)。あなたの質問から、私たちのMyAppContextからどのような種類のデータが必要かは不明ですが、現在ログインしているユーザーの情報のようなものを期待しています。このために、特定のIUserContext抽象化を定義できます。次に例を示します。

public interface IUserContext {
    IPrincipal CurrentUser { get; }
}

アプリケーションをASP.NETFrameworkに接続するアダプターは、この抽象化のために簡単に作成できます。

sealed class AspNetUserContextAdapter : IUserContext  {
    private readonly IHttpContextAccessor accessor;
    public AspNetUserContextAdapter(IHttpContextAccessor accessor) {
        this.accessor = accessor;
    }
    public IPrincipal CurrentUser => accessor.HttpContext.User;
}

このアダプタはIHttpContextAccessorに依存しますが、アダプタは Composition Root にあるインフラストラクチャコンポーネントであるため、これは問題ありません。このクラスを登録するには、次のような方法があります。

services.AddSingleton<IUserContext, AspNetUserContext>();
16
Steven

スタートアップクラスの場合:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.Extensions.DependencyInjection;

public void ConfigureServices(IServiceCollection services)
{        
    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    services.AddMvcCore();
}

コントローラ内:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;

private readonly IHttpContextAccessor _httpContextAccessor;

public ServerSentEventController(IHttpContextAccessor httpContextAccessor)
{
    _httpContextAccessor = httpContextAccessor;
}
3
Shaun Luttin