Sunday, March 27, 2011

WCF Channel Factory and Unity 2.0

Unity 2.0 does not support ChannelFactory in configuration natively. However, we could extend Unity. Inspired by Chris Tavares's FactoryElement extension for static factory configuration, here is the ChannelFactory extension for WCF channel factory configuration in Unity.

The element will get the generic type of the ChannelFactory and calling the CreateChannel() method upon the injection.
public class ChannelFactoryElement : InjectionMemberElement {
        private const string endpointConfigurationNamePropertyName = "endpointConfigurationName";
        private static int numFactories;
        private readonly int factoryNum;

        public ChannelFactoryElement() {
            factoryNum = Interlocked.Increment(ref numFactories);
        }

        [ConfigurationProperty(endpointConfigurationNamePropertyName, IsKey = false, IsRequired = true)]
        public string Name {
            get { return (string)base[endpointConfigurationNamePropertyName]; }
            set { base[endpointConfigurationNamePropertyName] = value; }
        }

        public override string Key {
            get { return "ChannelFactory " + factoryNum; }
        }

        public override IEnumerable<injectionmember> GetInjectionMembers(IUnityContainer container, Type fromType,
            Type toType, string name) {

            Type factoryType = typeof(ChannelFactory<>).MakeGenericType(toType);            
            var constructor = factoryType.GetConstructor(new Type[] { typeof(string) });
            var factory = constructor.Invoke(new object[]{Name});
            var method = factoryType.GetMethod("CreateChannel", Type.EmptyTypes);
            return new InjectionMember[] { new InjectionFactory(o => method.Invoke(factory, null)) };
        }

    }

The extension will register the factory as the element name of ChannelFactoryElement.
public class ChannelFactoryConfigExtension : SectionExtension {
        public override void AddExtensions(SectionExtensionContext context) {
            context.AddElement<ChannelFactoryElement>("factory");
        }
    }

The factory element uses endpointConfigurationName to retrieve the client's endpoint as in ChannelFactory(endpointConfigurationName) constructor.
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <sectionExtension type="MyNamespace.ChannelFactoryConfigExtension, MyAssembly" />
    <container>      
      <register type="MyNamespace.IMyService, MyAssembly">
        <factory endpointConfigurationName="testing"/>
      </register>
    </container>
  </unity>

No comments: