web-dev-qa-db-ja.com

Autofac-HttpContextが使用できないため、リクエストの有効期間スコープを作成できません-非同期コードが原因ですか?

短い質問: この未回答の問題と同じ

長い質問:

Autofacを使用していたMVC 4 + Web Apiソリューションのコードを、Autofacを使用しているがWeb Api 2(MVC 5.1プロジェクトではなく、Web API)のみを使用する新しいソリューションに移植したところです。

以前のソリューションでは、MVC4とWeb Apiを使用していたので、2つのBootstrapper.csファイルがあり、それぞれ1つずつでした。新しいプロジェクトのWeb Apiブートストラップのみをコピーしました。

現在、私は新しいソリューションに依存関係をプルする必要のある他の2つのプロジェクトがあります。アンチパターンであるにもかかわらず、DependencyResolver.Current.GetService<T>()を使用する必要があると仮定します。

最初は、MVC依存関係リゾルバーを同じコンテナーに設定するまで機能しませんでした。

_GlobalConfiguration.Configuration.DependencyResolver = 
     new AutofacWebApiDependencyResolver(container);

//I had to pull in Autofac.Mvc and Mvc 5.1 integration but this line fixed it
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
_

奇妙な部分は、それらのプロジェクトの1つで修正するだけです!ここに状況があります:

_ Solution.Web project
      Bootstrapper.cs that registers both dependency resolvers for web api and mvc.

 Solution.ClassLib project
      var userRepo = DependencyResolver.Current.GetService<IUserRepo>(); //Good! :)

 Solution.WindowsWorkflow project
      var userRepo = DependencyResolver.Current.GetService<IUserRepo>(); //Throws exception :(
_

例外は次のとおりです。HttpContextが使用できないため、リクエストの有効期間スコープを作成できません。

ワークフローを非難する前に、この正確なセットアップが別のソリューションで問題なく機能していることを知っているだけで、ワークフローはDependencyResolverを問題なく使用できました。だから私はこれがAutofacの新しいバージョンの使用と関係していると思います、そしてワークフローが非同期に実行されるという事実(ちょうど私が非同期コードに関してリンクした質問のように)

すべての登録コードをInstancePerLifetimeScope()ではなくInstancePerHttpRequest()を使用するように切り替えてスコープを作成しようとしました:

_using (var c= AutofacDependencyResolver.Current
                     .ApplicationContainer.BeginLifetimeScope("AutofacWebRequest"))
{
   var userRepo = DependencyResolver.Current.GetServices<IUserRepo>();
}
_

しかし、それは例外を変更しませんでした。コードをさらに分解すると、正確な原因は次のとおりです。

_var adr = AutofacDependencyResolver.Current; //Throws that exception 
_

本当にこれを乗り越える必要があり、行き詰まった時間が多すぎます。 2日間で既存の回答に賞金を報酬します

14
parliament

UPDATE 2014年11月20日:Autofac.Mvc5のリリースでは、この質問がリリースされてから、AutofacDependencyResolver.Currentの実装が更新されましたHttpContextの必要性をなくすため。この問題が発生していて、この答えが見つかった場合は、新しいバージョンのAutofac.Mvc5に更新することで、問題を簡単に解決できる可能性があります。ただし、元の質問の質問者が問題を抱えていた理由を理解できるように、元の回答はそのままにしておきます。

元の答えは次のとおりです:


AutofacDependencyResolver.CurrentにはHttpContextが必要です。

コードをウォークスルーすると、AutofacDependencyResolver.Currentは次のようになります。

public static AutofacDependencyResolver Current
{
  get
  {
    return DependencyResolver.Current.GetService<AutofacDependencyResolver>();
  }
}

そしてもちろん、現在の依存関係リゾルバーisAutofacDependencyResolverの場合、解決を試みます...

public object GetService(Type serviceType)
{
  return RequestLifetimeScope.ResolveOptional(serviceType);
}

これは、RequestLifetimeScopeProviderからライフタイムスコープを取得します...

public ILifetimeScope GetLifetimeScope(Action<ContainerBuilder> configurationAction)
{
  if (HttpContext.Current == null)
  {
    throw new InvalidOperationException("...");
  }

  // ...and your code is probably dying right there so I won't
  // include the rest of the source.
}

Glimpse のようなツールをサポートするには、依存関係リゾルバーを動的にラップ/プロキシしてインストルメント化する必要があります。そのため、DependencyResolver.Current as AutofacDependencyResolverをキャストすることはできません。

Autofac.Integration.Mvc.AutofacDependencyResolverを使用するほとんどの場合、HttpContextが必要です。

