私は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のコンストラクターに渡すにはどうすればよいですか?
コンストラクターにIHttpContextAccessor
を挿入します
コンポーネントにHttpContext
を挿入すると、 SOLIDの原則 に違反します。より具体的には、あなたは違反しています:
HttpContext
)に依存しているため。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>();
スタートアップクラスの場合:
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;
}