Skip to content
Bruno Sonnino
Menu
  • Home
  • About
Menu

Developing modular applications with MEF

Posted on 31 August 2013

Introduction

When we create LOB (Line-of-business) applications, we use to develop them in a modular way, to satisfy many requisites:

  • The application is sold by modules - the customer can buy the sales module, but not the industrial one.
  • The application can be developed by separated teams - each team develops an independent module.
  • The modules are only loaded when the user needs it.
  • The application doesn't need to be completely redeployed when a module changes
  • We can create new modules and add them to the application with no need to change the other modules.

To create a modular application we must create a custom infrastructure that needs lots of work and can introduce bugs to the system

.Net 4.0 brought a new resource that allows us to create modular and extensible applications: MEF (Managed Extensibility Framework). This is a framework to create extensible apps and it is thoroughly tested - Visual Studio uses MEF for its extension system: when you use a Visual Studio add-in, you are using MEF.

Introduction to MEF

Before developing out modular application, we will see how to use MEF. To use it, you must include a reference to System.ComponentModel.Composition. After adding the reference we must tell to the program which are the parts that must be combined: on one side, we have the exported classes, the modules that will be added to the main module, that will fit in the imported parts.

To do this association and the module discovery, combining the exported parts to the imported ones we use Containers. They will discover the modules and will combine the exported to the imported parts. Containers can use catalogs to find the application parts. Does this seem difficult? Let's see how does it work in the real world.

Create a new console project and add a reference to System.ComponentModel.Composition. Create a new class and call it Menu:

public class Menu
{
    private Module _module;
    public void OptionList()
    {
       Console.WriteLine(_module.Title);
    }
}

We need to create the Moduleclass:

public class Module
{
    public Module()
    {
        Title = "Customers";
    }
    public string Title { get; set; }
}

Now we need to declare what will be exported and where this exported class will fit, using the **[Import]**and [Export] attributes:

public class Menu
{
    [Import]
    private Module _module;

    public void OptionList()
    {
       Console.WriteLine(_module.Title);
    }
}

[Export]
public class Module
{
    public Module()
    {
        Title = "Customers";
    }
    public string Title { get; set; }
}

Note that the _module field in the Menu class is private - this doesn't affect the composition. Now, we need to create our container and let it do its magic. In Program.csput the following code:

static void Main(string[] args)
{
    var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
    var container = new CompositionContainer(catalog);
    var menu = new Menu();
    container.ComposeParts(menu);
    menu.OptionList();
    Console.ReadLine();
}

We have created an AssemblyCatalog (that searches the parts in the assembly - in our case, in the current assembly), then created the container that composes the parts after the menu is created. When the **menu.OptionList()**method is called, the module's title is listed:

Getting more than one component

Now, you will say: but we have only one module listed. How do we do to have more than one? We must create a new interface IModule:

public interface IModule
{
    string Title { get; set; }
}

Our class will implement this interface and the export will tell that we are exporting the IModuleinterface:

[Export(typeof(IModule))]
public class Module : IModule
{
    public Module()
    {
        Title = "Customers";
    }

    public string Title { get; set; }
}

In order to match the parts, we must say that the imported part is also of IModule type:

public class Menu
{
    [Import]
    private IModule _module;
    public void OptionList()
    {
       Console.WriteLine(_module.Title);
    }
}

We execute the application and see that everything works like before. We can add the new modules:

[Export(typeof(IModule))]
public class Customer : IModule
{
    public Customer()
    {
        Title = "Customers";
    }
    public string Title { get; set; }
}

[Export(typeof(IModule))]
public class Product : IModule
{
    public Product()
    {
        Title = "Products";
    }
    public string Title { get; set; }
}

[Export(typeof(IModule))]
public class Supplier : IModule
{
    public Supplier()
    {
        Title = "Suppliers";
    }
    public string Title { get; set; }
}

We execute the application and... we get a ChangeRejectedException exception: "More than one export was found that matches the constraint: ContractName IntroMEF.IModule". MEF is complaining that we have many classes that export IModule.

The [Import] attribute allows only one export for the same interface. If there is more than one, this exception is thrown. When we want that many classes export the same interface, we must use the [ImportMany] attribute. To allow importing all the classes, we need to change the import a little, changing the attribute to **[ImportMany]**and the property type of the _module field to IEnumerable:

public class Menu
{
    [ImportMany]
    private IEnumerable<IModule> _modules;

