Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

creating WCF ChannelFactory<T>

I'm trying to convert an existing .NET Remoting application to WCF. Both server and client share common interface and all objects are server-activated objects.

In WCF world, this would be similar to creating per-call service and using ChannelFactory<T> to create a proxy. I'm struggling a bit with how to properly create ChannelFactory<T> for an ASP.NET client.

For performance reasons, I want to cache ChannelFactory<T> objects and just create channel every time I call the service. In .NET remoting days, there used to be RemotingConfiguration.GetRegisteredWellknownClientTypes() method to get a collection of client objects that I could then cache. It appears, in WCF world there is no such thing, although I was able to get a collection of endpoints from config file.

Now here is what I think will work. I can create something like this:

public static ProxyHelper {     static Dictionary<Type, object> lookup = new Dictionary<string, object>();        static public T GetChannel<T>()     {         Type type = typeof(T);         ChannelFactory<T> factory;          if (!lookup.ContainsKey(type))         {             factory = new ChannelFactory<T>();             lookup.Add(type, factory);         }         else         {             factory = (ChannelFactory<T>)lookup[type];         }          T proxy = factory.CreateChannel();            ((IClientChannel)proxy).Open();          return proxy;     }     } 

I think the above code will work, but I'm a bit worried about multiple threads trying to add new ChannelFactory<T> objects if it's not in the lookup. Since I'm using .NET 4.0, I was thinking about using ConcurrentDictionary and use GetOrAdd() method or use TryGetValue() method first to check if ChannelFactory<T> exists and it does not exist, then use GetOrAdd() method. Not sure about performance though of ConcurrentDictionary.TryGetValue() and ConcurrentDictionary.GetOrAdd() method.

Another minor question is whether I need to call ChannelFactory.Close() method on channel factory objects after ASP.NET application ends or can I just let .NET framework dispose the channel factory objects on its own. The proxy channel will always be closed after calling service method by using ((IChannel)proxy).Close() method.

like image 395
Eric Avatar asked Jul 08 '10 01:07

Eric


People also ask

What is ChannelFactory in WCF?

A Channel Factory enables you to create a communication channel to the service without a proxy. A Channel Factory that creates and manages the various types of channels which are used by a client to send a message to various configured service endpoints.

What is Factory Channel?

Channel Factory is a global technology and data platform that maximizes both performance efficiency and contextual suitability, delivering contextual performance for advertisers on YouTube.

Does .NET core support WCF?

NET Core and . NET 5 support calling WCF services, but won't offer server-side support for hosting WCF. There are two recommended paths for modernizing WCF apps: gRPC is built on modern technologies and has emerged as the most popular choice across the developer community for RPC apps.


2 Answers

Here's a helper class that I use to handle channel factories:

public class ChannelFactoryManager : IDisposable {     private static Dictionary<Type, ChannelFactory> _factories = new Dictionary<Type,ChannelFactory>();     private static readonly object _syncRoot = new object();      public virtual T CreateChannel<T>() where T : class     {         return CreateChannel<T>("*", null);     }      public virtual T CreateChannel<T>(string endpointConfigurationName) where T : class     {         return CreateChannel<T>(endpointConfigurationName, null);     }      public virtual T CreateChannel<T>(string endpointConfigurationName, string endpointAddress) where T : class     {         T local = GetFactory<T>(endpointConfigurationName, endpointAddress).CreateChannel();         ((IClientChannel)local).Faulted += ChannelFaulted;         return local;     }      protected virtual ChannelFactory<T> GetFactory<T>(string endpointConfigurationName, string endpointAddress) where T : class     {         lock (_syncRoot)         {             ChannelFactory factory;             if (!_factories.TryGetValue(typeof(T), out factory))             {                 factory = CreateFactoryInstance<T>(endpointConfigurationName, endpointAddress);                 _factories.Add(typeof(T), factory);             }             return (factory as ChannelFactory<T>);         }     }      private ChannelFactory CreateFactoryInstance<T>(string endpointConfigurationName, string endpointAddress)     {         ChannelFactory factory = null;         if (!string.IsNullOrEmpty(endpointAddress))         {             factory = new ChannelFactory<T>(endpointConfigurationName, new EndpointAddress(endpointAddress));         }         else         {             factory = new ChannelFactory<T>(endpointConfigurationName);         }         factory.Faulted += FactoryFaulted;         factory.Open();         return factory;     }      private void ChannelFaulted(object sender, EventArgs e)     {         IClientChannel channel = (IClientChannel)sender;         try         {             channel.Close();         }         catch         {             channel.Abort();         }         throw new ApplicationException("Exc_ChannelFailure");     }      private void FactoryFaulted(object sender, EventArgs args)     {         ChannelFactory factory = (ChannelFactory)sender;         try         {             factory.Close();         }         catch         {             factory.Abort();         }         Type[] genericArguments = factory.GetType().GetGenericArguments();         if ((genericArguments != null) && (genericArguments.Length == 1))         {             Type key = genericArguments[0];             if (_factories.ContainsKey(key))             {                 _factories.Remove(key);             }         }         throw new ApplicationException("Exc_ChannelFactoryFailure");     }      public void Dispose()     {         Dispose(true);     }      protected virtual void Dispose(bool disposing)     {         if (disposing)         {             lock (_syncRoot)             {                 foreach (Type type in _factories.Keys)                 {                     ChannelFactory factory = _factories[type];                     try                     {                         factory.Close();                         continue;                     }                     catch                     {                         factory.Abort();                         continue;                     }                 }                 _factories.Clear();             }         }     } } 

