2013-05-28 39 views
2

場景:上Autofac定製lifetimescopes與多租戶需要指導

我需要在相同 Web應用程序(應用程序域)內提供不同接口實現對相同接口定義,但到不同的「示波器」

想象一下,一個簡單的分層網絡的內容結構是這樣的(如果你不熟悉與SharePoint):

RootWeb (SPSite) (ctx here) 
    |______SubWeb1 (SPWeb) (ctx here) 
    |______SubWeb2 (SPWeb) 
    |______SubWeb3 (SPWeb) 
      |_______SubWeb3.1 (SPWeb) (ctx here) 
      |_______SubWeb3.2 (SPWeb) 

RootWeb,SubWeb1 UND SubWeb3.1提供上下文。那是我實現了一個AppIsolatedContext類,該類專用於特定的層次結構級別。如果一個級別沒有提供上下文,它就會從父節點繼承上下文,依此類推。例如SubWeb3會從RootWeb繼承它的上下文。但SubWeb3.1提供了自己的獨立上下文。

孤立的上下文只是一個靜態的ConcurrentDictionary。

好吧,迄今爲止好。現在關於Autofac(我是Autofac和任何其他DI容器的新手 - 不是IoC的原理)...我不知道如何正確設置它來正確處理對象。事實上,它不應該是一個問題,因爲對象(一旦它們被創建)應該活着,直到應用程序域被回收爲止(將它們想象成「每個孤立的上下文單例」)。

我會傾向於做這樣的事情:

// For completeness.. a dummy page which creates a "dummy" context 
public partial class _Default : Page 
{ 
    private static AppIsolatedContext _dummyContainer = new AppIsolatedContext(); 

    public _Default() 
    { 
     _dummyContainer.ExceptionHandler.Execute("Test Message");    
    } 
} 

// The isolated context which holds all the "context" specific objects 
public class AppIsolatedContext 
{ 
    public static IContainer Container { get; set; } 

    public IExceptionHandler ExceptionHandler { get; set; } 
    //public ISomething Something { get; set; } 
    //public ISomethingElse SomethingElse { get; set; } 

    public AppIsolatedContext() 
    { 
     // set up autofac 
     // Create your builder. 
     ContainerBuilder builder = new ContainerBuilder(); 

     // Usually you're only interested in exposing the type 
     // via its interface: 
     builder.RegisterType<MailNotificationHandler>().As<INotificationHandler>(); 
     builder.RegisterType<ExceptionHandler>().As<IExceptionHandler>(); 

     Container = builder.Build(); 

     using (ILifetimeScope scope = Container.BeginLifetimeScope()) 
     { 
      ExceptionHandler = scope.Resolve<IExceptionHandler>(); 
      //Something = scope.Resolve<ISomething>(); 
      //SomethingElse = scope.Resolve<ISomethingElse>(); 
     } 
    } 
} 

當然我的應用並不侷限於這些「背景下單」的情況。我會每個請求生命週期的實例..但這正是ASP.NET集成模塊在那裏的權利?我希望他們可以無縫地集成到SharePoint(2013):)

所以我的問題是是不是我提出或我需要讓我的手髒?如果是這樣一些方向將是驚人的 ...

通過Autofac的文檔,我在它的多租戶能力跌跌撞撞挖。 我相信這可能適合我的目的。任何人都可以證實這一點?

using System; 
using System.Web; 
using Autofac.Extras.Multitenant; 

namespace DemoNamespace 
{ 
    public class RequestParameterStrategy : ITenantIdentificationStrategy 
    { 
     public bool TryIdentifyTenant(out object tenantId) 
     { 
      tenantId = AppIsolatedContext.Current.Id; // not implemented in the dummy class above, but present in the real thing. 
      return !string.IsNullOrWhiteSpace(tenantId); 
     } 
    } 
} 

如果有什麼不結晶 - 請不要猶豫,告訴我:)

回答

4

免責聲明:這是一個相當不平凡的問題,因爲我有點缺乏瞭解在SharePoint 2013中,我會盡我所能來回答,但您需要根據您的需求來調整答案。

我構造這個的方式是使用命名的生命期範圍。而不是使用自己的容器的上下文,使用命名作用域的層次結構。這就是多租戶支持的工作原理;這也是ASP.NET每個Web請求支持的工作原理。

您首先需要閱讀the Autofac wiki page on instance scopes以及this primer on Autofac lifetimes。這些都不是小文章,但都有重要的概念要理解。我在這裏解釋的部分內容只有在瞭解生命週期範圍時纔有意義。

