using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection.Emit; using System.Reflection; using System.ServiceModel; using System.ServiceModel.Channels; namespace WcfClientTests { public static class WcfClientFactory { #region Fields private static Dictionary _cache = new Dictionary(); #endregion Fields #region Methods public static Binding CreateBinding(BindingType bindingType) { Binding binding = null; TimeSpan timeout = TimeSpan.FromSeconds(30); int maxMessageSize = int.MaxValue; System.Xml.XmlDictionaryReaderQuotas readerQutas = new System.Xml.XmlDictionaryReaderQuotas(); readerQutas.MaxDepth = 32; readerQutas.MaxStringContentLength = 8192; readerQutas.MaxArrayLength = 16384; readerQutas.MaxBytesPerRead = 4096; readerQutas.MaxNameTableCharCount = 16384; if (bindingType == BindingType.Tcp) { NetTcpBinding tcpBinding = new NetTcpBinding(); tcpBinding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard; tcpBinding.MaxBufferSize = maxMessageSize; tcpBinding.MaxBufferPoolSize = 524288; tcpBinding.MaxReceivedMessageSize = maxMessageSize; tcpBinding.TransferMode = TransferMode.Buffered; tcpBinding.ReaderQuotas = readerQutas; binding = tcpBinding; } else if (bindingType == BindingType.NamedPipes) { NetNamedPipeBinding namedPipeBinding = new NetNamedPipeBinding(); namedPipeBinding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard; namedPipeBinding.MaxBufferSize = maxMessageSize; namedPipeBinding.MaxBufferPoolSize = 524288; namedPipeBinding.MaxReceivedMessageSize = maxMessageSize; namedPipeBinding.TransferMode = TransferMode.Buffered; namedPipeBinding.ReaderQuotas = readerQutas; binding = namedPipeBinding; } else { BasicHttpBinding httpBinding = new BasicHttpBinding(BasicHttpSecurityMode.None); httpBinding.AllowCookies = false; httpBinding.BypassProxyOnLocal = false; httpBinding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard; httpBinding.MaxBufferSize = maxMessageSize; httpBinding.MaxBufferPoolSize = 524288; httpBinding.MaxReceivedMessageSize = maxMessageSize; httpBinding.MessageEncoding = WSMessageEncoding.Text; httpBinding.TextEncoding = Encoding.UTF8; httpBinding.TransferMode = TransferMode.Buffered; httpBinding.UseDefaultWebProxy = true; httpBinding.ReaderQuotas = readerQutas; binding = httpBinding; } binding.SendTimeout = timeout; binding.ReceiveTimeout = timeout; binding.OpenTimeout = timeout; binding.CloseTimeout = timeout; return binding; } public static ICommunicationObject GetClient(Type contractType, Binding binding, EndpointAddress address) { return CreateFactory(contractType).Create(binding, address) as ICommunicationObject; } public static IClientFactory CreateFactory(Type contractType) { Type clientBaseType = typeof(ClientBase<>).MakeGenericType(contractType); // Try to get the create wrapper from the cache. IClientFactory wrapperFactory = null; if (_cache.TryGetValue(contractType, out wrapperFactory)) { return wrapperFactory; } if (!contractType.IsInterface) { throw new Exception("Must be an interface !"); } // Create the module and assembly builder. string name = Guid.NewGuid().ToString().Replace("-", ""); AssemblyName asmName = new AssemblyName(name); AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave); ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule(asmName.Name, name + ".dll"); // Create the wrapper type. Type wrapperType = CreateWrapperType(modBuilder, contractType, clientBaseType); // Create the proxy interface. The ide gens this. Type proxyInterface = CreateChannelInterfaceType(modBuilder, contractType); // Create the factory type. Type wrapperFactoryType = CreateFactoryType(modBuilder, wrapperType); //builder.AsmBuilder.Save("Test.dll"); // Create the factory, add to cache, and return wrapperFactory = Activator.CreateInstance(wrapperFactoryType) as IClientFactory; _cache[contractType] = wrapperFactory; return wrapperFactory; } private static Type CreateWrapperType(ModuleBuilder builder, Type contractType, Type clientBaseType) { ILGenerator ilGen = null; // Create the type and add the interface. TypeBuilder typeBuilder = builder.DefineType("TestClient_" + contractType.Name, TypeAttributes.Class | TypeAttributes.Public, clientBaseType); typeBuilder.AddInterfaceImplementation(contractType); ConstructorInfo constructorInfo = clientBaseType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(Binding), typeof(EndpointAddress) }, null); ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[] { typeof(Binding), typeof(EndpointAddress) }); ilGen = constructorBuilder.GetILGenerator(); ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Ldarg_1); ilGen.Emit(OpCodes.Ldarg_2); ilGen.Emit(OpCodes.Callvirt, constructorInfo); ilGen.Emit(OpCodes.Ret); // Get the interface methods MethodInfo[] interfaceMethods = contractType.GetMethods(); MethodInfo channelGetMethod = clientBaseType.GetProperty("Channel", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(true); MethodBuilder methodBuilder = null; Type[] methodParmeterTypes = null; // Loop through all the methods. for (int i = 0; i < interfaceMethods.Length; i++) { // Get the types methodParmeterTypes = Array.ConvertAll(interfaceMethods[i].GetParameters(), p => p.ParameterType); // Create method in our wrapper type. methodBuilder = typeBuilder.DefineMethod( interfaceMethods[i].Name, MethodAttributes.Public | MethodAttributes.Virtual, CallingConventions.HasThis, interfaceMethods[i].ReturnType, methodParmeterTypes//bleh ); ilGen = methodBuilder.GetILGenerator(); // Push the channel property value onto the stack ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Callvirt, channelGetMethod); // Load the method parameters on the stack for (int x = 0; x < methodParmeterTypes.Length; x++) { switch (x + 1) { case 0: ilGen.Emit(OpCodes.Ldarg_0); break; case 1: ilGen.Emit(OpCodes.Ldarg_1); break; case 2: ilGen.Emit(OpCodes.Ldarg_2); break; case 3: ilGen.Emit(OpCodes.Ldarg_3); break; default: ilGen.Emit(OpCodes.Ldarg_S, x); break; } } // Call the method. ilGen.EmitCall(OpCodes.Callvirt, interfaceMethods[i], null); // Return the value ilGen.Emit(OpCodes.Ret); // Mark the method. typeBuilder.DefineMethodOverride(methodBuilder, interfaceMethods[i]); } return typeBuilder.CreateType(); } private static Type CreateChannelInterfaceType(ModuleBuilder modBuilder, Type contractType) { // Interface that gets created in ide generated code TypeBuilder typeBuilder = modBuilder.DefineType(contractType.Name + "Channel", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract); typeBuilder.AddInterfaceImplementation(contractType); typeBuilder.AddInterfaceImplementation(typeof(IClientChannel)); typeBuilder.AddInterfaceImplementation(typeof(IContextChannel)); typeBuilder.AddInterfaceImplementation(typeof(IChannel)); typeBuilder.AddInterfaceImplementation(typeof(ICommunicationObject)); typeBuilder.AddInterfaceImplementation(typeof(IExtensibleObject)); typeBuilder.AddInterfaceImplementation(typeof(IDisposable)); return typeBuilder.CreateType(); } private static Type CreateFactoryType(ModuleBuilder modBuilder, Type wrapperType) { // Create the type and add the interface TypeBuilder typeBuilder = modBuilder.DefineType(wrapperType.Name + "Factory", TypeAttributes.Public | TypeAttributes.Class); typeBuilder.AddInterfaceImplementation(typeof(IClientFactory)); // Create the method. MethodBuilder methodBuilder = typeBuilder.DefineMethod("Create", MethodAttributes.Public | MethodAttributes.Virtual, CallingConventions.HasThis, typeof(object), new Type[] { typeof(Binding), typeof(EndpointAddress) }); // Get the constructor for our new wrapper type ConstructorInfo info = wrapperType.GetConstructor(new Type[] { typeof(Binding), typeof(EndpointAddress) }); // Gen the il ILGenerator gen = methodBuilder.GetILGenerator(); gen.Emit(OpCodes.Ldarg_1); gen.Emit(OpCodes.Ldarg_2); gen.Emit(OpCodes.Newobj, info); gen.Emit(OpCodes.Ret); // Mark the method. MethodInfo createMethod = typeof(IClientFactory).GetMethod("Create"); typeBuilder.DefineMethodOverride(methodBuilder, createMethod); // Return the type. return typeBuilder.CreateType(); } #endregion Methods } public interface IClientFactory { object Create(Binding binding, EndpointAddress address); } public enum BindingType { Http, Tcp, NamedPipes } }