WCF Percall模式和threadstatic变量

时间:2012-06-26 12:22:15

标签: c# .net wcf c#-4.0

我们有多个WCF服务都使用InstanceContextMode = PerCall,所有WCF服务实例都是通过使用Unity(IOC)和实现IInstanceProvider生成的。

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

为了实现这一点,通过实现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会暂时解决此问题。

感谢任何帮助...

3 个答案:

答案 0 :(得分: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线程上运行,因此我的线程静态始终为null。哎呀:)

答案 1 :(得分: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引用。虽然它似乎不是一种正确的方式,因为域对象可以访问服务层,但它解决了我的问题。

答案 2 :(得分:1)

有几件事: 首先,ThreadLocal可以更好地实现Threadstatic值的创建。无论何时需要初始化threadstatic字段,基本上都是这种情况。使用

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

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

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