Skip to content

Instantly share code, notes, and snippets.

@edwinf
Created August 1, 2012 01:59
Show Gist options
  • Save edwinf/3222717 to your computer and use it in GitHub Desktop.
Save edwinf/3222717 to your computer and use it in GitHub Desktop.
WCF Proxy through DI
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WCFDIProxy
{
public partial class ServiceContainer
{
partial void GetServiceLocators(ref List<ServiceContainer.ServiceLocator> locators)
{
locators = new List<ServiceContainer.ServiceLocator>();
/*
* Put your service locator code here. You can read it from XML files on the disk, you can pull
* them from a database, however you want to define them.
}
}
}
namespace WCFDIProxy
{
#region
using System;
using System.Collections.Generic;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Description;
using Microsoft.Practices.Unity;
#endregion
public partial class ServiceContainer
{
#region Constants and Fields
private readonly Dictionary<string, ServiceLocator> _ServiceLocators;
private readonly IUnityContainer _UnityContainer;
private static ServiceContainer _This;
#endregion
#region Constructors and Destructors
private ServiceContainer()
{
this._ServiceLocators = new Dictionary<string, ServiceLocator>();
this._UnityContainer = new UnityContainer();
this.BuildServiceReferences(this._UnityContainer);
}
private ServiceContainer(IUnityContainer container)
{
this._UnityContainer = container;
}
#endregion
#region Properties
/// <summary>
/// Singleton Property of the ServiceContainer class
/// </summary>
public static ServiceContainer Proxys
{
get
{
return _This;
}
}
#endregion
#region Public Methods
/// <summary>
/// This call should be put in your app start routine. This will intiialize the container per the default settings / code.
/// </summary>
public static void Initialize()
{
_This = new ServiceContainer();
}
/// <summary>
/// This function is used when you want to override your default configuration with a new unity container. This is useful when
/// you want to call a WCF service locally to try to debug issues.
/// </summary>
/// <param name="container"></param>
public static void Initialize(IUnityContainer container)
{
_This = new ServiceContainer(container);
}
/// <summary>
/// Resolves an interface into a WCF proxy based on configuration.
/// </summary>
/// <typeparam name="T">Interface to resolve</typeparam>
/// <returns>ServiceProxy wrapper of created interface</returns>
public ServiceProxy<T> Resolve<T>() where T : class, IDisposable
{
T proxy = this._UnityContainer.Resolve<T>();
return new ServiceProxy<T>(proxy);
}
#endregion
#region Methods
partial void GetServiceLocators(ref List<ServiceLocator> locators);
private void BuildServiceReferences(IUnityContainer container)
{
//Log.Debug("Begin building Service References");
List<ServiceLocator> locators = null;
GetServiceLocators(ref locators);
if (locators != null)
{
for (int i = 0; i < locators.Count; i++)
{
locators[i].FindInterfaceType();
this._ServiceLocators.Add(locators[i].InterfaceName, locators[i]);
}
//Log.Debug("Register interfaces with unity");
foreach (string interfaceName in this._ServiceLocators.Keys)
{
try
{
//registers the interface with unity specifying that to create an instance of this interface, it should call the CreateService method on the service locator class
container.RegisterType(this._ServiceLocators[interfaceName].Interface, new InjectionFactory(this._ServiceLocators[interfaceName].CreateService));
}
catch (Exception ex)
{
//Log.Error("Error instantiating service: " + interfaceName, ex);
//throw;
}
}
}
//Log.Debug("End building service references");
}
#endregion
/// <summary>
/// Class used to store definition information on the WCF Service
/// </summary>
internal class ServiceLocator
{
#region Constants and Fields
public string AssemblyName;
[NonSerialized]
public Type Interface;
public string InterfaceName;
public int MaxItemsInObjectGraph;
public int MaxReaderArrayLength;
public int MaxReaderDepth;
public int MaxReceivedMessageSize;
public int MaxStringContentLength;
public string URL;
#endregion
#region Public Methods
/// <summary>
/// Function called by the injector factory to create an instance of the resolved interface. This function returns the actual
/// ChannelFactory&gt;T&lt; WCF proxy."/>
/// </summary>
/// <param name="container">Unity container to use in resolving the interface</param>
/// <returns>WCF Proxy</returns>
public object CreateService(IUnityContainer container)
{
object ret;
//Log.Debug("Creating Service");
//Build Endpoint
//create channel
EndpointAddress ep = new EndpointAddress(this.URL);
//TODO: Hard coded NetTCP binding, change this as appropriote for your environment.
NetTcpBinding binding = new NetTcpBinding { Security = { Mode = SecurityMode.None } };
if (this.MaxReceivedMessageSize > 0)
{
binding.MaxReceivedMessageSize = this.MaxReceivedMessageSize;
}
if (this.MaxReaderArrayLength > 0)
{
binding.ReaderQuotas.MaxArrayLength = this.MaxReaderArrayLength;
}
if (this.MaxReaderDepth > 0)
{
binding.ReaderQuotas.MaxDepth = this.MaxReaderDepth;
}
if (this.MaxStringContentLength > 0)
{
binding.ReaderQuotas.MaxStringContentLength = this.MaxStringContentLength;
}
var channelFactoryType = typeof(ChannelFactory<>);
channelFactoryType = channelFactoryType.MakeGenericType(this.Interface);
//Log.Debug(" for interface: " + this.InterfaceName + ":" + this.Interface);
ChannelFactory factory = (ChannelFactory)Activator.CreateInstance(channelFactoryType, binding, ep);
var createchannel = factory.GetType().GetMethod("CreateChannel", new Type[0]);
ret = createchannel.Invoke(factory, null);
this.AddBehavior(factory);
//Log.Debug("Finished Creating Service");
return ret;
}
/// <summary>
/// Function to find the assembly needed to create a type of the instance.
/// </summary>
public void FindInterfaceType()
{
Assembly interfaceAssembly = Assembly.Load(this.AssemblyName);
this.Interface = interfaceAssembly.GetType(this.InterfaceName);
}
#endregion
#region Methods
/// <summary>
/// Add Logging message inspector to faciliate End-to-End logging
/// </summary>
/// <param name="factory">
/// The factory.
/// </param>
private void AddBehavior(ChannelFactory factory)
{
if (factory != null)
{
if (this.MaxItemsInObjectGraph > 0)
{
foreach (OperationDescription op in factory.Endpoint.Contract.Operations)
{
DataContractSerializerOperationBehavior dataContractBehavior =
op.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dataContractBehavior != null)
{
dataContractBehavior.MaxItemsInObjectGraph = this.MaxItemsInObjectGraph;
}
}
}
}
}
#endregion
}
}
}
namespace WCFDIProxy
{
using System;
using System.ServiceModel;
public class ServiceProxy<T> : IDisposable where T : class, IDisposable
{
public ServiceProxy(T proxy)
{
this.Proxy = proxy;
}
public T Proxy { get; private set; }
#region IDisposable Members
public void Dispose()
{
IClientChannel p = this.Proxy as IClientChannel;
if (p != null)
{
if (p.State == CommunicationState.Faulted)
{
p.Abort();
}
else
{
try
{
//No reason to call dispose across a service boundry as we're disposing of the local version
//this.Proxy.Dispose();
p.Close();
}
catch (Exception ex)
{
//Log.Error("Error calling dispose on the service:", ex);
p.Abort();
}
}
}
else
{
//Local version of proxy interface, dispose
if (this.Proxy != null)
{
this.Proxy.Dispose();
}
}
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment