短い質問: この未回答の問題と同じ
長い質問:
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日間で既存の回答に賞金を報酬します
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>();
}
}
そしてもちろん、現在の依存関係リゾルバーisがAutofacDependencyResolver
の場合、解決を試みます...
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コンテキストがあった何かだったと思います。
これが私がお勧めするものです:
Autofac.Integration.WebApi.AutofacWebApiDependencyResolver
を使用します。AutofacHostFactory.Container
とそのホストファクトリ実装を使用して、依存関係を解決します。 (WCFはシングルトンホストの可能性などがあるため、少し奇妙です。そのため、「リクエストごと」はそれほど単純ではありません。)CommonServiceLocator
実装を検討してください。リクエストのライフタイムを作成しませんが、いくつかの問題を解決する可能性があります。それらをまっすぐに保ち、本来の生息地の外でさまざまなリゾルバーを使用しない場合は、問題が発生することはありません。
あなたcanサービス登録でInstancePerApiRequest
とInstancePerHttpRequest
をかなり安全に使用できます。これらの拡張機能はどちらも同じ有効期間スコープタグを使用するため、 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;
これにより、将来のトラブルを大幅に軽減できます。
私の仮定:
IUserRepo
はHttpContextに依存しています私の仮定が正しければ、ワークフロープロジェクトはHttpContext.Current
について何も知りません。
WindowsWorkflowプロジェクトは常に実行されます(私が正しく理解していれば、実際にはこの技術では動作しませんでした)。 MVCはHTTPリクエストに基づいています。 HttpContext.Current
は、リクエストが着信した場合にのみ入力されます。リクエストがない場合-この変数はnullです。リクエストがないにもかかわらずワークフローインスタンスがHttpContext
にアクセスしようとするとどうなりますか?正解-null参照例外。または、あなたの場合、依存関係解決の例外。
するべきこと:
User.Current
やHttpContext.Current
などのすべてのMVC仕様。ワークフローモジュール(必要な場合)、ワークフロー固有のすべての実装。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
実装を変更することができます。
現在、「httpcontextがない」問題の影響を受けているが、バージョンの制約のために上記の優れた推奨事項を使用できないテストがある状況にあります。
私たちがそれを解決した方法は、テスト設定で「モック」httpコンテキストを作成することでした:参照: テスト初期化メソッドのモックHttpContext.Current