これが、このエラーが発生し続ける理由です。 InstancePerHttpRequestが登録されている依存関係がなくてもかまいません。AutofacDependencyResolverには引き続きWebコンテキストが必要です。

これが問題ではなかった他のワークフローアプリがMVCアプリか、常にWebコンテキストがあった何かだったと思います。

これが私がお勧めするものです:

  • Webコンテキスト外でコンポーネントを使用する必要があり、WebApiを使用している場合は、Autofac.Integration.WebApi.AutofacWebApiDependencyResolverを使用します。
  • WCFを使用している場合は、標準のAutofacHostFactory.Containerとそのホストファクトリ実装を使用して、依存関係を解決します。 (WCFはシングルトンホストの可能性などがあるため、少し奇妙です。そのため、「リクエストごと」はそれほど単純ではありません。)
  • 「不可知論的」な技術が必要な場合は、AutofacのCommonServiceLocator実装を検討してください。リクエストのライフタイムを作成しませんが、いくつかの問題を解決する可能性があります。

それらをまっすぐに保ち、本来の生息地の外でさまざまなリゾルバーを使用しない場合は、問題が発生することはありません。

あなたcanサービス登録でInstancePerApiRequestInstancePerHttpRequestをかなり安全に使用できます。これらの拡張機能はどちらも同じ有効期間スコープタグを使用するため、 MVC WebリクエストとWeb APIリクエストの概念は、基礎となるライフタイムスコープがHttpContextに基づいており、もう1つのケースがIDependencyScopeに基づいている場合でも、同様に扱うことができます。したがって、アプリ/アプリのタイプ間で登録モジュールを仮想的に共有することができ、それは正しいことを行うはずです。

元のAutofacコンテナが必要な場合は、独自の参照を保存します。Autofacがそのコンテナを返すと想定するのではなく、参照を保存する必要がある場合があります。何らかの理由で後で取得する必要がある場合は、アプリケーションコンテナーに追加します。

public static class ApplicationContainer
{
  public static IContainer Container { get; set; }
}

// And then when you build your resolvers...
var container = builder.Build();
GlobalConfiguration.Configuration.DependencyResolver =
  new AutofacWebApiDependencyResolver(container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
ApplicationContainer.Container = container;

これにより、将来のトラブルを大幅に軽減できます。

25
Travis Illig

私の仮定:

  1. MVCプロジェクトとは別のスレッド/ AppDomainでワークフロープロジェクトを実行しています。
  2. IUserRepoはHttpContextに依存しています

私の仮定が正しければ、ワークフロープロジェクトはHttpContext.Currentについて何も知りません。

WindowsWorkflowプロジェクトは常に実行されます(私が正しく理解していれば、実際にはこの技術では動作しませんでした)。 MVCはHTTPリクエストに基づいています。 HttpContext.Currentは、リクエストが着信した場合にのみ入力されます。リクエストがない場合-この変数はnullです。リクエストがないにもかかわらずワークフローインスタンスがHttpContextにアクセスしようとするとどうなりますか?正解-null参照例外。または、あなたの場合、依存関係解決の例外。

するべきこと:

  1. コンテナー登録をモジュールに分割します-すべてのドメインクラスのドメインモジュール。次に、MVCモジュール:User.CurrentHttpContext.CurrentなどのすべてのMVC仕様。ワークフローモジュール(必要な場合)、ワークフロー固有のすべての実装。
  2. ワークフローの初期化で、ドメインおよびワークフローモジュールを含むautofacコンテナーを作成し、MVCの依存関係を除外します。 MVCコンテナの場合-ワークフローモジュールなしで作成します。
  3. IUserRepoの場合、HttpContextに依存しない実装を作成します。これはおそらく最も問題が多いでしょう。

AzureでのQuartz.Netの実行についても同様のことを行いました。これに関する私のブログ投稿を参照してください: http://tech.trailmax.info/2013/07/quartz-net-in-Azure-with-autofac-smoothness/ 。この投稿は直接の助けにはなりませんが、autofacモジュールを分割する理由を説明します。

コメントに従って更新:WebApiはここで多くのことを明確にします。 WebApiリクエストは、MVCリクエストと同じパイプラインを通過しません。また、WebApiコントローラーはHttpContextにアクセスできません。 この答え を参照してください。

ここで、wepApiコントローラーで何をしているのかに応じて、MVCとWebApiの両方で機能するようにIUserRepo実装を変更することができます。

3
trailmax

現在、「httpcontextがない」問題の影響を受けているが、バージョンの制約のために上記の優れた推奨事項を使用できないテストがある状況にあります。

私たちがそれを解決した方法は、テスト設定で「モック」httpコンテキストを作成することでした:参照: テスト初期化メソッドのモックHttpContext.Current

0
SteveT