2012-06-26 24 views
0

我们有多个WCF服务,都使用InstanceContextMode = PerCall,并且所有WCF服务实例都是通过使用Unity(IOC)并实现IInstanceProvider来生成的。WCF Percall模式和线程静态变量

相关标识符用于审核具有相同标识符的所有方法调用和数据库进程。

为了实现此目的,通过实现IDispatchBehavior创建端点行为,并在AfterReceiveRequest方法中,生成一个GUID并将其分配给ThreadStatic(CommonData)属性。该属性可以在应用程序的所有层中访问。以下代码块显示CommonData和CommonData类的总体;

public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) 
     { 
      CommonData.ClearDictionary(); 
      //the lines between are deleted as they are not relevant to the question 

      CommonData.Current.Add(MessageHeaderCodes.CorrelationId, Guid.NewGuid().ToString());  
      return null; 
     } 

和commondata类:

public class CommonData 
    {    
     [ThreadStatic] 
     private static Dictionary<string, string> headerData;  

     public static Dictionary<string, string> Current 
     { 
      get 
      { 
       if (headerData == null) 
       { 
        headerData = new Dictionary<string, string>(); 
       } 
       return headerData; 
      }    
     } 

     private CommonData() 
     { 

     } 

     public static string GetHeader(string header) 
     { 
      string headerValue = string.Empty; 
      KeyValuePair<string, string> headerKeyValuePair = CommonData.Current.SingleOrDefault(p => p.Key == header); 
      headerValue = headerKeyValuePair.Value; 
      return headerValue; 
     } 

     public static void ClearDictionary() 
     { 
      Current.Clear(); 
     } 
    } 

这里的问题是以下; 在一些服务中,开发人员报告说相关标识符返回null。由于问题是间歇性的,因此目前不可能有完整的堆栈跟踪。此外,他们表示重置IIS可以暂时解决此问题。

任何帮助表示赞赏...

+0

你究竟如何检索标识符(我猜GetHeader方法)? – Bond

+0

我没有从客户端获取标识符,我只是在AfterReceiveRequest方法中为每个调用创建一个新的标识符; CommonData.Current.Add(MessageHeaderCodes.CorrelationId,Guid.NewGuid()。ToString()); – daryal

+0

是的,但你尝试稍后以某种方式检索它?怎么样?如果不是 - 那么究竟什么是空的? – Bond

回答

1

由于Blam建议,我已经采用了OperationContext.Current,通过编写扩展来存储自定义对象。以下是扩展名:

public class OperationContextExtension : IExtension<OperationContext> 
{ 
    public void Attach(OperationContext owner) 
    { 
     this.Current = new Dictionary<string, string>(); 
    } 

    public void Detach(OperationContext owner) 
    { 
     this.Current = null; 
    } 

    public Dictionary<string,string> Current { get; set; } 
} 

另一方面,我必须将System.ServiceModel引用添加到域对象。尽管由于域对象可以访问服务层,它看起来并不合适,但它解决了我的问题。

1

这并没有真正回答你的问题,而将是一个评论,但我想有一些代码...

我迷茫了你GetHeader方法。为什么你在字典做一个LINQ .FirstOrDefault(),而不是只:

public static string GetHeader(string header) 
{ 
    if(CommonData.Current.ContainsKey(header)) 
     return CommonData.Current[header]; 
    return null; 
} 

除此之外,我并没有真正看到任何你的代码错误。我很好奇开发人员在哪里得到空标识符。他们当然必须确保他们与调度员被调用的线程相同。如果有任何异步进程或ThreadPool被用于任何地方在另一个线程上运行某些内容,那么[ThreadStatic]将不存在。

我有一个问题曾经在我(很愚蠢)引用我的[ThreadStatic]变量从一个终结器,它是在GC线程上运行调用的方法,所以我的线程静态总是为空。哎呀:)

+0

您对第一条或第三条错误评论是正确的;我会更新,因为如果headerKeyValuePair为null,可能会导致异常。开发人员在与业务相关的代码中获得空值。也许最好提供更多有关结构的细节,但我认为我只是错过了一些非常简单的东西。我会尝试更新问题... – daryal

1

几件事情: 首先,您的Threadstatic值创建最好由ThreadLocal实现。基本上就是这样,只要你需要你的线程静态字段被初始化。使用

private static ThreadStatic<Dictionary<string, string>> headerData = new ThreadStatic<Dictionary<string, string>>(() => new Dictionary<string, string>()); 

其次,为了从字典中获取值,您要查找的是TryGetValue方法。即不包含 - 获取对(因为它不需要两次散列查找),当然不是SingleOrDefault。

第三,要有一个可靠的ID来标识从一个服务调用发生的任何事情,只需将其作为所有后续方法调用的参数之一。