WCF and Proxies - The InProcFactory Class
(Page 4 of 6 )
To demonstrate the power of ChannelFactory<T>, consider my static helper class InProcFactory, defined as:
public static class InProcFactory
{
public static I CreateInstance<S,I>() where I : class
where S : I;
public static void CloseProxy<I>(I instance) where I : class;
//More members
}
InProcFactoryis designed to streamline and automate in-proc hosting. TheCreateInstance()method takes two generic type parameters: the type of the service S and the type of the supported contractI.CreateInstance()constrainsSto derive fromI. UsingInProcFactoryis straightforward:
IMyContract proxy = InProcFactory.CreateInstance<MyService,IMyContract>();
proxy.MyMethod();
InProcFactory.CloseProxy(proxy);
It literally takes a service class and hoists it up as a WCF service. It is as close as you can get in WCF to the old Win32 call ofLoadLibrary().
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 facingInProcFactoryis thatCreateInstance()can be called to instantiate services of every type. For every service type, there should be a single matching host (an instance ofServiceHost). Allocating a host instance for each call is not a good idea. The problem is what shouldCreateInstance()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. WhenCreateInstance()is called to create an instance of a particular type, it looks in the dictionary, using a helper method calledGetHostRecord(), 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 newGUIDas a unique pipe name.CreateInstance()then grabs the address of the endpoint from the host record and usesChannelFactory<T>to create the proxy. In its static constructor, which is called upon the first use of the class,InProcFactorysubscribes 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,InProcFactoryprovides theCloseProxy()method, which queries the proxy toICommunicationObjectand closes it.
Reliability
WCF and other service-oriented technologies make a distinction between transport reliability and message reliability. Transport reliability(such as the one offered by TCP) offers point-to-point guaranteed delivery at the network packet level, as well as guarantees the order of the packets. Transport reliability is not resilient to dropping network connections and a variety of other communication problems.
Message reliability,as the name implies, deals with reliability at the message level independent of how many packets are required to deliver the message. Message reliability provides for end-to-end guaranteed delivery and order of messages, regardless of how many intermediaries are involved, and how many network hops are required to deliver the message from the client to the service. Message reliability is based on an industry standard for reliable message-based communication that maintains a session at the transport level. It offers retries in case of transport failures such as dropping a wireless connection; it automatically deals with congestion, message buffering, and flow control; and it can adjust the number of messages accordingly. Message reliability also deals with managing the connection itself via connection verification and cleanup when no longer needed.
Next: Binding and Reliability >>
More Windows Scripting Articles
More By O'Reilly Media
|
This article is excerpted from chapter one of Programming WCF Services, written by Juval Lowry (O'Reilly, 2007; ISBN: 0596526997). Check it out today at your favorite bookstore. Buy this book now.
|
|