2011-10-24 27 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拋出Dependency解決異常,如果發生這種情況?

UPDATE 儘管這是Autofac正確的行爲 - SingleInstance是作用域只是根一生 - 它可以在網絡環境中潛在的危險。確保所有開發人員獲得正確的註冊可能是一件痛苦的事情。這裏是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