Then I define a service invoker:

public interface IServiceInvoker {     R InvokeService<T, R>(Func<T, R> invokeHandler) where T: class; } 

and an implementation:

public class WCFServiceInvoker : IServiceInvoker {     private static ChannelFactoryManager _factoryManager = new ChannelFactoryManager();     private static ClientSection _clientSection = ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection;      public R InvokeService<T, R>(Func<T, R> invokeHandler) where T : class     {         var endpointNameAddressPair = GetEndpointNameAddressPair(typeof(T));         T arg = _factoryManager.CreateChannel<T>(endpointNameAddressPair.Key, endpointNameAddressPair.Value);         ICommunicationObject obj2 = (ICommunicationObject)arg;         try         {             return invokeHandler(arg);         }         finally         {             try             {                 if (obj2.State != CommunicationState.Faulted)                 {                     obj2.Close();                 }             }             catch             {                 obj2.Abort();             }         }     }      private KeyValuePair<string, string> GetEndpointNameAddressPair(Type serviceContractType)     {         var configException = new ConfigurationErrorsException(string.Format("No client endpoint found for type {0}. Please add the section <client><endpoint name=\"myservice\" address=\"http://address/\" binding=\"basicHttpBinding\" contract=\"{0}\"/></client> in the config file.", serviceContractType));         if (((_clientSection == null) || (_clientSection.Endpoints == null)) || (_clientSection.Endpoints.Count < 1))         {             throw configException;         }         foreach (ChannelEndpointElement element in _clientSection.Endpoints)         {             if (element.Contract == serviceContractType.ToString())             {                 return new KeyValuePair<string, string>(element.Name, element.Address.AbsoluteUri);             }         }         throw configException;     }  } 

Now every time you need to call a WCF service you could use this:

WCFServiceInvoker invoker = new WCFServiceInvoker(); SomeReturnType result = invoker.InvokeService<IMyServiceContract, SomeReturnType>(     proxy => proxy.SomeMethod() ); 

This assumes that you've defined a client endpoint for the IMyServiceContract service contract in the config file:

<client>     <endpoint          name="myservice"          address="http://example.com/"          binding="basicHttpBinding"          contract="IMyServiceContract" /> </client> 
like image 116
Darin Dimitrov Avatar answered Oct 07 '22 07:10

Darin Dimitrov


Yes, if you want to create something like this - a static class to hold all those ChannelFactory<T> instances - you definitely have to make sure this class is 100% thread-safe and cannot stumble when accessed concurrently. I haven't used .NET 4's features much yet, so I cannot comment on those specifically - but I would definitely recommend to make this as safe as possible.

As for your second (minor) question: the ChannelFactory itself is a static class - so you cannot really call a .Close() method on it. If you meant to ask whether or not to call the .Close() method on the actual IChannel, then again: yes, try your best to be a good citizen and close those channels if you ever can. If you miss one, .NET will take care of it - but don't just toss your unused channels on the floor and go on - clean up after yourself! :-)

like image 24
marc_s Avatar answered Oct 07 '22 06:10

marc_s