2012-03-06 51 views
2

我有幾個循環,每個循環通過一個ConcurrentQueue<T>產生異步進程。這些進程調用一些使用存儲庫進行數據庫交互的業務服務實現。服務實現都通過StructureMap進行連接。StructureMap:在特定上下文中定製生命週期範圍

的倉庫實現具有一些特點需要精心管理:

  • 正在使用的數據庫技術是Redis
  • 該實現使用ServiceStack.net的Redis客戶端(通過PooledRedisClientManager)。
  • 有幾種方法利用了(我相信)不能由同一個RedisClient同時創建的事務。因此,通過多個異步進程共享單個存儲庫實現是不可能的,因爲它會嘗試同時創建多個事務。
  • 爲了釋放內存和數據庫連接,這些對象需要顯式地處理 - 因此我已在存儲庫類上實現了IDisposable。
  • 沒有任何理由爲什麼一個存儲庫實例不能共享範圍內的單個異步進程,因爲不會有併發請求/事務。

考慮到上述情況,我想將單個存儲庫實例的範圍限定到每個異步過程的生存期。

要記住的一件事是,與異步進程範圍一起使用的服務也被系統中具有不同生命週期特徵的其他部分使用(例如,在存儲庫範圍爲頁面請求的生命週期)。

我會嘗試一些代碼來說明(從我的代碼簡化):

程序管理隊列(和接線事件處理程序執行該IAsyncResult調用):

public class Program 
{ 
    private readonly ConcurrentQueue<QueueItem> queue = new ConcurrentQueue<QueueItem>(); 
    private readonly IItemManager itemManager; // implemented via constructor DI. 

    public void ProcessQueueItems() 
    { 
     while (queue.Count > 0 || shouldContinueEnqueuing) 
     { 
      QueueItem item; 
      if (queue.TryDequeue(out item)) 
      { 
       // Begin async process here - only one repository should be used within the scope of this invocation 
       // (i.e. withing the scope of the itemManager.ProcessItem(item) method call. 
       new ItemProcessor(itemMananger.ProcessItem).BeginInvoke(e.Item, ItemCallback, null); 
      } 

      Thread.Sleep(1); 
     } 

    } 

    private static void ItemCallback(IAsyncResult result) 
    { 
     var asyncResult = (AsyncResult) result; 
     var caller = (ItemProcessor) asyncResult.AsyncDelegate; 

     var outcome = caller.EndInvoke(result); 

     // Do something with outcome... 
    } 

    private delegate ItemResult ItemProcessor(QueueItem item); 
} 

的由異步結果調用的實現。在我想要的ProcessItem(...)方法中管理範圍:

public class ItemManager : IItemManager 
{ 
    private readonly IServiceA serviceA; // implemented via constructor DI. 
    private readonly IServiceB serviceB; // implemented via constructor DI. 

    public ItemResult ProcessItem(QueueItem item) 
    { 
     // Both serviceA and serviceB use the repository which is injected via StructureMap. They should share 
     // the instance and at the end of the process it should be disposed (manually, if needs be). 
     var something = serviceA.DoSomething(item); 

     return serviceB.GetResult(something); 
    } 
} 

我認爲這說明了情況和目標。我的問題如下:

  1. 我可以如上所述使用StructureMap在單個進程的上下文中使用不同的作用域。
  2. 我不想在我的域/服務層中包含StructureMap的直接依賴項。所以,如果我能夠在這個階段使用不同的範圍,是否有一種簡單的方法可以在不直接從流程內部調用StructureMap的情況下執行此操作?
  3. 我可以通過DSL配置指示StructureMap在處理結束時處理庫還是需要在我的代碼中明確執行此操作?

回答

1

對於這種情況,您可以使用nested container

嵌套容器將跟蹤它創建的所有瞬態對象。當嵌套容器本身被丟棄時,它將調用 Dispose()在它創建的任何瞬態對象上。

var nestedContainer = container.GetNestedContainer(); 
var processor = nestedContainer.GetInstance<IItemProcessor>(); 

其他方式,以確保每一個對象使用同一個版本庫是使用帶()方法

// Get the IRepository which should be shared 
// This object is registered using simple 
// For<ISession>.Use<Session> registration so not scoped 
// http context or anything like that 
var session = container.GetInstance<ISession>(); 

// Create instance of IProcessor using the specific instance 
// of ISession. If multiple classes in the object grap use ISession 
// they will get the same instance. Note that you can use multiple 
// With() statements 
var itemProcessor = container.With(session).GetInstance<IItemProcessor>(); 
+1

我得到交易碰撞時,我嘗試使用嵌套容器的方法(在換句話說,同一個存儲庫被多個進程同時使用) - 我的存儲庫設置爲'ThreadLocalStorageLifecycle'。將使用單獨的命名配置文件的嵌套容器能夠解決這個問題嗎? – 2012-03-20 11:50:33

+0

@Zac沒有看到代碼就很難說。但是,如果您爲每個線程創建新的嵌套容器並在沒有任何生命週期的情況下注冊存儲庫,那麼不應該有任何衝突。我不知道你爲什麼有ItemManager和ItemProcessor。他們看起來像是做了完全一樣的事情。我可能只有ItemProcessor.Process()會啓動線程,處理項目並處理嵌套的容器。 – 2012-03-21 17:31:13