2009-12-13 20 views
51

當我的服務執行時,許多類將需要訪問User.Current(這是我自己的User類)。我可以安全地將_currentUser存儲在[ThreadStatic]變量中嗎? WCF是否重用其線程?如果是這樣的話,它何時會清理ThreadStatic數據?如果使用ThreadStatic不安全,我應該在哪裏放置這些數據?在OperationContext.Current裏面有一個地方可以存儲那種數據嗎?在哪裏存儲當前WCF調用的數據? ThreadStatic是否安全?

編輯12/14/2009:我可以斷言使用ThreadStatic變量是不安全的。 WCF線程位於線程池中,並且ThreadStatic變量不會重新初始化。

回答

77

有一個博客post建議實施IExtension<T>。你也可以看看這個discussion

這裏有一個建議的實現:

public class WcfOperationContext : IExtension<OperationContext> 
{ 
    private readonly IDictionary<string, object> items; 

    private WcfOperationContext() 
    { 
     items = new Dictionary<string, object>(); 
    } 

    public IDictionary<string, object> Items 
    { 
     get { return items; } 
    } 

    public static WcfOperationContext Current 
    { 
     get 
     { 
      WcfOperationContext context = OperationContext.Current.Extensions.Find<WcfOperationContext>(); 
      if (context == null) 
      { 
       context = new WcfOperationContext(); 
       OperationContext.Current.Extensions.Add(context); 
      } 
      return context; 
     } 
    } 

    public void Attach(OperationContext owner) { } 
    public void Detach(OperationContext owner) { } 
} 

,你可以使用這樣的:

WcfOperationContext.Current.Items["user"] = _currentUser; 
var user = WcfOperationContext.Current.Items["user"] as MyUser; 
+0

謝謝,我會探討這個選項。 – Sylvain 2009-12-14 13:37:36

+0

@Darin:檢查我提供的其他答案,您對此有何看法? – Sylvain 2009-12-14 20:54:50

+0

應該是'WcfOperationContext'而不是'WcfInstanceContext'? – dan 2012-03-29 06:46:09

13

編輯:不要使用此解決方案。改用Darin的方法。正如@ n​​p-hard所說,如果存在異步操作(發生線程切換),則此解決方案無法工作。


我找到了另一種解決辦法。你可以使用OperationContext類的OperationCompleted事件清除你的ThreadStatic變量。

public class SomeClass 
{ 
    [ThreadStatic] 
    private static _currentUser = null; 

    public static void GetUser() 
    { 
     if (_currentUser == null) 
     { 
      _currentUser = LoadUser(); 

      // Reinitialize _currentUser at the end of the request 
      OperationContext.Current.OperationCompleted += 
       (sender, args) => _currentUser = null; 
     } 

     return _currentUser; 
    } 
} 

+0

我接受了這個答案,因爲它是最簡單的解決方案。 Darin的解決方案更加複雜,在更復雜的情況下我可能會更好。 – Sylvain 2009-12-15 20:20:31

+1

如果存在異步操作(發生線程切換),則這不起作用 – 2012-03-24 19:31:46

+0

這是將gremlins引入代碼的最佳方法。 – 2012-07-30 12:30:25

-1

我發現我們錯過了數據或當前上下文當我們把異步多線程切換調用。爲了處理這種情況,你可以嘗試使用CallContext。它應該用於.NET遠程處理,但它也應該在這種情況下工作。

坐落在CallContext中的數據:

DataObject data = new DataObject() { RequestId = "1234" }; 
CallContext.SetData("DataSet", data); 

檢索共享從CallContext中的數據:

var data = CallContext.GetData("DataSet") as DataObject; 

// Shared data object has to implement ILogicalThreadAffinative 

public class DataObject : ILogicalThreadAffinative 
{ 
    public string Message { get; set; } 
    public string Status { get; set; } 
} 

爲什麼ILogicalThreadAffinative?

當對另一個AppDomain中的對象進行遠程方法調用時,當前的CallContext類會生成一個LogicalCallContext,並隨調用一起傳輸到遠程位置。

只有公開ILogicalThreadAffinative接口並存儲在CallContext中的對象纔在AppDomain之外傳播。

+1

這是傳統技術,[官方文檔](https://msdn.microsoft.com/en-us/library/w61s16a1(v = vs.100).aspx)已歸檔並明確提及:「This topic is特定於遺留技術,爲了與現有應用程序向後兼容而保留,不建議用於新開發。現在應該使用Windows Communication Foundation(WCF)開發分佈式應用程序 – MarioDS 2016-11-21 11:50:12