Click here to Skip to main content
15,895,142 members
Articles / Programming Languages / C#
Tip/Trick

Exploring Dependency Injection / Inversion of Control (IoC) with Managed Extensibility Framework (MEF)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
13 Feb 2016Apache3 min read 18.4K   2   8
Exploring Dependency Injection / Inversion of Control (IoC) with Managed Extensibility Framework (MEF)

Introduction

The Managed Extensibility Framework (MEF) enables developers to extend their application without any configuration. It has many features, one of which allows for developers to use it as a Dependency Injection framework.

If you want developers to be able to extend your application using only source code, then that means you would have to modify your application to call their source code, but this is not a desirable scenario because for one you do not want to continually have to modify your application to accept new components.

One potential solution would be for you to create an interface that other developers who create extensions can create. This results in lower coupling, but it would still require the user to inform your application of these extensions by updating a configuration file. This can thus result in maintenance difficulties.

This would also make it difficult for the creators of extensions to your application to use their extensions in other applications, since to use your application they have to implement your specific interface.

MEF provides solutions to these problems, where it will automatically load classes or even other assemblies without you as the developer having to provide any configuration information--just put these components in a particular directory and they will be read automatically!

My plan for this tip is to first demonstrate Dependency Injection without using MEF, then show you the same application using MEF to help you gain a better understanding of what MEF is doing and how it saves you from writing code.

Creating the Application Without MEF

Dependency Injection is a specific form of Inversion of Control (IoC).  Dependency Injection is a software pattern where the control of a particular function is determined not by the algorithm that is inside the function, but instead by an object that is passed in.  It is useful, for example, when you would want the software to behave in one way while in production mode and a different way while being tested. 

Consider this simple application that performs an operation twice using a Singleton, which uses the Lazy class to defer the creation of a resource-intensive object until the last possible moment, thus saving resources if it turns out that the creation of that object did not actually end up being required:

C#
using System;

namespace DependencyInjectionSample
{
    class Program
    {
        static void Main(string[] args)
        {
            IDoubleOperation doubleOperation = DoubleOperation.Instance;
            Console.WriteLine("Result of double operation = " + 
			doubleOperation.PerformDoubleOperation(3, 5));
            Console.WriteLine("Press any key to exit.");
            Console.ReadLine();
        }
    }

    public interface IOperation
    {
        int PerformOperation(int a, int b);
    }

    public interface IDoubleOperation
    {
        int PerformDoubleOperation(int a, int b);
    }

    public class Addition : IOperation
    {
        private static Lazy<Addition> instance =
            new Lazy<Addition>(() => new Addition());

        public static Addition Instance
        {
            get { return instance.Value; }
        }

        public int PerformOperation(int a, int b)
        {
            return a + b;
        }
    }

    public class DoubleOperation : IDoubleOperation
    {
        private static Lazy<DoubleOperation> instance =
            new Lazy<DoubleOperation>(() => new DoubleOperation(Addition.Instance));

        public static DoubleOperation Instance
        {
            get { return instance.Value; }
        }

        private IOperation operation;

        public DoubleOperation(IOperation operation)
        {
            this.operation = operation;
        }

        public int PerformDoubleOperation(int a, int b)
        {
            return operation.PerformOperation(
                operation.PerformOperation(a, b),
                operation.PerformOperation(a, b));
        }
    }
}

Creating the Application with MEF

The main components of MEF are a catalog and a composition container. The catalog helps you to discover extensions and the container is used to coordinate creation and satisfy dependencies.

With MEF, you can use either import or export attributes, where an import attribute refers to data that will be brought into the application, and an export attribute refers to data that is output from the application.  Using attributes is a type of declarative code, which in some ways is better than traditional procedural code, because declarative code focuses more on what you want the code to do rather than how it is done.  

If your application has an import attribute, then it must be matched with an export attribute. The attributes are filled by the composition engine.