    public void OptionList()
    {
        foreach (var module in _modules)
        {
           Console.WriteLine(module.Title);
        }
    }
}

We also changed OptionListto list all the modules. Now, when we execute the application, all the modules are found and listed:

Working with modules in different assemblies

You may be thinking: "this is easy to do - everything is in the same assembly. Doesn't MEF allow modular and extensible apps?". All the magic is in the container and in the catalog. We have reated an AssemblyCatalog pointing to the current assembly, but we could do the same thing pointing to another assembly. You will answer: "this is still easy, I can add a reference to the other assembly where the modules are located. Where is the magic?".

AssemblyCatalogis not the only catalog that we can use. We can use other catalog types, like the DirectoryCatalog, that finds parts in assemblies located in a specified folder.

Let's change our project: in the solution, create a Portable Class Library and give it the name of **IntroMEF.Interfaces.**Choose the desired frameworks (use Framework 4.0 or higher) and delete the Class1.cs file. Add a new interface and put there the IModule interface, removing it from the main project. On the main project, add a reference to the interface project.

Create a new Class library project and give it the name of IntroMef.Modules. Add a reference to System.ComponentModel.Composition and remove the Class1.cs file. Add a reference to IntroMef.Interfaces and move the classes Customer, Product and Supplier to the new project.

Now we need to tell that our parts are not only in the current assembly, but they can also be found in the current folder. For that we must compose two catalogs: one for the current assembly (for the imported parts) and another for the folder (for the exported parts). We will use an AggregateCatalog to compose both catalogs:

static void Main(string[] args)
{
    var catalog = new AggregateCatalog(
        new AssemblyCatalog(Assembly.GetExecutingAssembly()),
        new DirectoryCatalog("."));
    var container = new CompositionContainer(catalog);
    var menu = new Menu();
    container.ComposeParts(menu);
    menu.OptionList();
    Console.ReadLine();
}

We execute our application and nothing happens - no module is listed. That is due to the fact that we haven't copied the modules assembly to the folder where the executable is located. Copy the assembly IntroMef.Modules to the executable folder, open a command window and execute the application again. Now the modules are recognized.

We didn't need to add any references to the main project in the module library nor add a reference to the library in the main project. All we had to do is to copy the file to the executable folder. MEF found the modules when the assembly file was in the correct folder.

Creating a WPF project with MEF

Now you may be thinking: "that's great, but I don't create any console projects - how do I do a modular project using WPF?". The answer is, in the same way we create a console application. To show that I am not lying, we will create a WPF project.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="120" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <ListBox Grid.Column="0" ItemsSource="{Binding Modules}" 
             DisplayMemberPath="Title" x:Name="LbxMenu"/>
    <ContentControl Grid.Column="1" Content="{Binding ElementName=LbxMenu, 
        Path=SelectedItem.Content}" />
</Grid>

We are adding a grid with two columns. In the first one we will add the found modules and in the second one, the contents for the selected module. To make it work, we must have two properties in every module, Title, with the module's title and Content, a UserControl with the contents of that module.

We need to create an interface for the modules. We will do in the same way we did before, creating a new library. Create a class library and name it WPFMef.Interfaces. Add the references to PresentationCoreand WindowsBase. Remove the Class1.cs file and add a new interface IModule:

public interface IModule
{
    string Title { get; }
    UIElement Content { get; } 
}

Then create a new class library for the modules. Add a new class library and name it WPFMef.Modules. Add the references to WPFMef.Interfaces, System.ComponentModel.Composition, System.Xaml, PresentationCore, PresentationFrameworkand WindowsBase. Create the exported classes:

[Export(typeof(IModule))]
public class Customer : IModule
{
    public string Title
    {
        get { return "Customers"; }
    }

    public UIElement Content
    {
        get { return new CustomerList(); }
    }
}

[Export(typeof(IModule))]
public class Product : IModule
{
    public string Title
    {
        get { return "Products"; }
    }

    public UIElement Content
    {
        get { return new ProductList(); }
    }
}

[Export(typeof(IModule))]
public class Supplier : IModule
{
    public string Title
    {
        get { return "Suppliers"; }
    }

    public UIElement Content
    {
        get { return new SupplierList(); }
    }
}

The next step is to create the views that will be shown when the option is selected. Create three UserControls with a content similar to this one:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="40" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <TextBlock Text="Customer List" HorizontalAlignment="Center" 
               VerticalAlignment="Center" FontSize="18"/>
    <ListBox x:Name="List" Grid.Row="1"/>
