.NET & Funky Fresh

Syndication

News

  • <script type="text/javascript" src="http://ws.amazon.com/widgets/q?ServiceVersion=20070822&amp;MarketPlace=US&amp;ID=V20070822/US/bluspiconinc-20/8001/8b68bf4b-6724-40e7-99a5-a6decf6d8648"> </script>
Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
MEF, IServiceLocator and Caliburn

My company just began working on a large, modular WPF project.  I’ve been doing some research and recently took some time to dig into the Managed Extensibility Framework (MEF).  I’m quite impressed with the direction that the project is going.  MEF is going to fulfill our modularity needs quite well.

At the same time, I have refactored Caliburn to use the CommonServiceLocator that P&P released last year.  Caliburn is a loosely coupled set of UI services and requires dependency injection to work.  I have IServiceLocator implementations of Windsor, StructureMap, Unity and Spring.NET.  Many of these implementations are based on the adapter code that can be found on the CommonServiceLocator site.  Interestingly, there isn’t an implementation of IServiceLocator for MEF.

As my current project needs MEF to solve modularity concerns and Caliburn to solve UI architecture issues, I spent some time today writing an IServiceLocator adapter for MEF.  It turned out not to be too difficult, but the code is not pretty.  This stems from the fact that MEF’s CompositionContainer is generics based, but ServiceLocatorImplBase uses Type instances.  I had to resort to using reflection to interact with the underlying container.  I’m going to post the code below so that you can see how to do this sort of thing, but also in hopes that someone like Glenn can correct any stupid stuff ;)  (I was hoping there is a non-reflection-based mechanism that I just didn’t see.)

In addition to implementing IServiceLocator, Caliburn needs a way to inform the container of its own components.  In Caliburn you do this by either implementing IConfigurator or by providing a delegate which can take Caliburn’s ComponentInfo and register the services appropriately with the container.  This is where the real MEF challenge comes in.  MEF is not exactly a DI container, and thus doesn’t have the typical registration mechanism that you might expect.  I wasn’t really sure what the best way to solve this problem was, so I implemented my own ComposablePart.  Below is the entire MEFAdapter which implements both IServiceLocator and IConfigurator.  I would appreciate feedback from any MEF experts or members of the MEF team.  Much thanks in advance!

MEFAdapter

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using Caliburn.Core;
using Microsoft.Practices.ServiceLocation;

namespace Caliburn.MEF
{
    public class MEFAdapter : ServiceLocatorImplBase, IConfigurator
    {
        private readonly CompositionContainer _container;
        private readonly MethodInfo _getExportedObjectByType;
        private readonly MethodInfo _getExportedObjectByTypeAndKey;
        private readonly MethodInfo _getExportedObjectsByType;

        public MEFAdapter(CompositionContainer container)
        {
            _container = container;

            _getExportedObjectByType = _container.GetType().GetMethod("GetExportedObject", Type.EmptyTypes);
            _getExportedObjectByTypeAndKey = _container.GetType().GetMethod("GetExportedObject", new[] { typeof(string) });
            _getExportedObjectsByType = _container.GetType().GetMethod("GetExportedObjects", Type.EmptyTypes);
        }

        protected override object DoGetInstance(Type serviceType, string key)
        {
            MethodInfo genericMethod;

            if(!string.IsNullOrEmpty(key))
            {
                genericMethod = _getExportedObjectByTypeAndKey.MakeGenericMethod(serviceType);
                return genericMethod.Invoke(_container, new[] { key });
            }

            genericMethod = _getExportedObjectByType.MakeGenericMethod(serviceType);
            return genericMethod.Invoke(_container, null);
        }

        protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
        {
            MethodInfo genericMethod = _getExportedObjectsByType.MakeGenericMethod(serviceType);
            var results = (IEnumerable)genericMethod.Invoke(_container, null);

            foreach(var result in results)
            {
                yield return result;
            }
        }

        public void ConfigureWith(IEnumerable<ComponentInfo> components)
        {
            var batch = new CompositionBatch();

            foreach(var componentInfo in components)
            {
                batch.AddPart(new ComponentPart(componentInfo));
            }

            _container.Compose(batch);
        }
    }
}

