2015-11-21 32 views
6

出於某種奇怪的原因,System.Runtime.Remoting.Messaging.CallContextAsyncLocal類只能使用完整的CLR。這使得在處理可移植類庫或Windows Phone應用程序時執行異步範圍設定變得非常困難,特別是在Windows Phone API變爲異步專用的情況下;所以我們沒有真正的選擇不使用async/await。在PCL和Windows Phone應用程序中實現異步作用域

什麼這實際上意味着,在WPF或WinForms的,我們可以寫這樣的方法:

private async void button_Click(object sender, EventArgs e) 
{ 
    CallContext.LogicalSetData("x", new object()); 
    await Something(); 
    var context = CallContext.LogicalGetData("x"); 
} 

在WPF和WinForms,框架確保每個點擊相同的按鈕獲得自己的範圍內,並與可以孤立運行。使用ThreadLocal<T>[ThreadStatic]很難達到相同效果,因爲每次點擊都將在UI線程上執行。

我的問題是,我們如何解決Windows Phone中,商店和其他應用程序類型這個問題不支持CallContextAsyncLocal<T>

一些背景資料:

很多時候我們想要的(商業)邏輯在某種情況下的運行。該上下文可以包含業務邏輯在整個操作中可以使用的信息。在服務器環境中,這很容易想象,因爲您需要在數據庫事務中運行請求,需要訪問當前用戶,租戶ID等。但即使在客戶端應用程序中,操作可能也需要訪問上下文信息,例如用於當前正在運行的操作或用於記錄的上下文的相關ID。在此類操作過程中(如點擊事件),我們可能需要解決其他服務(從我們的Composition Root)。爲使操作順利進行,我們可能需要在整個客戶端操作中重複使用相同的組件,這意味着我們的Composition Root需要知道它正在運行的上下文。

儘管所有這些信息都可以通過使用服務公共API的方法調用傳遞給整個系統,但這不僅會迫使我們通過實現細節污染應用程序中的服務的API,還會導致嚴重的維護問題,因爲我們必須在整個系統中傳遞所有這些信息,並且對組件之一的簡單內部更改將通過所有方法調用傳播到調用堆棧。當涉及到我們的Composition Root時,我們絕對不想通過應用程序傳遞DI庫中的某些緩存/範圍對象,因爲這會將我們的業務邏輯緊密地耦合到外部庫。顯然,我們也不想傳遞某種service locator

因此,在客戶端應用程序中使用類似CallContextAsyncLocal<T>的方式實施範圍界定非常重要。

回答

3

今天沒有簡單的解決方案,很抱歉。當Windows Phone(最終)成爲「電話上的Windows 10」(即AsyncLocal<T>)時,這是可能的。但現在...

最簡單的方法是傳遞上下文,顯式地(作爲參數)或隱式地(作爲this上的成員變量)。

也可以用自定義等待者來實現這個限制版本。但除了非常複雜之外,該解決方案還需要您修改整個應用程序中的每個await(或使用編譯後的IL重寫來實現相同的效果)。

+2

廢話。不是我希望的答案。 – Steven

+0

@史蒂文:對不起!作爲安慰獎,歡迎您相信隱含的背景並不是一個偉大的設計。它們適用於日誌上下文(將多個日誌消息分組爲單個邏輯「活動」),就是這樣。不可見的上下文對於真正的業務數據/邏輯來說是一個糟糕的機制 - 它們使系統難以測試和維護。我寫了大量的異步代碼(除了「活動」關聯),我只在調試時使用隱式上下文來創建因果關係堆棧,從不進行生產,並且絕對不會執行業務層信息。 –

+0

我同意這對商業邏輯不利,但透明的上下文(如'TransactionScope')對於基礎設施的關注非常有用,它們實際上使開發人員的生活變得更加容易。 [Simple Injector](https://simpleinjector.org)(一個DI lib)基於這種概念設計了範圍化的生活方式,這使得開發人員的生活變得更加容易,因爲它允許他們的業務邏輯完全忽略範圍它正在運行。在整個系統中通過DI容器的範圍導致DI容器上的強耦合,這是不好的做法。 – Steven

相關問題