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
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report

Importing multiple extension assemblies in C# with MEF

Denzel D. user avatar by
Denzel D.
·
Jun. 21, 10 · Interview
Like (0)
Save
Tweet
Share
22.23K Views

Join the DZone community and get the full member experience.

Join For Free

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.

Popular on DZone

  • OpenVPN With Radius and Multi-Factor Authentication
  • Strategies for Kubernetes Cluster Administrators: Understanding Pod Scheduling
  • Tackling the Top 5 Kubernetes Debugging Challenges
  • Building a REST API With AWS Gateway and Python

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

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

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: