Udostępnij za pośrednictwem


Mixing MEF and PIAB

The Policy Injection Application Block (PIAB) provides a way to dynamically apply cross-cutting concerns by configuration or attributes. The combination of this technique and composition using Managed Extensibility Framework (MEF) can be realized through the use of a factory method as demonstrated here. The example demonstrates PIAB by the use of a CallHandler specifically for incrementing a performance counter. This is handled by the PerformanceCounterCallHandlerAttribute found in Microsoft.Practices.EnterpriseLibrary.PolicyInjection.dll. In the example the interface ISomeService is what is exported to MEF for composition.

 

    public interface ISomeService

    {

        void TestMethod();

    }

In order to use a type with policy injection it must derive from MarshalByRefObject:

    public class SomeService : MarshalByRefObject, ISomeService

    {

        public SomeService() {}

        [PerformanceCounterCallHandler("EntLibPerfTest", "lta")]

        public void TestMethod()

        {

            Console.WriteLine(":-) Called my TestMethod"); //LOGIC

        }

    }

The parameters to the PerformanceCounterCallHandler attribute represents the performance counter category and instance name respectively. In order to allow PIAB to do its work you should retrieve the type through the static Create method on the PolicyInjection class (or Wrap it):

 

                SomeService s = PolicyInjection.Create<SomeService>();

 

MEF works with [Export] and [Import] attributes and will construct the registered export when a client asks for an implementation (according to the Creation Policy of course). Therefore it is not an option to export the type SomeService directly. Instead we create a Factory for the type that returns the type after applying registered policies for that type:

 

    public class SomeServiceFactory

    {

        [Export(typeof(ISomeService))]

        public ISomeService Instance

        {

            get

            {

                //return new SomeService();

                return PolicyInjection.Create<SomeService>()

            }

        }

    }

This solves the problem of delegating the construction to our Policy Injection Application Block, and you can simply write:

 

    class Program

    {

        static void Main(string[] args)

        {

            using (var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()))

            {

                using (var container = new CompositionContainer(catalog))

                {

                    ISomeService svc = container.GetExport<ISomeService>().Value; // wrapped

                    svc.TestMethod();

                }

            }

        }

    }

If we take a dependency in another class of this service as:

 

    [Export(typeof(MEFComposedType))]

    public class MEFComposedType

    {

        ISomeService _someservice;

        [ImportingConstructor]

        public MEFComposedType(ISomeService service)

        {

            _someservice = service;

        }

        public void Foo()

        {

            _someservice.TestMethod();

        }

    }

It will be created by our factory method and injected as expected:

 

    class Program

    {

        static void Main(string[] args)

        {

            using (var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()))

            {

                using (var container = new CompositionContainer(catalog))

                {

                    MEFComposedType mct = container.GetExportedValue<MEFComposedType>();

                    mct.Foo();

                }

            }

        }

    }

This will work fine for dependencies you can isolate, but if you rely on MEF to construct and compose more complex graphs of types it may become necessary to use the static Wrap method of the PolicyInjection before using services.

Another way to accomplish almost the same is to export the actual type AND the interfaces and use the factory to add the policy:

    [Export]

    public class SomeService : MarshalByRefObject, ISomeService

    {

        [PerformanceCounterCallHandler("EntLibPerfTest", "lta")]

        public void TestMethod()

        {

            Console.WriteLine(":-) Called my TestMethod");

        }

    }

    public class SomeServiceFactory

    {

        SomeService _someservice;

        [ImportingConstructor]

        public SomeServiceFactory(SomeService someservice)

        {

            _someservice = someservice;

        }

        [Export(typeof(ISomeService))]

        public ISomeService Instance

        {

            get

            {

                return PolicyInjection.Wrap<SomeService> (_someservice);

            }

        }

    }

This method has the unfortunate side-effect of exporting actual types to the catalog, but solves the problem of using policy injection in complex graphs of dependencies. An alternative method could be interface inheritance. Having one public Interface that other components takes dependencies on and an internal interface deriving from this would allow for the usual flexibility.

Comments

  • Anonymous
    October 03, 2011
    Nice blog, thanks for sharing! I came across this because I was looking for a way to apply PIAB in my wcf service layer which use a custom instance provider to crank out service instance. MEF container is used to produce instance for the service locator. Therefore what works for me is the other way around: I export all services to MEF first, then in the custom instance provider, I locate the service instance and then use PolicyInjection to wrap around the instance so that PI proxy can be returned instead. Below is the code sample: Service:    [Export(typeof(ICustomerService))]    public class CustomerService : ICustomerService    {        #region ICustomerService Members        public Customer GetCustomer(string customerID)        {            return CustomerDAO.GetCustomer(customerID);        }        #endregion    } Custom instance provider public class PolicyInjectionInstanceProvider : IInstanceProvider    {        private Type serviceContractType;        private CompositionContainer Container { get; set; }        public PolicyInjectionInstanceProvider(Type t)        {            if (t!= null && !t.IsInterface)            {                throw new ArgumentException("Specified Type must be an interface");            }            this.serviceContractType = t;        }        #region IInstanceProvider Members        public object GetInstance(InstanceContext instanceContext, System.ServiceModel.Channels.Message message)        {            Type type = instanceContext.Host.Description.ServiceType;            if (serviceContractType != null)            {                Compose();                var importDefinition = new ImportDefinition(i => i.ContractName.Equals(serviceContractType.FullName), serviceContractType.FullName, ImportCardinality.ZeroOrMore, false, false);                var atomicComposition = new AtomicComposition();                IEnumerable<Export> extensions;                Container.TryGetExports(importDefinition, atomicComposition, out extensions);                if (extensions != null && extensions.Count() > 0)                {                    var service = extensions.First().Value;                    return PolicyInjection.Wrap(serviceContractType, service);                }                                //return PolicyInjection.Create(type);            }            else            {                if (!type.IsMarshalByRef)                {                    throw new ArgumentException("Type Must inherit MarshalByRefObject if no ServiceInterface is Specified");                }                return PolicyInjection.Create(type);            }            return null;        }        public object GetInstance(InstanceContext instanceContext)        {            return GetInstance(instanceContext, null);        }        public void ReleaseInstance(InstanceContext instanceContext, object instance)        {            var disposable = instance as IDisposable;            if (disposable != null)            {                disposable.Dispose();            }            Container.Dispose();        }        #endregion        #region Private Methods        private void Compose()        {            var catalog = new AggregateCatalog();            catalog.Catalogs.Add(new DirectoryCatalog(@".")); //Extensions            Container = new CompositionContainer(catalog);        }        #endregion    }