C# Plugins & Modules

Plugins and modules are widely used in almost any type of application. They enhance them with additional features. Beside adding features, plugins have the big advantage, that they separate code in a very clean and testable way. Even hundreds of plugins can be managed without problems, as their functionality is encapsuled in a single place.

This tutorial will explain you, how to implement a C# plugin- and module application-architecure, which is quite simple, clean and easy to maintain!

Differences between plugins and modules

Plugins add small functionalities to a software. An example could be a plugin which adds business-logic to a CRM- or an ERP-system. The registration is very lightweight normally.

Modules have more functionality. Possibly adding a whole new part to a system like i.e. a logistics-module. The registration comes often with some meta-data about the module and brings resources such as icons, menupoints etc.

Application Plugin Architecture

The following diagram shows, how a plugin/module based architecture looks like. It is made up of our application with the pluginmanager, the plugins and the SDK. The pluginmanager searches for, loads, registers and executes available plugins.

Application Plugin Architecture

Notice that the application and the plugins share the SDK, which provides everything the plugin needs to know (i.E. interfaces and parameter-classes). It is normally implemented in a separate library and referenced in each plugin-project.

Implementing a plugin in C#

To cover plugins, we’ll build a simple console-application, which loads plugins and let us choose, which of them we want to execute. For the sake of simplicity we’ll omit an explicit pluginmanager. You’ll see for sure which parts would go into it.

To use plugins in an application, it must provide parts that loads and registers plugins and another one, which executes them. To identify a plugin-class in the load-process amongst other classes, the plugin-classes must implement an interface. So the logic of the loader is "Load all classes as plugin, which implement interface X". See the following example of a plugin:

public interface IPlugin
{
	string PluginName { get; }
	void Execute(MyParameter myParameter);
}

public class ExamplePlugin : IPlugin
{
	public string PluginName { get { return "Example Plugin"; } }

	public void Execute(MyParameter myParameter)
	{
		// Whatever needs to be done
	}
}

As you can see, the interface provides a PluginName-property, which we will use to register the plugin and show as selection in our demo-application.

Loading plugins

Normally we want to load all available plugins in the startup of the application. We can do this by using the reflection-functionalities c# provides us. The following method, which is kept very generic, can be used to load all types (classes), which implement a certain interface.

public static List<Type> GetTypesByInterface<T>(Assembly assembly)
{
	if (!typeof(T).IsInterface)
		throw new ArgumentException("T must be an interface");

	return assembly.GetTypes()
		.Where(x => x.GetInterface(typeof(T).Name) != null)
		.ToList<Type>();
}

We can call the method like this, when the application starts:

Assembly consoleAssembly = Assembly.GetExecutingAssembly();
List<Type> pluginTypes = GetTypesByInterface<IPlugin>(consoleAssembly);

In our example we load the plugins from the current assembly (the exe). You could also load plugin-assemblies dynamically on startup with the method Assembly.LoadFrom("yourassembly.dll"). It returns an assembly as well, which you can pass to the GetTypesByInterface-method.

Registering the plugins

As we cannot execute types directly or access any properties of them, we need to create instances of the plugin-types and register/store them for later use. C# provides the very cool Activator.CreateInstance-method, which allows us exactly that! The following code shows how it works:

List<IPlugin> plugins = new List<IPlugin>();
foreach(Type pluginType in pluginTypes)
{
	IPlugin plugin = Activator.CreateInstance(pluginType) as IPlugin;
	plugins.Add(plugin);
}

Now we can show the user a list of all plugins and ask, which she’d like to execute:

Console.WriteLine("Available Plugins:");
plugins.ForEach(t=> Console.WriteLine(t.PluginName));

Console.WriteLine("Enter plugin-name to execute:");
string pluginToExecute = Console.ReadLine();

Executing a specific plugin

The last and probably most easy part is to execute a specific plugin. In fact we have already everything in place for it. Let’s create a method, which executes a plugin by plugin-name:

public static void ExecutePlugin(List<IPlugin> plugins, string pluginName)
{
	var plugin = plugins.Where(t => t.PluginName == pluginName).First();
	plugin.Execute(new MyParameter());
}

Now the only thing left, is to call the method with the plugin the user entered.

ExecutePlugin(plugins, pluginToExecute);