Consider this version of PerformDoubleOperation, which uses MEF:

C#
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

namespace DependencyInjectionMefSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));

            // This is the composition engine
            var container = new CompositionContainer(catalog);
            container.ComposeParts();

            //IDoubleOperation doubleOperation = DoubleOperation.Instance;
            IDoubleOperation doubleOperation = container.GetExportedValue<IDoubleOperation>();
            Console.WriteLine("Result of double operation = " + 
				doubleOperation.PerformDoubleOperation(3, 5));
            Console.WriteLine("Press any key to exit.");
            Console.ReadLine();
        }
    }

    public interface IOperation
    {
        int PerformOperation(int a, int b);
    }

    public interface IDoubleOperation
    {
        int PerformDoubleOperation(int a, int b);
    }

    [Export(typeof(IOperation))]
    public class Addition : IOperation
    {
        public int PerformOperation(int a, int b)
        {
            return a + b;
        }
    }

    [Export(typeof(IDoubleOperation))]
    public class DoubleOperation : IDoubleOperation
    {
        private IOperation operation;

        [ImportingConstructor]
        public DoubleOperation(IOperation operation)
        {
            this.operation = operation;
        }

        public int PerformDoubleOperation(int a, int b)
        {
            return operation.PerformOperation(
                operation.PerformOperation(a, b),
                operation.PerformOperation(a, b));
        }
    }
}

Summary

This tip/trick is meant to be a simple demonstration of how an application can be built without MEF, and then a comparison of how the same is performed using MEF features. As I learn more about MEF, I intend on adding more details to this tip. If there is anything in particular about MEF that you are interested in, please let me know and I will try to add more details and example on that aspect.

References

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0


Written By
Software Developer (Senior)
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Question[My vote of 2] Oh dear. Pin
Pete O'Hanlon13-Feb-16 8:02
mvePete O'Hanlon13-Feb-16 8:02 
AnswerRe: [My vote of 2] Oh dear. Pin
Roger C Moore16-Feb-16 11:20
Roger C Moore16-Feb-16 11:20 
GeneralRe: [My vote of 2] Oh dear. Pin
Pete O'Hanlon16-Feb-16 22:12
mvePete O'Hanlon16-Feb-16 22:12 
A hint from an author - use the article writing process as your opportunity to explore the topics in depth. That way, you can anticipate the questions that people will have about your article and seek to answer them as you go along. Yes, it means that it takes you longer to write the article, but it also means that you have a much more in depth understanding of what's going on because you put yourself through the mental discipline of really getting to understand what's going on.

As an example - a while back, I wrote an article that included a Visual Studio extension - part of that was binding multiple commands to different windows. Now, this isn't well documented and it takes a lot of pulling bits and pieces together but it was something that I knew people would want to know how to do so I delayed writing the article until I had that fully nailed down and could explain the process in depth. That took me about three weeks but it made for a much stronger article. Don't sell yourself short - if you release a quick entry like this, you're going to get lower votes than if you take the time and release something really in depth. As an author, you'll get a better reputation for 1 good article than for 10 superficial ones.
This space for rent

GeneralRe: [My vote of 2] Oh dear. Pin
Roger C Moore17-Feb-16 7:01
Roger C Moore17-Feb-16 7:01 
GeneralRe: [My vote of 2] Oh dear. Pin
Pete O'Hanlon17-Feb-16 22:50
mvePete O'Hanlon17-Feb-16 22:50 
GeneralRe: [My vote of 2] Oh dear. Pin
Roger C Moore18-Feb-16 11:26
Roger C Moore18-Feb-16 11:26 
GeneralRe: [My vote of 2] Oh dear. Pin
Pete O'Hanlon18-Feb-16 22:29
mvePete O'Hanlon18-Feb-16 22:29 
AnswerNow my vote of 5 Pin
Pete O'Hanlon18-Feb-16 22:30
mvePete O'Hanlon18-Feb-16 22:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.