6

I'm building a series of WCF Services that are going to be used by more than one application. Because of that I'm trying to define a common library to access WCF services.

Knowing that each service request made by different users should use a different Channel I'm thinking in cache the Channel per-request (HttpContext.Current.Items) and cache the ChannelFactory used to create the channel per Application (HttpApplication.Items) since I can create more than one channel with the same ChannelFactory.

However, I have a question regarding this cache mechanism when it comes to closing the ChannelFactory and Channel.

  1. Do I need to close the Channel after it's used, at the end of the request, or is it ok to leave it there to be closed (?) when the context of that request dies?
  2. What about ChannelFactory? Since each channel is associated with the ChannelFactory that created it, is it safe to keep the same ChannelFactory during the life of the application process (AppDomain)?

This is the code I'm using to manage this:

public class ServiceFactory
{
    private static Dictionary<string, object> ListOfOpenedChannels
    {
        get
        {
            if (null == HttpContext.Current.Items[HttpContext.Current.Session.SessionID + "_ListOfOpenedChannels"])
            {
                HttpContext.Current.Items[HttpContext.Current.Session.SessionID + "_ListOfOpenedChannels"] = new Dictionary<string, object>();
            }

            return (Dictionary<string, object>)HttpContext.Current.Items[HttpContext.Current.Session.SessionID + "_ListOfOpenedChannels"];
        }
        set
        {
            HttpContext.Current.Items[HttpContext.Current.Session.SessionID + "_ListOfOpenedChannels"] = value;
        }
    }

    public static T CreateServiceChannel<T>()
    {
        string key = typeof(T).Name;

        if (ListOfOpenedChannels.ContainsKey(key))
        {
            return (T)ListOfOpenedChannels[key];
        }
        else
        {
            ChannelFactory<T> channelF = new ChannelFactory<T>("IUsuarioService");
            T channel = channelF.CreateChannel();
            ListOfOpenedChannels.Add(key, channel);
            return channel;
        }
    }
}

Thanks!

Jeroen
  • 60,696
  • 40
  • 206
  • 339
tucaz
  • 6,524
  • 6
  • 37
  • 60

2 Answers2

8

Ideally close the channel as soon as you are done with it. This will place it back into the channel pool so it can be used by another worker thread.

Yes, the channel factory (the expensive bit) can remain for the lifetime of the application.


Update

As of .Net 4.5 there is a built in caching options for factories ChannelFactory Caching .NET 4.5

MattC
  • 3,984
  • 1
  • 33
  • 49
  • 4
    +1 exactly - cache the "expensive" part - the ChannelFactory - but create the channels as needed and close/dispose as early as possible – marc_s Dec 01 '09 at 13:11
  • Great guys, so if I keep just ONE ChannelFactory cached during the lifetime of the application and reuse it in all channel creations I won't have problems, right? – tucaz Dec 01 '09 at 14:25
  • One channel factory per service type, correct. This way you don't have to do all the reflection and type creation every call, which is why for performance you shouldn't use the proxy generated by VS as the class is gens uses a new factory every call. – MattC Dec 01 '09 at 16:59
  • 1
    "you shouldn't use the proxy generated by VS as the class is gens uses a new factory every call" - according to the link at the end that particular problem was solved with .NET 3.5 so that ChannelFactory is cached within ClientBase. http://blogs.msdn.com/wenlong/archive/2007/10/27/performance-improvement-of-wcf-client-proxy-creation-and-best-practices.aspx – Xiaofu Mar 13 '10 at 15:45
  • @Xiaofu: It is true that `ClientBase` will use an MRU cache on the `ChannelFactory` in some cases. It will not cache the ChannelFactoy if the `Endpoint` or `ClientCredentials` are changed or if the `Binding` is programmatically defined. There is a way to force the caching of the ChannelFactory by setting the CacheSettings to `CacheSettings.AlwaysOn`. http://msdn.microsoft.com/en-us/library/hh314046(v=vs.110).aspx – Derek W Aug 16 '14 at 01:19
1

This is an aside. Why are you using SessionID as a context key? The context.Items is unique per request. That is:

HttpContext.Current.Items[HttpContext.Current.Session.SessionID +"_ListOfOpenedChannels"]

should be functionally equivalent to:

HttpContext.Current.Items["ListOfOpenedChannels"]
Frank
  • 11
  • 1
  • I realized that after I posted this question. The final result is here: http://github.com/tucaz/CommunicationsManager Thanks – tucaz Mar 04 '10 at 11:09