That’s it. We can use the knowledge from above for most situations, where we need simple plugins. One disadvantage of this method is, that we need to keep every instance of a plugin in the memory, which becomes a problem if you either have very large memory-consuming plugins or thousands of them. In the next part we’ll see how we can overcome this, when we talk about modules, as their memory footprint is generally not small.

Implementing modules in C#

The main difference between a plugin and a module is, that the module provides additional information and resources. And with the implementation we cover here, we need to create an instance only if the module is executed. Beside of this, the module is very similar to the plugin above, as it must have an interface as well for the execution.

Defining module metainformation

So how can we add additional information to our module? The C# language allows us to decorate classes with attributes. These attributes can be read without instanciating the class. The following example shows a class decorated by the ModuleAttribute, which has the property ModuleName defined. This is a very simple example, but you could of course add many more properties in your attribute like moduletype, resources etc.

As you can see, the module will implement an interface (IModule) as well, which is used for the execution. It has the same signature as the plugin from above, so we’ll not cover this again!

[ModuleAttribute(ModuleName="My Module")]
public class MyModule : IModule
{
}

The definition of the attribute above is simple – it is a class that inherits from Attribute and defines the property ModuleName.

public class ModuleAttribute : Attribute
{
	public string ModuleName { get; set; }
}

Loading and registering the modules

To be able to use and present the modulename to the user, we need to store two things: the type of the module and the attribute. Why both? Because the attribute does not contain the type of the class which it decorated. We use the following class therefore, which is implemented in a generic way:

public class AttributeImplementation
{
	public Attribute Attribute { get; set; }
	public Type ImplementationType { get; set; }

	public AttributeImplementation(Attribute attribute, Type implementationType)
	{
		this.Attribute = attribute;
		this.ImplementationType = implementationType;
	}
}

The next step is to gather all modules from our our assembly. The following methods use reflection to get all the classes, which are decorated by a certain attribute. They are then stored and returned in a List.

public static List<AttributeImplementation> LoadAllClassesImplementingSpecificAttribute<T>(Assembly assembly)
{
	IEnumerable<Type> typesImplementingAttribute = GetTypesWithSpecificAttribute<T>(assembly);

	List<AttributeImplementation> attributeList = new List<AttributeImplementation>();
	foreach (Type type in typesImplementingAttribute)
	{
		var attribute = type.GetCustomAttribute(typeof(T));
		attributeList.Add(new AttributeImplementation(attribute, type));
	}

	return attributeList;
}

private static IEnumerable<Type> GetTypesWithSpecificAttribute<T>(Assembly assembly)
{
	foreach (Type type in assembly.GetTypes())
	{
		if (type.GetCustomAttributes(typeof(T), true).Length > 0)
		{
                    yield return type;
		}
	}
}

Now we just need to call the following method in our main-method to register all available modules:

Assembly consoleAssembly = Assembly.GetExecutingAssembly();
List<AttributeImplementation> modules = 
	LoadAllClassesImplementingSpecificAttribute<ModuleAttribute>(consoleAssembly);

Presenting the module

For this demo we’ll just list the modules in the console. In a real application you’d probably list the modules in a menu. As you can see in the following code, we do not need to create an instance of the module for this. Instead we just show the ModuleName which is stored in the attribute.

Console.WriteLine("Available Modules:");
modules.ForEach(t => Console.WriteLine(((ModuleAttribute)t.Attribute).ModuleName));

Executing a specific module

To execute a specific module, we need to instantiate a module and execute via the IModule-interface. The following method does this for us:

public static void ExecuteModule(List<AttributeImplementation> modules, string moduleName)
{
	AttributeImplementation moduleMetadata = modules.Where(t => 
		((ModuleAttribute)t.Attribute).ModuleName == moduleName).First();
            
	IModule module = Activator.CreateInstance(moduleMetadata.ImplementationType) as IModule;
	module.Execute(new MyParameter());
}

Now we can ask the user, which module she’d like to run and execute the module-execution method above accordingly:

Console.WriteLine("Enter module-name to execute:");
string moduleToExecute = Console.ReadLine();

ExecuteModule(modules, moduleToExecute);

Summary

You should now have everything you need to implement an application with a plugin- or modulebased architecture. You’d of course do some more sophisticated things and add some nice error handling as well. And you would of course add the plugins- or modules to external assemblies and load them dynamically.