Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WCF Channel and ChannelFactory Caching

So I've decided to up the performance a bit in my WCF application, and attempt to cache Channels and the ChannelFactory. There's two questions I have about all of this that I need to clear up before I get started.

1) Should the ChannelFactory be implemented as a singleton?

2) I'm kind of unsure about how to cache/reuse individual channels. Do you have any examples of how to do this you can share?

It's probably important to note that my WCF service is being deployed as a stand alone application, with only one endpoint.

EDIT:

Thank you for the responses. I still have a few questions though...

1)I guess I'm confused as to where the caching should occur. I'm delivering a client API that uses this code to another department in our company. Does this caching occur on the client?

2)The client API will be used as part of a Silverlight application, does this change anything? In particular, what caching mechanisms are available in such a scenario?

3)I'm still not clear about the design of the GetChannelFactory method. If I have only one service, should only one ChannelFactory ever be created and cached?

I still haven't implemented any caching feature (because I'm utterly confused about how it should be done!), but here's what I have for the client proxy so far:

namespace MyCompany.MyProject.Proxies
{
    static readonly ChannelFactory<IMyService> channelFactory =
        new ChannelFactory<IMyService>("IMyService");

    public Response DoSomething(Request request)
    {
        var channel = channelFactory.CreateChannel();

        try
        {
            Response response = channel.DoSomethingWithService(request);
            ((ICommunicationObject)channel).Close();
            return response;
        }
        catch(Exception exception)
        {
            ((ICommenicationObject)channel).Abort();
        }
    }
}
like image 501
Didaxis Avatar asked Oct 20 '11 20:10

Didaxis


2 Answers

Use the ChannelFactory to create an instance of the factory, then cache that instance. You can then create communicatino channels as needed/desired from the cached istance.

Do you have a need for multiple channel factories (i.e.., are there multiple services)? In my experience, that's where you'll see the biggest benefit in performance. Creating a channel is a fairly inexpensive task; it's setting everything up at the start that takes time.

I would not cache individual channels - I'd create them, use them for an operation, and then close them. If you cache them, they may time out and the channel will fault, then you'll have to abort it and create a new one anyway.

Not sure why you'd want to usea singleton to implement ChannelFactory, especially if you're going to create it and cache it, and there's only one endpoint.

I'll post some example code later when I have a bit more time.

UPDATE: Code Examples

Here is an example of how I implemented this for a project at work. I used ChannelFactory<T>, as the application I was developing is an n-tier app with several services, and more will be added. The goal was to have a simple way to create a client once per life of the application, and then create communication channels as needed. The basics of the idea are not mine (I got it from an article on the web), though I modified the implementation for my needs.

I have a static helper class in my application, and within that class I have a dictionary and a method to create communication channels from the channelf factory.

The dictionary is as follows (object is the value as it will contain different channel factories, one for each service). I put "Cache" in the example as sort of a placeholder - replace the syntax with whatever caching mechanism you're using.

public static Dictionary<string, object> OpenChannels {     get     {         if (Cache["OpenChannels"] == null)         {             Cache["OpenChannels"] = new Dictionary<string, object>();         }          return (Dictionary<string, object>)Cache["OpenChannels"];     }     set     {         Cache["OpenChannels"] = value;     } } 

Next is a method to create a communication channel from the factory instance. The method checks to see if the factory exists first - if it does not, it creates it, puts it in the dictionary and then generates the channel. Otherwise it simply generates a channel from the cached instance of the factory.

public static T GetFactoryChannel<T>(string address) {      string key = typeof(T.Name);      if (!OpenChannels.ContainsKey(key))     {         ChannelFactory<T> factory = new ChannelFactory<T>();         factory.Endpoint.Address = new EndpointAddress(new System.Uri(address));         factory.Endpoint.Binding = new BasicHttpBinding();         OpenChannels.Add(key, factory);     }      T channel = ((ChannelFactory<T>)OpenChannels[key]).CreateChannel();      ((IClientChannel)channel).Open();      return channel; } 

I've stripped this example down some from what I use at work. There's a lot you can do in this method - you can handle multiple bindings, assign credentials for authentication, etc. Its pretty much your one stop shopping center for generating a client.

Finally, when I use it in the application, I generally create a channel, do my business, and close it (or abort it if need be). For example:

IMyServiceContract client;  try {     client = Helper.GetFactoryChannel<IMyServiceContract>("http://myserviceaddress");      client.DoSomething();      // This is another helper method that will safely close the channel,      // handling any exceptions that may occurr trying to close.     // Shouldn't be any, but it doesn't hurt.     Helper.CloseChannel(client); } catch (Exception ex) {     // Something went wrong; need to abort the channel     // I also do logging of some sort here     Helper.AbortChannel(client); } 

Hopefully the above examples will give you something to go on. I've been using something similar to this for about a year now in a production environment and it's worked very well. 99% of any problems we've encountered have usually been related to something outside the application (either external clients or data sources not under our direct control).

Let me know if anything isn't clear or you have further questions.

like image 117
Tim Avatar answered Nov 09 '22 23:11

Tim


You could always just make your ChannelFactory static for each WCF Contract...

You should be aware that from .Net 3.5 the proxy objects are pooled for performance reasons by the channel factory. Calling the ICommunicationObject.Close() method actually returns the object to the pool in the hope it can be reused.

I would look at the profiler if you want to do some optimisation, if you can prevent just one IO call being made in your code it could far outweigh any optimisation you will make with the channel factory. Don't pick an area to optimise, use the profiler to find where you can target an optimisation. If you have an SQL database for instance, you will probably find some low hanging fruit in your queries that will get you orders of magnitude performance increases if these haven't already been optimised.

like image 27
Spence Avatar answered Nov 09 '22 23:11

Spence