First Look: Managed Extensibility Framework (MEF)

by Danielvg 14. January 2010 18:36

MEFis a new framework set to simplify the creation of extensible applications, it will ship with the .Net 4 Framework later this year, but there is a preview included in the beta2 version of .Net 4.
I will here try and cover some of the basics by creating a small pointless application, if you want more advanced information and more code examples then the standard documentation is very nice!

MEF pretty much works as an IoC Container, you put everything into a mixing bowl and the rest is just magic. With MEF, attributes are used to mark what properties are dependencies and what objects can be used for injection. These attributes are [Import] for dependencies and [Export] for injectable objects.

The first example is a basic logger function for a WPF app, the logger is injected by MEF, notice the use of Import and Export attributes:


using System.Collections.ObjectModel;
using System.ComponentModel.Composition;

namespace Demo.Mef
{
    public interface ILogger
    {
        ObservableCollection<string> Logs { get; }
        void Log(string message);
    }

    // We set the Logger class to be exported as ILogger
    [Export(typeof(ILogger))]
    public class Logger : ILogger
    {
        private ObservableCollection<string> logs = new ObservableCollection<string>();

        public void Log(string message)
        {
            Logs.Add(message);
        }

        public ObservableCollection<string> Logs
        {
            get { return logs; }
        }
    }

    public partial class MainWindow : Window
    {
         // Mark the Logger to be imported by MEF
         [Import]
         public ILogger Logger { get; set; }

      }
}

Having defined exports and imports the only thing left to do it create the container and mix everything. MEF uses a CompositionContainer as its mixing bowl, the container is able to take catalogs and look through them for known attributes.

using System.ComponentModel.Composition;

namespace Demo.Mef
{
    public partial class MainWindow : Window
    {
        // Mark the Logger to be imported by MEF
        [Import]
        public ILogger Logger { get; set; }

        public MainWindow()
        { 
            InitializeComponent(); 
            Compose(); 
        }

        private void Compose()
        {
            // AssemblyCatalog takes an assembly and  looks for all Imports and Exports within it
            var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            // Create the container with the above catalog 
            var container = new CompositionContainer(catalog); 
            // Here we tell the container to mix everything up
            container.ComposeParts(this);
        } 
    }
}

And that is about it, when you run the above code MEF will look through the running assembly for import and export attributes and try to make everything match.
If you want to make sure that all Imports are filled before doing anything else then the interface IPartImportsSatisfiedNotification will help you. If your object implements the interface the method OnImportsSatisfied() will be called.

 

Nothing fancy there, most of the above could be done with Unity, so on to the next example that builds on top of the previous example.

In the following example MEF looks for buttons with the Export contract “Demo.Mef.MenuItems” in all assemblies found in the PlugIns directory.  Then when the form has loaded and MEF is done, all the buttons found are added to a stackpanel.

The following is located in a separate assembly that builds to /PlugIns/, it is simply a button with some behavior and most importantly two attributes.

namespace Demo.Mef.Extra
{
    // Mark the class as export with the contract Demo.Mef.MenuItems
    // so it can be matched with the contract in the main app(),
    // this could be used as a filter
    [Export("Demo.Mef.MenuItems", typeof(Button))]
    // This attribute makes MEF create a new instance each time the button is requested
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class MessageBoxButton : Button
    {
        public MessageBoxButton()
        {
            this.Content = "PresssMeee";
            this.Click += new System.Windows.RoutedEventHandler(MessageBoxButton_Click);
        }

        void MessageBoxButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            MessageBox.Show("Yes this demo also has Hello world!");
        }
    }
}

Since we want more then just one button in the menu we use the Import attribute ImportMany which fills the property with all the Export buttons MEF was able to find with the specified contract

[ImportMany("Demo.Mef.MenuItems")]
public IEnumerable<Button> MenuItems { get; set; }

To make MEF look in the PlugIns directory a few changes has to be made to the Compose method in the main application:


