This week it was asked to me to find some elegant and practical way to use WCF. After some reading (very good Dong’s article http://blogs.msdn.com/wenlong/archive/2007/10/27/performance-improvement-of-wcf-client-proxy-creation-and-best-practices.aspx) I reached some issues:
1. I want to write only one client code for running both local and remote objects (like the good old Remoting).
2. Wouldn’t it be nice if I could build a WCF client application without the needing Svcutil.exe tool?
3. I don’t want to call Close() every time I’m done with the proxy. Why don’t use Dispose() and using {}?
4. I want to be able to cache proxy objects.
So, here are some ideas to help solve these issues.
First I decided to build an object factory that I’ll use from the presentation tier to call business objects from the business tier. This object factory has a method public static T CreateInstance<T>() that is responsible to instantiate the object of type T. They can be local or remote objects, depending on some configuration settings. This way I only write one client code, and I solve the issue #1.
To solve the others issues I created a base class Proxy<T> : IDisposable, where T is an interface. It will be the base of all proxy objects. This class creates and opens an internal ChannelFactory<T> object in its constructor, and closes it in its Dispose() method.
That gives us two things: a chance to cache both the ChannelFactory and/or the proxy object; and let us to benefit from the using {} structure, so the Close() method is always called.
Now is the tricky part. In order to eliminate the need of using the Svcutil.exe tool, we need to “build” the proxy class at runtime. The result is basically a class that derives from Proxy<T> and implements T.
So let’s code!
This is the base class that all proxies derive from:
public class Proxy<T> : IDisposable
{
private bool m_disposed = false;
private ChannelFactory<T> m_factory = null;
private T m_bo = default(T);
protected Proxy()
//In this version it's always created a new ChannelFactory and proxy.
//Next version it could get these objects from a pool.
m_factory = new ChannelFactory<T>(typeof(T).FullName);
m_factory.Open();
m_bo = m_factory.CreateChannel();
}
// This is how the derived proxy type talks to the remote object.
protected T BO
get { return m_bo; }
#region IDisposable Members
public void Dispose()
Dispose(true);
GC.SuppressFinalize(this);
private void Dispose(bool disposing)
if (!this.m_disposed)
if (disposing)
// Next version, instead of releasing the objects, it'll return them to the pool.
// Dispose managed resources.
m_bo = default(T);
if (m_factory != null)
if (m_factory.State == CommunicationState.Opened)
try
m_factory.Close();
catch
m_factory.Abort();
else
m_factory = null;
// Call the appropriate methods to clean up unmanaged resources here.
m_disposed = true;
~Proxy()
Dispose(false);
#endregion
This is the object factory class. The main method is CreateInstance<T>(). Based on configurations settings, the method instantiate a local or a remote object. For the proxy classes created there is a cache, so they’re created only once.
public class BusinessFactory
private static Dictionary<Type, Type> m_proxys = null;
private static AssemblyBuilder m_assemblyBuilder = null;
private static ModuleBuilder m_moduleBuilder = null;
static BusinessFactory()
m_proxys = new Dictionary<Type, Type>();
public static T CreateInstance<T>()
object obj = null;
Type type = typeof(T);
Type proxyType = null;
//Read from config if it's a remote object.
bool isRemote = ReadFromConfig(type.FullName);
if (isRemote)
//Search for the proxy type in the cache.
proxyType = m_proxys[type];
catch (KeyNotFoundException)
//If it's not found, create the proxy type.
proxyType = CreateProxyType<T>();
m_proxys.Add(type, proxyType);
obj = Activator.CreateInstance(proxyType);
obj = Activator.CreateInstance(type);
catch (Exception ex)
throw ex;
return (T)obj;
//Build a new proxy class.
private static Type CreateProxyType<T>()
if (m_assemblyBuilder == null)
//Holds only one AssemblyBuilder and ModuleBuilder.
m_assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("ProxiesAssembly"), AssemblyBuilderAccess.RunAndSave);
m_moduleBuilder = m_assemblyBuilder.DefineDynamicModule("ProxiesAssembly", "ProxiesAssembly.dll");
//Create the TypeBuilder for the proxy class.
TypeBuilder tb = m_moduleBuilder.DefineType(type.Name + "DynamicType", TypeAttributes.Public);
//Derive the proxy class from Proxy<T>.
tb.SetParent(typeof(Proxy<T>));
//Implement interface T in the proxy class.
tb.AddInterfaceImplementation(type);
//Adds all interface methods to the proxy class.
MethodInfo[] methods = type.GetMethods();
foreach (MethodInfo method in methods)
//Initialize the parameter types array.
ParameterInfo[] parameters = method.GetParameters();
Type[] parameterTypes = new Type[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
parameterTypes[i] = parameters[i].ParameterType;
MethodBuilder meth = tb.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.Virtual, method.ReturnType, parameterTypes);
tb.DefineMethodOverride(meth, method);
//Generates the method's body.
//It basicaly will call Proxy<T>.BO.Method(params)
ILGenerator il = meth.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, (typeof(Proxy<T>)).GetMethod("get_BO"));
il.Emit(OpCodes.Ldarg, i + 1);
il.Emit(OpCodes.Callvirt, method);
il.Emit(OpCodes.Ret);
return tb.CreateType();
Here is how the client application uses it.
Let’s assume we have the contract below and its implementation somewhere in the business tier.
[ServiceContract()]
public interface IUser
[OperationContract()]
int Save(BEUser user);
int Delete(int userId);
BEUser GetByLogon(string logon);
BEUser GetById(int userId);
List<BEUser> GetAll();
int AddUserToGroups(BEUser user, List<BEGroup> groups);
int RemoveUserFromGroups(BEUser user, List<BEGroup> groups);
A presentation tier could run this simple code and execute User’s methods from a local or a remote object, just depending on a configuration setting. No need of Scvutil tool.
IUser bpUser = BusinessFactory.CreateInstance<IUser>();
UPData.Users = bpUser.GetAll();
...
And if we want to close the channel as soon as possible, we can use the using { } statement.
using ((Proxy<IUser>)bpUser)
· WCF Feature Details
o http://msdn2.microsoft.com/en-us/library/ms733103(VS.85).aspx
· Performance Improvement for WCF Client Proxy Creation in .NET 3.5 and Best Practices
o http://blogs.msdn.com/wenlong/archive/2007/10/27/performance-improvement-of-wcf-client-proxy-creation-and-best-practices.aspx