1

I have an ASP.Net site that consumes ASP.Net XML webservices. To communicate with each webmethod in the webservice I have a static class with static business methods, one for each webmethod in the webservice. The static business methods create a new instance of the webreference class each time they are called. The new instance of the webreference class is used to only call the webmethod, none of the properties in the instance of the webreference are changed from their defaults.

My question is can I create just a global static instance of the webreference class to use by all of the static business methods instead of creating a new one each time a static business method is called? (Basically are instances of a webreference class thread safe?)

My concern here is that the instance of the webreference class has some properties that are not thread safe and since the code is for a web site, multiple threads calling the same static business methods at the same time would cause issues between the threads.

The reason I'm asking is to try and find additional changes I can make to increase the site's performance. In tracing the site's performance I see a fair amount of time being spent on creating an instance of the webreference class. Additionally based on the garbage collection counters I'm seeing a lot of time being spent there too.

Example Code:

This is what I'm currently doing

public static class WebMethodWrapper
{
    public static bool CallMethodA(string p1)
    {
       using(com.mysite.service1 _provider = new com.mysite.service1())
       {
            return(_provider.WebMethodA(p1));
       }
    }
    public static bool CallMethodB(string p1)
    {
       using(com.mysite.service1 _provider = new com.mysite.service1())
       {
            return(_provider.WebMethodB(p1));
       }
    }
}

This is what I'd like to do

public static class WebMethodWrapper
{
    static com.mysite.service1 _Provider = null;
    static WebMethodWrapper()
    {
       _Provider = new com.mysite.service1();
    }


    public static bool CallMethodA(string p1)
    {
       return(_Provider.WebMethodA(p1));  
    }
    public static bool CallMethodB(string p1)
    {
      return(_Provider.WebMethodB(p1));
    }
}
Aaron
  • 511
  • 3
  • 25

1 Answers1

1

My question is can I create just a global static instance of the webreference class to use by all of the static business methods instead of creating a new one each time a static business method is called? (Basically are instances of a webreference class thread safe?)

My concern here is that the instance of the webreference class has some properties that are not thread safe and since the code is for a web site, multiple threads calling the same static business methods at the same time would cause issues between the threads.

A jolly good question to which it seems you are well on the way to answering. I agree you should probably stick with your current approach where each static method creates its own local copy of the service client. This encourages thread-safety not only from the point of view of the client, but also guarantees that remote calls to the service are done so using unique proxies - where results are not potentially multiplexed with other requests.

If you went down the other route of using a shared instance, then you have to take into consideration those scenarios where the service faults in one thread.

  • Maybe there was a timeout?
  • Maybe some remote business logic failed?
  • Maybe the network failed because your room-mate is downloading the latest episode of Game of Thrones exceeding your download quota?

You would then need to invalidate that client and recreate a new one. All of this would need to be safely thread-locked. It sort of becomes quite complex to manage this orchestration.

Let's consider your alternative code:

public static bool CallMethodA(string p1)
{
   return(_Provider.WebMethodA(p1));  
}

Let's say this was successfully called the first time. Now imagine you need to call this 5 mins 5 seconds later but sadly by this time the server has severed the connection because it has a timeout of 5 mins. Your second call faults. The above code would need to be adjusted to allow for those scenarios. In our simple example below we recreate the client during a failure and try once more.

Perhaps:

public static class WebMethodWrapper
{
    static com.mysite.service1 _Provider = null;
    static object _locker = new object();

    static WebMethodWrapper()
    {
        _Provider = new com.mysite.service1();
    }

    static com.mysite.service1 Client
    {
        get
        {
            lock (_locker)
            {
                return _Provider;
            }
        }
    }

    public static bool CallMethodA(string p1)
    {
        try
        {
            return (Client.WebMethodA(p1));
        }
        catch (Exception ex) // normally just catch the exceptions of interest
        {
            // Excercise for reader - use a single method instead of repeating the below
            // recreate
            var c = RecreateProxy();

            // try once more.  
            return (c.WebMethodA(p1));
        }
    }

    public static bool CallMethodB(string p1)
    {
        try
        {
            return (Client.WebMethodB(p1));
        }
        catch (Exception ex) // normally just catch the exceptions of interest
        {
            // Excercise for reader - use a single method instead of repeating the below
            // recreate
            var c = RecreateProxy();

            // try once more.  
            return (c.WebMethodB(p1));
        }
    }

    static com.mysite.service1 RecreateProxy()
    {
        lock (_locker)
        {
            _Provider = new com.mysite.service1();
            return _Provider;
        }
    }
}

All of this could be wrapped-up in some generic service client cache that could maintain a collection of ready-to-go clients in a connection pool? Maybe a background thread periodically pings each client to keep them alive? An exercise for the reader perhaps.

Then again, sharing the same proxy instance between threads may not be a good idea from the service point of view, unless your service is marked as per-call which may or may not impact your design or performance.

Conclusion

Your current code is arguably safer and less complex.

Good luck!

Community
  • 1
  • 1
  • Roy-- Thanks for the detailed answer. I'm going to look over the generated client code again to see what global class variables are in there that are being used internally between the methods. Do you believe the same rule applies for the service model that is available in 4.0+? – Aaron Sep 11 '15 at 14:16
  • You're quite welcome Aaron. I think if your service is marked as [per-call](https://msdn.microsoft.com/en-us/library/system.servicemodel.instancecontextmode(v=vs.110).aspx), then each thread would get its own instance of the service and it would be quite safe. Check out [WCF proxy singleton in multithreaded app](http://stackoverflow.com/questions/9108789/wcf-proxy-singleton-in-multithreaded-app) and [creating WCF ChannelFactory](http://stackoverflow.com/questions/3200197/creating-wcf-channelfactoryt/3201001#3201001) for more info. –  Sep 11 '15 at 22:21