private void Compose()
{
    // AssemblyCatalog takes an assembly and  looks for Imports/Exports within it
    var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
    // DirectoryCatalog takes all assemblies within a given dir and looks for Imports/Exports
    var directoryCatalog = new DirectoryCatalog("PlugIns");

    // AggregateCatalog holds multiple  ComposablePartCatalogs
    var aggregator = new AggregateCatalog();
    aggregator.Catalogs.Add(assemblyCatalog);
    aggregator.Catalogs.Add(directoryCatalog);

    var container = new CompositionContainer(aggregator);
    container.ComposeParts(this);
}

Only thing left to do now is add the buttons to the stackpanel, as said we can use the IPartImportsSatisfiedNotification interface to make sure MEF is done.

public void OnImportsSatisfied()
{
    foreach (var menuItem in MenuItems)
    {
        stackPanelMenu.Children.Add(menuItem);
    }
}

That is about it, should I want to add a new button to the menu all it takes is a button with the export attribute, no need to recompile the main application just drop the dll in the PlugIns directory and the button is added next time the application is started! While all of this has been possible before it is very nice to get an easy integrated way of creating extensible applications.

The result of the above code:

 image

Source code can be found here Demo.Mef.zip (39,03 kb)

 

On a side note, I am somewhat confused about MEF vs Unity vs PRISM and will try to make a post about it soon.

Tags: , , ,

Code

Tuples in .Net 4 (System.Tuple)

by Danielvg 2. January 2010 15:16

.Net 4 brings with it the Tuple type! I have never worked with tuples in programming before and have a hard time seeing their purpose but here is an example of a tuple in C#:


var tupleOne = new Tuple<int, string>(1, "Hello World");
Console.WriteLine("Tuple contains: " + tupleOne.Item1 + " and " + tupleOne.Item2);

There are also some factory methods for creating tuples:


var tupleTwo = Tuple.Create("Hello World", 2010);
var tupleThree = Tuple.Create("Hello World", 2010, new SomeClass());
Console.WriteLine("Tuple contains: " + tupleThree.Item1 + ", " + tupleThree.Item2 + " and " + tupleThree.Item3.MyProperty);

Tuple facts:

  • Tuples are immutable
  • Tuples are reference types
  • Located in System.Tuple
  • Can take up to 8 generic parameters, but can contain an infinite number of elements.
  • Elements are always named Item1, Item2… Item7, Rest

See msdn for more information.

Tags: , ,

Code

Parallel programming - made too easy in .Net 4?

by Danielvg 25. November 2009 00:01

I am doing a test project at DTU under the Edison Project, where I basically have to make a program that is able to simulate the power consumption of multiple electric vehicles with different driving patterns, batteries and user profiles. After having made a prototype that ran on one thread I started to talk with a colleague at work on improving performance and getting it to run on multiple cores, he quickly suggested the new Parallel library which ships with .Net 4 Beta 2. I was really surprised at how easy it was to implement, all it took was 1 line of code to make the prototype utilize all cores in the system!

Going from:

private void CalculateTick(IEnumerable<ICar> cars)
{
    tickNumber++;
    tickDate = tickDate.AddSeconds(1); 
    foreach(var car in cars)
    {
        car.CalculateTick(tickDate);
    }
}

to:
private void CalculateTick(IEnumerable<ICar> cars)
{
    tickNumber++;
    tickDate = tickDate.AddSeconds(1);
    Parallel.ForEach(cars, (car) =>
        {
            car.CalculateTick(tickDate);
        });
}

did the trick of making the program run 3 times faster (using a test system with 4 cores)

I have a feeling that it is just too easy, you do not really get a chance to think about the effects this could have on the elements within the loops. But with that said.. I Love it!, and I will definitely take advantage of multiple cores more often!

If you want to read more about Parallel Extensions, which even includes parallel linq, a good place to start is here