简体   繁体   中英

WCF Percall mode and threadstatic variables

We have multiple WCF services all working with InstanceContextMode=PerCall and all WCF service instances are generated by employing Unity (IOC) and implementing IInstanceProvider.

A correlation identifier is used to audit all method calls and database processes with the same identifier.

In order to achieve this, an endpoint behavior is created by implementing IDispatchBehavior and in AfterReceiveRequest method, a guid is generated and assigned to a ThreadStatic ( CommonData ) property. This property can be access in all layers of the application. The following code block shows the population of CommonData, and the CommonData class;

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;
        }

and commondata class:

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();
        }
    }

The problem here is the following; In some of the services, developers reported that the correlation identifier returns null. Since the problem is intermittant it is not possible to have a full stack trace at the moment. Also, they stated that reseting IIS resolves this problem temporarily.

Any help is appreciated...

This doesn't really answer your question, and would have been a comment, except I wanted to have some code...

I am confused by your GetHeader method. Why are you doing a Linq .FirstOrDefault() on the dictionary, instead of just:

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

Aside from that, I don't actually see anything wrong with your code. I am curious as to where the developers are getting the null identifier. They would of course have to make sure they are on the same thread that the dispatcher was called on. If any async process, or ThreadPool was used anywhere to run something on another thread, then the [ThreadStatic] would not exist.

I had an issue once where I (very stupidly) referenced my [ThreadStatic] variable from a method that was called by a finalizer, which is run on the GC thread, so my thread static was always null. oops :)

As Blam suggested, I have employed OperationContext.Current by writing an Extension to store custom objects. Below is the extension:

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; }
}

On the otherhand, I have to add System.ServiceModel reference to the domain objects. Although it does not seem to be a proper way as domain objects can access to the service layer, it resolved my problem.

Several things: First, Your Threadstatic value creation is better implemented by ThreadLocal. This is basically the case whenever you need your threadstatic field to be initialized. Use

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

Second, for acquiring the value from the dictionary, what you're looking for is the TryGetValue method. Ie not Contains-Get pair (as it does hash lookup twice unnecessary), and certainly not SingleOrDefault.

And third, to have a reliable ID identifying whatever happened from the one service call, just have it as one of the parameters of all the subsequent method calls.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM