Thursday, 25 March 2010

Problems with MEF

I have been implementing MEF in a large Windows project. Within the project we have lazy loading objects like Tasks and various models. It is necessary to create separate classes for these collections of objects. These classes have imports that need to be resolved. The problem is that the Imports must be satisfied by a creating the dependency containers within the class. This takes a long time because MEF uses reflection to search for exports within the assemblies, in our case we have a very large code base and this takes a long time. It also means a lot of code duplication for classes that use cross cutting concerns like loggers because before using the class the Imports must be satisfied. The other problem we had was that if we needed to import a single export and that there are more than 1 available (eg Logger and NullLogger) then MEF does not resolve this and does not give any warning about what is going on. We also found that making unit tests was difficult because we had no control over the composition.

So we are looking again at Spring.net. The main reason is that we want to compose our container once at the start of the application. Composition is a very time consuming operation and we don’t want to have decomposition. Since Spring.net uses a configuration file we have more control over the composition which is handy when it comes to unit testing. If we use an interface to the Spring compose method we can use mocks in our unit tests. Our application does not require the ability to dynamically recompose when a new dll is dropped into a directory because our application runs in a batch mode which means it will respond to changes in the configuration simply the next time the batch is run.

Over the last month I do realize that there are a number of scenarios when MEF would be the better choice, in particular in Silverlight applications. I am lucky that my application has been architected in such a way that the choice of dependency injection technology is orthogonal to the overall design of the application because we used SOLID OO principles and programmed cross cutting concerns with interfaces.

Wednesday, 24 March 2010

Problems installing VS2010 Release Candidate

We have been working on an entity framework data access layer where we use T4 templates to generate the POCO classes. In Beta2 this functionality was not complete therefore we needed VS2010 RC to properly test out our DAL. The uninstall of VS2010 was really no problem. Since I am working on an old Windows XP workstation I needed to apply Windows XP service Pack 3. The service pack took a very long time to clean up, I needed to let it run over night.

The problem came when I installed VS2010. The setup dialog came up, I was able to go through some of them and then suddenly they disappeared. After rebooting the Installation failed with a message box saying there are some problem with CiceroUIWndFrame in setup.exe right after it finishes loading components.

After some searching I found http://social.msdn.microsoft.com/Forums/en-US/setupprerelease/thread/dbcdcd52-d162-4460-9920-33c9ab54b36f

The solution was “Display the language bar at the taskbar, right-click on it and choose settings. Then you remove the hand writing support from the list.”. Thank god for internet because I would never have come to the idea that the Beta 2 had installed a handwriting support into my language settings!

Friday, 5 March 2010

Lazy loading an IEnumerable with MEF version 2

Here I have refactored the previous example to use strongly typed attributes…

using Ccp.Contracts.Task;
namespace CCP.BusinessLogic.Tasks
{
    public interface ITaskEngineMetadata
    {
        TaskType taskType { get; }
    }
}

using System.ComponentModel.Composition;
using Ccp.Contracts.Task;
namespace CCP.BusinessLogic.Tasks
{
    [MetadataAttribute]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
    public class TaskEngineAttribute : ExportAttribute
    {
        public TaskEngineAttribute()
            : base(typeof(ITask))
        {
        }
        public TaskType taskType { get; set; }
    }
}

namespace Ccp.Contracts.Task
{
    public enum TaskType
    {
        None = 0,
        Test = 7,
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Ccp.Contracts.Task
{
    public interface ITask
    {
        void Run(TaskClaimCheck ticket);
    }
}

using Ccp.Contracts.Task;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace CCP.BusinessLogic.Tasks
{
    public class TaskFactory
    {
        [ImportMany(typeof(ITask))]
        public IEnumerable<Lazy<ITask, ITaskEngineMetadata>> TaskEngines { get; set; }

        public TaskFactory()
        {
            InitializeMef();
        }

        public ITask Create(TaskType taskType)
        {
            foreach (var task in TaskEngines)
            {
                if (task.Metadata.taskType == taskType)
                {
                        return task.Value;
                }
            }
            throw new NotImplementedException("must add mapping to task mapping dictionary");
        }

        private void InitializeMef()
        {
            DirectoryCatalog directoryCatalog = new DirectoryCatalog(@".");
            CompositionBatch batch = new CompositionBatch();
            batch.AddPart(this);
            CompositionContainer container = new CompositionContainer(directoryCatalog);
            container.Compose(batch);
        }
    }
}

using Ccp.Contracts.Task;
using Ccp.Contracts;
using System.Threading;
using System.ComponentModel.Composition;
namespace CCP.BusinessLogic.Tasks
{
    [TaskEngine(taskType= TaskType.Test)]
    public class TestTask : ITask
    {
        public void Run(TaskClaimCheck ticket)
        {
            return;
        }

    }
}

Lazy loading an IEnumerable with MEF version 1

Here is an application of a Factory using MEF based on an example from Mike Taulty…

namespace Ccp.Contracts.Task
{
    public interface ITask
    {
        void Run(TaskClaimCheck ticket);
    }
}

using System.ComponentModel.Composition;
namespace CCP.BusinessLogic.Tasks
{
    [Export(typeof(ITask))]
    [ExportMetadata("TaskTypeID", TaskType.Test)]
    public class TestTask : ITask

    }
}
namespace Ccp.Contracts.Task
{
    public enum TaskType
    {
        None = 0,
        Test = 7,
    }
}

using Ccp.Contracts.Task;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

namespace CCP.BusinessLogic.Tasks
{
    public class TaskFactory
    {
        [ImportMany(typeof(ITask))]
        public IEnumerable<Lazy<ITask, Dictionary<string, object>>> TaskEngines { get; set; }

        public TaskFactory()
        {
            InitializeMef();
        }

        public ITask Create(TaskType taskType)
        {
            foreach (var task in TaskEngines)
            {
                if (task.Metadata.ContainsKey("TaskTypeID"))
                {
                    object oTaskTypeID;
                    task.Metadata.TryGetValue("TaskTypeID", out oTaskTypeID);
                    TaskType itaskType = (TaskType)oTaskTypeID;
                    if (itaskType == taskType)
                    {
                        return task.Value;
                    }
                }
            }
            throw new NotImplementedException("must add mapping to task mapping dictionary");
        }

        private void InitializeMef()
        {
            DirectoryCatalog directoryCatalog = new DirectoryCatalog(@".");
            CompositionBatch batch = new CompositionBatch();
            batch.AddPart(this);
            CompositionContainer container = new CompositionContainer(directoryCatalog);
            container.Compose(batch);
        }
    }
}