Dave Hillier
Dave Hillier

Reputation: 19063

MEF Import from a Castle Kernel in Silverlight

I'm currently using MEF in my project, however, a legacy component uses Castle to export all its components.

I would like to be able to Import from this kernel when creating new objects, in addition to getting the exports from the Xap.

Is this possible? Can you show me some example code?

Upvotes: 2

Views: 254

Answers (2)

Dave Hillier
Dave Hillier

Reputation: 19063

As Matthew correctly said, the way to do this is using an ExportProvider

Another example is here (it demonstrates exports from Xaml). Below is what I did in the end to solve the problem.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Linq;
using Castle.MicroKernel;
using Castle.MicroKernel.Registration;
using Castle.Windsor;

namespace MEFCastleBridge
{
    public class CastleExportProvider : ExportProvider
    {
        WindsorContainer _container;
        private readonly Dictionary<ExportDefinition, List<Export>> _exports =
            new Dictionary<ExportDefinition, List<Export>>();
        private readonly object _sync = new object();

        public CastleExportProvider(WindsorContainer container)
        {
            _container = container;
            var handlers = _container.Kernel.GetAssignableHandlers(typeof(object));
            foreach (var handler in handlers)
            {
                RegisterCastleComponent(handler);
            }
            _container.Kernel.ComponentRegistered += ComponentRegistered;
        }

        protected override IEnumerable<Export> GetExportsCore(
            ImportDefinition definition, AtomicComposition atomicComposition)
        {
            var contractDefinition = definition as ContractBasedImportDefinition;
            var retVal = Enumerable.Empty<Export>();
            if (contractDefinition != null)
            {
                string contractName = contractDefinition.ContractName;
                if (!string.IsNullOrEmpty(contractName))
                {
                    var exports =
                       from e in _exports
                       where string.Compare(e.Key.ContractName, contractName, StringComparison.OrdinalIgnoreCase) == 0
                       select e.Value;

                    if (exports.Count() > 0)
                    {
                        retVal = exports.First();
                    }
                }
            }

            return retVal;
        }

        void RegisterCastleComponent(IHandler handler)
        {
            var type = handler.Service;
            var contractName = type.ToString();
            lock (_sync)
            {
                var found = from e in _exports
                            where string.Compare(e.Key.ContractName, 
                                contractName, StringComparison.OrdinalIgnoreCase) == 0
                            select e;

                if (found.Count() == 0)
                {
                    var metadata = new Dictionary<string, object>();
                    var definition = new ExportDefinition(contractName, metadata);
                    _exports.Add(definition, new List<Export>());
                }

                var wrapper = new Export(contractName, () => _container.Resolve(type));
                found.First().Value.Add(wrapper);
            }
        }

        void ComponentRegistered(string key, IHandler handler)
        {
            RegisterCastleComponent(handler);
        }
    }

    public interface IMyComponent
    {
        string TheString { get; }
    }

    public class RegisteredComponent : IMyComponent
    {
        public string TheString { get { return "RegisteredComponent"; } }
    }

    [Export(typeof(IMyComponent))]
    public class ExportedComponent : IMyComponent
    {
        public string TheString { get { return "ExportedComponent"; } }
    }

    public class ExportExample
    {
        // Will contain an instance of RegisteredComponent and ExportedComponent
        [ImportMany]
        public List<IMyComponent> Components { get; set; }

        public ExportExample()
        {
            // Create a Windsor container and add a type.
            var container = new WindsorContainer();
            container.Register(Component.For<IMyComponent>().ImplementedBy<MyComponent>().LifeStyle.Singleton);

            // Add the Export Provider, in addition to the DeploymentCatalog
            var compContainer = new CompositionContainer(new DeploymentCatalog(), new CastleExportProvider(container));
            // Should only be called once, before any attempt to SatisfyImports.
            CompositionHost.Initialize(compContainer);
            CompositionInitializer.SatisfyImports(this);

            Test = string.Join(", ", Components.Select(c => c.DoSomething));
        }

        public string Test { get; set; }
    }
}

Upvotes: 1

Matthew Abbott
Matthew Abbott

Reputation: 61589

MEF was designed to be as flexible as possible, and one of its secretly hidden but real nice features, is the ability to define new ExportProvider instances, that allow you to plug in additional components. I've talked about this previously by utilising the Common Service Locator project in an ASP.NET MVC with MEF Project (see part 3 here).

The CSL is a nice flexible approach, as there are many specific CSL implementations for many of the existing IoC containers, such as Castle, Autofac, Ninject, Unity etc.

Another good example can be found here, which demonstrates a slightly different, but fundamentally similar approach.

Upvotes: 2

Related Questions