ComponentPart

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Primitives;
using System.Linq;
using System.Reflection;
using Caliburn.Core;

namespace Caliburn.MEF
{
    public class ComponentPart : ComposablePart
    {
        private readonly ComponentInfo _info;
        private readonly List<ImportDefinition> _imports = new List<ImportDefinition>();
        private readonly ExportDefinition[] _exports;
        private readonly Dictionary<ImportDefinition, Export> _satisfiedImports = new Dictionary<ImportDefinition, Export>();
        private object _cachedInstance;
        private readonly ConstructorInfo _greedyConstructor;

        public ComponentPart(ComponentInfo info)
        {
            _info = info;

            _greedyConstructor = (from c in info.Implementation.GetConstructors()
                                  orderby c.GetParameters().Length descending
                                  select c).FirstOrDefault();

            if (_greedyConstructor != null)
            {
                foreach (var parameterInfo in _greedyConstructor.GetParameters())
                {
                    var parameterType = parameterInfo.ParameterType;

                    var import = new ImportDefinition(
                        def => def.ContractName == parameterType.FullName,
                        ImportCardinality.ExactlyOne,
                        false,
                        true
                        );

                    _imports.Add(import);
                }
            }

            string contractName = info.Key is string ? info.Key.ToString() : ((Type)info.Key).FullName;

            var export = new ExportDefinition(
                contractName,
                null
                );

            _exports = new[] {export};
        }

        public override object GetExportedObject(ExportDefinition definition)
        {
            if (_info.Lifetime == ComponentLifetime.PerRequest)
                return CreateInstance(definition);

            if(_cachedInstance == null)
                _cachedInstance = CreateInstance(definition);

            return _cachedInstance;
        }

        private object CreateInstance(ExportDefinition definition)
        {
            var args = new List<object>();

            if(_greedyConstructor != null)
            {
                foreach(var parameterInfo in _greedyConstructor.GetParameters())
                {
                    var arg = (from export in _satisfiedImports.Values
                               where export.Definition.ContractName == parameterInfo.ParameterType.FullName
                               select export).FirstOrDefault();

                    args.Add(arg.GetExportedObject());
                }
            }

            return args.Count > 0
                       ? Activator.CreateInstance(_info.Implementation, args.ToArray())
                       : Activator.CreateInstance(_info.Implementation);
        }

        public override void SetImport(ImportDefinition definition, IEnumerable<Export> exports)
        {
            _satisfiedImports[definition] = exports.FirstOrDefault();
        }

        public override IEnumerable<ExportDefinition> ExportDefinitions
        {
            get { return _exports; }
        }

        public override IEnumerable<ImportDefinition> ImportDefinitions
        {
            get { return _imports; }
        }
    }
}

Posted 02-04-2009 5:12 PM by Rob Eisenberg

[Advertisement]

Comments

Silverlight Travel » MEF, IServiceLocator and Caliburn wrote Silverlight Travel &raquo; MEF, IServiceLocator and Caliburn
on 02-05-2009 1:12 AM

Pingback from  Silverlight Travel &raquo; MEF, IServiceLocator and Caliburn

DotNetShoutout wrote MEF, IServiceLocator and Caliburn - .NET & Funky Fresh
on 02-05-2009 10:27 AM

Thank you for submitting this cool story - Trackback from DotNetShoutout

.NET & Funky Fresh wrote A MEF Misunderstanding
on 02-05-2009 2:24 PM

Yesterday I wrote about integrating Caliburn and MEF along with getting things to work with IServiceLocator

Community Blogs wrote A MEF Misunderstanding
on 02-05-2009 2:39 PM

Yesterday I wrote about integrating Caliburn and MEF along with getting things to work with IServiceLocator

Silverlight Travel » A MEF Misunderstanding wrote Silverlight Travel &raquo; A MEF Misunderstanding
on 02-06-2009 5:28 AM

Pingback from  Silverlight Travel &raquo; A MEF Misunderstanding

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)