</Grid>

On the constructor of the view, add this code to add items to the ListBox:

public CustomerList()
{
    InitializeComponent();
    List.ItemsSource = Enumerable.Range(1, 100)
        .Select(n => "Customer " + n);
}

We have added a simple window that shows "Customer 1" to "Customer 100", just to show what can be done. Our module library is finished.

Go back to the main project, add the references to the library WPFMef.Interfaces and to System.ComponentModel.Composition. Create a new class and give it the name of MainViewModel. We will add there the code to initialize the container and the Modules property:

public class MainViewModel
{
    [ImportMany]
    public IEnumerable<IModule> Modulos { get; set; }

    public MainViewModel()
    {
        var catalog = new AggregateCatalog(
            new AssemblyCatalog(Assembly.GetExecutingAssembly()),
            new DirectoryCatalog("."));
        var container = new CompositionContainer(catalog);
        container.ComposeParts(this);
    }
}

We only need to associate the ViewModel to the View. In MainWindow.xaml.cs, add the following code:

public MainWindow()
{
    InitializeComponent();
    DataContext = new MainViewModel();
}

The project is ready and can be executed. When we execute it, we don't see anything in the window. We forgot to copy the assembly with the modules again. Just copy the assembly to the executable folder and run the program again. Yesssss!

The data is shown when we select an option the content view at the right is changed.

If you don't want to copy the project manually, you can change the project options, changing the output path for the modules, pointing to the executable path:

Conclusions

As you can see, MEF allows creating modular applications in an easy way that allows expanding our projects just by copying the dlls with the new modules. That works the same way in Windows Forms, WPF, Silverlight, Asp.Net and even Windows 8 (using NuGet). You have a standard way to create plug-ins that are not coupled, allow development done by many teams and adding or fixing modules with no need to recompile the whole project.

The full source code for this article is available in https://github.com/bsonnino/IntroMefEn

1 thought on “Developing modular applications with MEF”

  1. Eugene Cheung says:
    4 January 2017 at 04:18

    Nice Job!

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

  • May 2025
  • December 2024
  • October 2024
  • August 2024
  • July 2024
  • June 2024
  • November 2023
  • October 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • November 2022
  • October 2022
  • September 2022
  • August 2022
  • June 2022
  • April 2022
  • March 2022
  • February 2022
  • January 2022
  • July 2021
  • June 2021
  • May 2021
  • April 2021
  • March 2021
  • February 2021
  • January 2021
  • December 2020
  • October 2020
  • September 2020
  • April 2020
  • March 2020
  • January 2020
  • November 2019
  • September 2019
  • August 2019
  • July 2019
  • June 2019
  • April 2019
  • March 2019
  • February 2019
  • January 2019
  • December 2018
  • November 2018
  • October 2018
  • September 2018
  • August 2018
  • July 2018
  • June 2018
  • May 2018
  • November 2017
  • October 2017
  • September 2017
  • August 2017
  • June 2017
  • May 2017
  • March 2017
  • February 2017
  • January 2017
  • December 2016
  • November 2016
  • October 2016
  • September 2016
  • August 2016
  • July 2016
  • June 2016
  • May 2016
  • April 2016
  • March 2016
  • February 2016
  • October 2015
  • August 2013
  • May 2013
  • February 2012
  • January 2012
  • April 2011
  • March 2011
  • December 2010
  • November 2009
  • June 2009
  • April 2009
  • March 2009
  • February 2009
  • January 2009
  • December 2008
  • November 2008
  • October 2008
  • July 2008
  • March 2008
  • February 2008
  • January 2008
  • December 2007
  • November 2007
  • October 2007
  • September 2007
  • August 2007
  • July 2007
  • Development
  • English
  • Português
  • Uncategorized
  • Windows

.NET AI Algorithms asp.NET Backup C# Debugging Delphi Dependency Injection Desktop Bridge Desktop icons Entity Framework JSON Linq Mef Minimal API MVVM NTFS Open Source OpenXML OzCode PowerShell Sensors Silverlight Source Code Generators sql server Surface Dial Testing Tools TypeScript UI Unit Testing UWP Visual Studio VS Code WCF WebView2 WinAppSDK Windows Windows 10 Windows Forms Windows Phone WPF XAML Zip

  • Entries RSS
  • Comments RSS
©2025 Bruno Sonnino | Design: Newspaperly WordPress Theme