生命週期的作用域是可嵌套的,這就是你如何共享singletons或每個web請求的事例。應用程序的根源是一個包含所有註冊的容器,並從中產生範圍。

  • 集裝箱 兒童範圍子範圍

在更相關的代碼格式的

    • 兒童,是這樣的:

      var builder = new ContainerBuilder(); 
      var container = builder.Build(); 
      using(var child = container.BeginLifetimeScope()) 
      { 
          using(var childOfChild = child.BeginLifetimeScope()) 
          { 
          } 
      } 
      

      實際上,您將組件解析出作用域 - 容器本身就是作用域。

      約終生範圍主要事項:

      • 您可以命名他們,讓您有一個名爲範圍內的「單身」。
      • 您可以在致電BeginLifetimeScope時動態註冊。

      這是Autofac的多租戶支持如何工作。每個租戶都有自己的命名生命週期範圍。

      不幸的是,多租戶支持是一個級別:應用程序容器產生特定於租戶的「根」範圍,但就是這樣。您擁有這些上下文的網站層次結構具有多個級別,因此多租戶支持不起作用。但是,您可以查看該源代碼的想法。

      我會做的是命名每個級別的範圍。每個網站都會通過一個ILifetimeScope來解決問題。在代碼中,它會看起來有點像:

      var builder = new ContainerBuilder(); 
      // RootWeb will use the container directly and build its per-web-request 
      // scope from it. 
      var container = builder.Build(); 
      
      // Each sub web will get its own scope... 
      using(var sw1Scope = container.BeginLifetimeScope("SubWeb")) 
      { 
          // Each child of the sub web will get a scope... 
          using(var sw11Scope = sw1Scope.BeginLifetimeScope("SubWeb")) 
          { 
          } 
          using(var sw12Scope = sw1Scope.BeginLifetimeScope("SubWeb")) 
          { 
          } 
      } 
      

      注意我的標籤子網站範圍內的每個級別爲「子網站」 - 這將讓你有「實例每個子網站」之類的註冊在容器級別和子級別的註冊中。

      // Register a "singleton" per sub-web: 
      builder.RegisterType<Foo>() 
           .As<IFoo>() 
           .InstancePerMatchingLifetimeScope("SubWeb"); 
      

      現在,顯然,這是一個概念性的東西 - 你不會真正能夠使用類似的語句來包裝所有東西。您需要以不同方式管理您的創建和處理,因爲創建將發生在與處置不同的地方。

      您可以查看ASP.NET和multitenant源代碼,以獲取有關如何執行此操作的想法。一般算法如下:

      • 在應用程序啓動時,構建根容器。
      • 當子網絡啓動時,產生一個爲子網命名的嵌套生命期範圍。
      • 如果一個子網站需要註冊一個特定組成部分,也將調用BeginLifetimeScope
      • 期間如果您需要在每個子網站級別的「上下文」,你會傳遞該子網站,而不是創建範圍創建一個完整的獨立容器。

      現在,您可以通過將子網絡ID的根級別字典保留在作用域內來採取另一步驟,以便根本不需要每個級別的「上下文」對象。它更像是一種DependencyResolver.Current.GetService<T>類型的模式。如果您看看Autofac multitenant支持中的MultitenantContainer如何工作,您將看到類似租戶ID範圍的字典。

      事實上,多租戶支持將是一個很好的模式,特別是如果你還想要每個web請求的範圍。 Autofac ASP.NET支持要求您傳入父親ILifetimeScope,其中將生成子Web請求生存期範圍。多租戶支持在那裏添加了一些動態方面,所以當ASP.NET支持調用BeginLifetimeScope時,事務的多租戶部分會自動計算出來(通過租戶標識)哪個租戶應該是當前請求的父級。你可以對你的子網絡層次做同樣的事情。但是,多租戶支持是一個扁平的結構,而你的子網是一個層次結構,所以multitenant的支持不會只是工作

      這是說你在這裏有一個有趣的用例的一個很長的路,但你會讓你的手很骯髒

+0

謝謝你告訴我的冗長的方式我需要讓自己的手髒:)我會看看你建議給我的各種章節/實現。 儘管一個小小的音符:鑑於我總是可以得到「當前的自定義上下文ID」這樣的事實不足以支持多租戶工作嗎?對於AutoFac來說,它只是一個「ID」列表,它必須擁有不同的類型映射。 – lapsus

+0

問題在於層次結構 - 如上所述,多租戶是FLAT,而您的站點是層次結構。它是相似的,但租戶作用域始終脫離容器,而您希望子Web作用域脫離適當的父作用域 - 而不僅僅是根容器。 –