2011-10-24 2 views
3

私はasp.netでAutofac 2.5を使用していますが、ライフタイムスコープのコンポーネントが単一インスタンスコンポーネントの依存として解決されてスレッドの安全性が損なわれる問題があります。これは登録の問題ですが、私はAutofacがこれを違反とみなし、例外をスローすると考えました。Autofacは混合スコープのコンポーネントを解決します

private class A{} 

    private class B 
    { 
     public B(A a){} 
    } 

    [Test] 
    [ExpectedException()] 
    public void SingleInstanceCannotResolveLifetimeDependency() 
    { 
     var builder = new ContainerBuilder(); 
     builder.RegisterType<A>() 
      .InstancePerLifetimeScope(); 
     builder.RegisterType<B>() 
      .SingleInstance(); 

     using (var container = builder.Build()) 
     { 
      using (var lifetime = container.BeginLifetimeScope()) 
      { 
       //should throw an exception 
       //because B is scoped singleton but A is only scoped for the lifetime 
       var b = lifetime.Resolve<B>(); 
      } 
     } 
    } 

この場合、Autofacに依存関係解決の例外をスローする方法はありますか?

UPDATE これはAutofacための正しい動作ですが - SingleInstanceはスコープだけルート寿命である - それは、Web環境で潜在的に危険なことができます。すべての開発者が正しい登録を取得していることを確認することは苦しいことがあります。ここでは、ライフタイムスコープのインスタンスがルートスコープで解決されないように、インスタンスルックアップをチェックするAutofacの小さな拡張メソッドがあります。 Webプロジェクトからライフサイクルの問題を取り除くのに役立っていることは分かっています。

public static class NoLifetimeResolutionAtRootScopeExtensions 
{ 
    /// <summary> 
    /// Prevents instances that are lifetime registration from being resolved in the root scope 
    /// </summary> 
    public static void NoLifetimeResolutionAtRootScope(this IContainer container) 
    { 
     LifetimeScopeBeginning(null, new LifetimeScopeBeginningEventArgs(container)); 
    } 

    private static void LifetimeScopeBeginning(object sender, LifetimeScopeBeginningEventArgs e) 
    { 
     e.LifetimeScope.ResolveOperationBeginning += ResolveOperationBeginning; 
     e.LifetimeScope.ChildLifetimeScopeBeginning += LifetimeScopeBeginning; 
    } 

    private static void ResolveOperationBeginning(object sender, ResolveOperationBeginningEventArgs e) 
    { 
     e.ResolveOperation.InstanceLookupBeginning += InstanceLookupBeginning; 
    } 

    private static void InstanceLookupBeginning(object sender, InstanceLookupBeginningEventArgs e) 
    { 
     var registration = e.InstanceLookup.ComponentRegistration; 
     var activationScope = e.InstanceLookup.ActivationScope; 

     if (registration.Ownership != InstanceOwnership.ExternallyOwned 
      && registration.Sharing == InstanceSharing.Shared 
      && !(registration.Lifetime is RootScopeLifetime) 
      && activationScope.Tag.Equals("root")) 
     { 
      //would be really nice to be able to get a resolution stack here 
      throw new DependencyResolutionException(string.Format(
       "Cannot resolve a lifetime instance of {0} at the root scope.", registration.Target)) 
     } 
    } 
} 

コンテナを作成するときにこれを適用すると、ライフスコープのサービスがルートスコープで解決されたときに例外がスローされます。

container.NoLifetimeResolutionAtRootScope(); 

答えて

3

はい - 子スコープに名前を付けて、コンポーネントAを明示的に関連付けする必要があります。それ以外の場合は、確認したように、Aインスタンスがルート(コンテナ)スコープに作成されます。

// Replace `A` registration with: 
builder.RegisterType<A>().InstancePerMatchingLifetimeScope("child"); 

そして...

// Replace scope creation with: 
using (var lifetime = container.BeginLifetimeScope("child")) { 
+0

おかげで、それは、単一のインスタンスは、単にルート寿命がスコープであることを把握するために私にしばらく時間がかかりました。 – Danielg

+0

小さな拡張メソッドで質問を更新し、これらのケースの例外をスローしました。 "_activationStack"がResolveOperationに公開されているといいですね。もっと有用な情報を投げることができます。 – Danielg

関連する問題