WCF Essentials—Implementing InProcFactory T
| Visual C# Tutorials |
| .NET Framework Tutorials |
| © 2007 O'Reilly Media |
Implementing InProcFactory<T>
All in-proc calls should use named pipes, and should also flow all transactions. You
can use programmatic configuration to automate the configurations of both the client
and the service, and use ChannelFactory<T> to avoid the need for a proxy.
Example 1-22 shows the implementation of InProcFactory with some of the code
removed for brevity.
Example 1-22. The InProcFactory class
public static class InProcFactory { struct HostRecord { public HostRecord (ServiceHost host,string address) { Host = host; Address = address; } public readonly ServiceHost Host; public readonly string Address; } static readonly Uri BaseAddress = new Uri("net.pipe://localhost/"); static readonly Binding NamedPipeBinding; static Dictionary<Type,HostRecord> m_Hosts = new Dictionary<Type,HostRecord>( ); static InProcFactory( ) { NetNamedPipeBinding binding = new NetNamedPipeBinding( ); binding.TransactionFlow = true; NamedPipeBinding = binding; AppDomain.CurrentDomain.ProcessExit += delegate { foreach(KeyValuePair<Type,HostRecord> pair in m_Hosts) { pair.Value.Host.Close( ); } }; } public static I CreateInstance<S,I>( ) where I : class where S : I { HostRecord hostRecord = GetHostRecord<S,I>( ); return ChannelFactory<I>.CreateChannel(NamedPipeBinding, new EndpointAddress(hostRecord.Address)); } static HostRecord GetHostRecord<S,I>( ) where I : class where S : I { HostRecord hostRecord; if(m_Hosts.ContainsKey(typeof(S))) { hostRecord = m_Hosts[typeof(S)]; } else { ServiceHost host = new ServiceHost(typeof(S), BaseAddress); string address = BaseAddress.ToString() + Guid.NewGuid().ToString( ); hostRecord = new HostRecord(host,address); m_Hosts.Add(typeof(S),hostRecord); host.AddServiceEndpoint(typeof(I),NamedPipeBinding,address); host.Open( ); } return hostRecord; } public static void CloseProxy<I>(I instance) where I : class { ICommunicationObject proxy = instance as ICommunicationObject; Debug.Assert(proxy != null); proxy.Close( ); } }
The main challenge facing InProcFactory is that CreateInstance( ) can be called to
instantiate services of every type. For every service type, there should be a single
matching host (an instance of ServiceHost). Allocating a host instance for each call is
not a good idea. The problem is what should CreateInstance( ) do when it is asked
to instantiate a second object of the same type:
IMyContract proxy1 = InProcFactory.CreateInstance<MyService,IMyContract>( ); IMyContract proxy2 = InProcFactory.CreateInstance<MyService,IMyContract>( );
The solution is to internally manage a dictionary that maps service types to a particular
host instance. When CreateInstance( ) is called to create an instance of a particular
type, it looks in the dictionary, using a helper method called GetHostRecord( ),
which creates the host only if the dictionary does not already contain the service
type. If it needs to create a host, GetHostRecord( ) programmatically adds to that host
an endpoint, using a new GUID as a unique pipe name. CreateInstance( ) then grabs
the address of the endpoint from the host record and uses ChannelFactory<T> to create
the proxy. In its static constructor, which is called upon the first use of the class, InProcFactory subscribes to the process exit event, using an anonymous method to close all hosts when the process shuts down. Finally, to help the clients close the proxy, InProcFactory provides the CloseProxy( ) method, which queries the proxy to
ICommunicationObject and closes it.
|

