DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Newsletter
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Design Automation in Closure Engineering: Building Parametric Assemblies With CATIA and VB Scripting
  • The Hidden World of Exit Codes: Tiny Integers With Big Meanings
  • CockroachDB TIL: Volume 11
  • How a DevOps Assembly Line Can Speed up Pipeline Movement

Trending

  • Running a Self-Hosted WAF in Production: Practical Lessons From SafeLine
  • The SDLC After AI
  • Adding Graph Retrieval to a RAG Pipeline With Python
  • Build a Voice Assistant With Gemini Live API

Draft This article hasn't been published, so readers can't see it.

In moderation This article is waiting for review, so readers can't see it yet.

Deleted This article has been deleted, so readers can't see it.

Importing multiple extension assemblies in C# with MEF

By 
Denzel D.
·
May. 22, 23 · Interview
Post on X (opens in a new tab) Share on LinkedIn (opens in a new tab)
24.8K Views

Join the DZone community Save articles, join the discussion, and download Refcards and Trend Reports with a free account.

Join for free Already a member? Log in

An application rarely depends on one single extension. If it does, then it has something wrong with its extension model and maybe it doesn’t need one after all. An extensible application allows the use of a multiple extensions that satisfy the standard exposed by the SDK:


In my previous article I showed how to use MEF to import a single external assembly. However, MEF doesn’t impose a limit on the number of assemblies that can be imported, although some conventions should be followed.

The extension standard is defined by a single class library with the following contents:

namespace PluginDevKit
{
    public interface IWriter
    {
        void DisplayMessage(string message);
    }
}

I will create two new class libraries that will represent two different extensions for the base application. For the first extension, the implementation will be:

using PluginDevKit;
using System.ComponentModel.Composition;
using System.Diagnostics;

namespace FirstPlugin
{
    [Export(typeof(IWriter))]
    public class FirstPlugin : IWriter
    {
        public void DisplayMessage(string message)
        {
            Debug.Print("First Plugin: " + message);
        }
    }
}

Here, I am implementing the IWriter interface and exporting the main class that will later on be used by the main application.

The second extension is implemented the same way, with minor naming changes and it displays a different message:

using PluginDevKit;
using System.ComponentModel.Composition;
using System.Diagnostics;

namespace SecondPlugin
{
    [Export(typeof(IWriter))]
    public class SecondPlugin : IWriter
    {
        public void DisplayMessage(string message)
        {
            Debug.Print("Second Plugin: " + message);
        }
    }
}

Now let’s look at the application itself, that will consume the extensions above. It’s structure is a bit different compared to the one that imports only a single assembly.

First of all, the property that is used to import is no longer a single instance:

internal IEnumerable<IWriter> Writer { get; set; }

There are multiple instances of IWriter to be loaded, therefore those are stored in an instance of IEnumerable, that will later on allow to go through each one of them to access their functionality.

There is also an interesting aspect when it comes to setting the Import attribute. The standard Import attribute only allows one Export to be assigned to the property that satisfies the condition set by the contract. If there is more than one Export present that satisfies the same contract, a CompositionException will be thrown when the composition will be attempted. Prior to the current release, the Import attribute could’ve been used to set a collection that will contain multiple instances of satisfactory Exports. In the current release, this is not allowed.

Instead, the ImportMany attribute should be used.

[ImportMany(typeof(IWriter))]
internal IEnumerable<IWriter> Writer { get; set; }

This attribute automatically implies that there are multiple Exports that satisfy the defined Import.

When loading a single assembly, or when all extensions are registered in the same assembly, an instance of AssemblyCatalog can be used to parse the assembly. When there are multiple assemblies, this approach can no longer be used and an instance of AggregateCatalog should be used instead. It represents a collection of AssemblyCatalog instances (via the Catalogs property) that can be used to parse and compose multiple assemblies at once.

var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.LoadFrom(Application.StartupPath + @"\Plugin.dll")));
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.LoadFrom(Application.StartupPath + @"\Plugin2.dll")));

I copied both extensions in the application folder, so I can easily access them. It is a good practice to keep all extension components in a single location that is relative to the application.

The rest of the composition process is similar to the one when a single assembly is used:

var container = new CompositionContainer(catalog);

var batch = new CompositionBatch();
batch.AddPart(this);

container.Compose(batch);

foreach(IWriter w in Writer)
    w.DisplayMessage("DD");

Once compiled, in the Output window you can see that both extensions have been activated and the DisplayMessage method was successfully called.


Assembly (CLI)

Opinions expressed by DZone contributors are their own.

Related

  • Design Automation in Closure Engineering: Building Parametric Assemblies With CATIA and VB Scripting
  • The Hidden World of Exit Codes: Tiny Integers With Big Meanings
  • CockroachDB TIL: Volume 11
  • How a DevOps Assembly Line Can Speed up Pipeline Movement

Partner Resources

×

Comments

Loading comments

The comments didn't load

Please try again in a moment.

Likes

Loading

The next likes didn't load. Please try again.

The likes didn't load

Please try again in a moment.

See who liked this

Log in or join DZone for free to see who liked this article.

Log in Join for free
  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook