This is Part 1 of a series of articles about a Clifton Method Core Component in which you will learn how to dynamically load assemblies at runtime.
Series of Articles
Building applications with runtime loaded assemblies is a useful capability, as it provides the ability to:
- customize an application from "logical components."
- replace components with new or different behavior, for example, mock objects.
- extend an application's behavior by adding new components.
By "logical components", I mean an assembly (DLL) that encapsulates all the functionality of "something" into a group. An easy way to first understand this concept is by thinking about physical devices, for example, a camera. All the functionality that a camera might provide, such as taking a picture, streaming video, configuring its resolution, and other settings, can be organized into a logical component. Because different cameras will most likely have different APIs and options, one can create different DLLs for each physical device and customize the application for the actual camera (or cameras) that are part of a particular installation.
Creating services as logical components is another useful thing to do. For example, a web server can be a component, encapsulating handling HTTP requests. The router might be a different component. If you're implementing a server that provides public services, you probably don't need a router that performs authentication. If you're implementing a server that serves non-public pages or REST calls, you would want a router component that performs authentication. Another good example is interfacing to different databases, such as SQL Server, Oracle, or a NoSQL database.
When is a set of classes suitable for wrapping in a module as a component, and when are they not? Ideally, you should already be asking yourself this question when writing a non-modular application because even the internals of such an application should be organized in such a way that the code is structured "logically."
What is Suitable For Modularization?
Ask these questions:
Does It Behave Like a Component?
- Is the set of classes intended to be re-usable?
- Would it be useful to mock the functionality provided by these classes?
- Am I coding against a specific physical (hardware) object or against a specific behavior?
- Can I foresee the physical hardware changing or the behavior changing depending on application/customer-specific needs?
- Are the classes implementing high level behaviors?
- Are the classes implementing business rules that might vary depending on application-specific needs?
In the last question, what do we mean by "high level behaviors?"
Is It High Level?
High level behavior typically:
- Interfaces with other high level components in the system.
- Communicates to other applications (such as a database).
- Handles asynchronous events, such as HTTP, TCP/IP, serial, USB, or other transports.
- Interfaces with physical hardware.
- Is implemented as a prototype (is instantiated) but acts as a singleton for the specific functionality that it provides.
- Implements a common interface for all logical components of the same "kind" (hence #5, instantiated.)
- #6 means that the behavior is abstracted through an interface.
Is it Low Level?
Low level behaviors typically fall into the "utility" category, classes that implement:
- Extension methods
- Conversion methods
- Can be, and often is, implemented as static methods
- Does not typically implement interfaces to abstract the behavior
You essentially have all "yes" answers for "Does it behave like a component?" and "Is it high level?", and all "no" answers for "Is it low level?"
Modular Component vs. Inheritable
The typical approach to building "components" is to use inheritance and a factory pattern.
A Typical Inheritance Architecture
Here, the code, in a monolithic manner, implements the each of the concrete server implementations and asks the factory method to create the desired concrete object.
A Typical Modular Component Architecture
In this scenario, the application gets an instance of whatever is specified in a separate configuration file as to the implementing concrete type. The concrete types are implemented as separate assemblies.
What do you notice about this?
- The module loader is similar to a factory pattern, but you don't ask it to get you anything, instead the application, through a configuration file, determines what is loaded. The "get me something" factory pattern is replaced a "you get this", more like a strategy pattern.
- The concrete classes that are implemented in the module typically don't inherit from a base class but rather implement an interface.
- Inheritance is replaced with modularity.
That last point is worth repeating: Inheritance is replaced with modularity. Instead of creating a potentially complex inheritance tree of specializations, each specialization lives autonomously in its own module, and the inheritance is usually very shallow, in fact usually just implementing the interface requirements.
The Annoying Thing about Modular Architectures
In order to utilize a modular architecture, the interface classes must be shared, either by referencing the same files, typically in some common folder, or wrapping the shared files in an assembly shared both by your application and the module. The salient point is that the files, or assembly of the files, must be shared. Why? Because:
- The application needs to know how to talk to the module, which is through the interface
- The module needs to know what it implements, again through the interface.
A monolithic application doesn't have this issue because everything is in the same application solution:
Once you start writing your code so that you have different projects (assemblies) for your components, you must share the interface source code or abstract class code in a shared assembly:
This is also true when you start writing components as runtime loaded modules:
Here again, we see an inversion of more monolithic application development. Rather than:
- everything living in one project (highly monolithic)
- many projects with each project referencing other projects that it needs to know about (compile-time modularity)
we instead have:
- an application that shares an interface specification and separate modules (assemblies) which are not referenced by the application nor which reference each other, except through the shared interface.
Certainly, the project files for the different modules may live together in the same solution (and even in multiple solutions, as these modules are often shared), but the salient point is, neither the application, nor the modules, directly reference other modules.
After that lengthy introduction, we can look at the implementation details.
One thing to note is that in this code, I was dabbling, somewhat inconsistently, with semantic types, as I wrote about in Strong Type Checking with Semantic Typess. So you'll see a couple types like
AssemblyFileName that are type wrappers for strings. I'm not sure whether there's benefit to this or not -- it's probably unnecessary complexity here, though I do like the idea of specifying what type of string is expected in the parameter, based on type rather than variable name.
I also rely on some Linq extension methods and assertion methods that are in the repo, but are not discussed here.
I specify modules in an XML file. You could just as easily put this in the application config file, a database table, a JSON document, or something else. The XML format that I use looks like this:
<Module AssemblyName='[some module name].dll'/>
<Module AssemblyName='[another module name].dll'/>
I load the XML file into the
List<AssemblyFileName> that the module registration requires (see below) with two helper methods in your application:
static private List<AssemblyFileName> GetModuleList(XmlFileName filename)
"Module definition file " + filename.Value + " does not exist.");
XDocument xdoc = XDocument.Load(filename.Value);
static private List<AssemblyFileName> GetModuleList(XDocument xdoc)
List<AssemblyFileName> assemblies = new List<AssemblyFileName>();
(from module in xdoc.Element("Modules").Elements("Module")
(s => assemblies.Add(AssemblyFileName.Create(s)));
Using the above code (for an XML file), I register modules with:
IModuleManager moduleMgr = serviceManager.Get<IModuleManager>();
List<AssemblyFileName> modules = GetModuleList(XmlFileName.Create("modules.xml"));
Module registration is performed here:
public virtual void RegisterModules(
OptionalPath optionalPath = null,
Func<string, Assembly> assemblyResolver = null)
List<Assembly> modules = LoadModules(moduleFilenames, optionalPath, assemblyResolver);
List<IModule> registrants = InstantiateRegistrants(modules);
The idea here is to allow the application to specify a sub-folder in which modules (DLLs) are located. This helps to create a cleaner separation of module assemblies from other, "statically linked" dependencies.
This is an interesting optional parameter. It is a function that takes the module name and returns the
Assembly. The idea here is that the assembly may be located in a strange place, such as a resource file of the application. I haven't written about this technique, but when I do, I'll come back here and provide a link to that concept. In general though, you can use this optional function to attempt to resolve an assembly located somewhere other than a sub-folder of the application.
Loading the Modules
LoadModules iterates through the list of modules:
protected virtual List<Assembly> LoadModules(List<AssemblyFileName> moduleFilenames,
OptionalPath optionalPath, Func<string, Assembly> assemblyResolver)
List<Assembly> modules = new List<Assembly>();
Assembly assembly = LoadAssembly(a, optionalPath, assemblyResolver);
This returns a list of
LoadAssembly attempts to actually load the assembly, optionally passing the "I need this assembly" over to the assembly resolver function that you provided in the registration call.
protected virtual Assembly LoadAssembly(
Func<string, Assembly> assemblyResolver)
FullPath fullPath = GetFullPath(assyName, optionalPath);
Assembly assembly = null;
Assert.Not(assemblyResolver == null, "AssemblyResolver must be defined
when attempting to load modules from the application's resources.");
assembly = assemblyResolver(assyName.Value);
assembly = Assembly.LoadFile(fullPath.Value);
catch (Exception ex)
throw new ModuleManagerException("Unable to load module " +
assyName.Value + ": " + ex.Message);
The optional path is appended to the executing assembly location in the
protected virtual FullPath GetFullPath
(AssemblyFileName assemblyName, OptionalPath optionalPath)
string assyLocation = Assembly.GetExecutingAssembly().Location;
if (assyLocation == "")
Assert.Not(optionalFolder == null, "Assemblies embedded as resources require that
the optionalPath parameter specify the path to resolve assemblies.");
appLocation = optionalPath.Value;
appLocation = Path.GetDirectoryName(assyLocation);
appLocation = Path.GetDirectoryName(assyLocation);
if (optionalPath != null)
appLocation = Path.Combine(appLocation, optionalPath.Value);
string fullPath = Path.Combine(appLocation, assemblyName.Value);
Unfortunately, in the above code, there is a dual use of the optional path. There is a nuance of loading assemblies when the Module Manager is itself an embedded resources assembly -- the executing path in this case is an empty string, because the assembly, loaded by .NET assembly through a separately implemented assembly resolver (not discussed here), isn't associated with the executing assembly! This is very odd behavior and this whole situation should be ignored until I write the article on embedding assemblies as resources. In this case, the optional path is the full path to resolves the assembly location. Quite honestly, this whole issue requires a refactoring of how assemblies embedded as resources should be handled.
Once the assemblies have been loaded, the Module Manager instantiates the registrants. A "registrant" is a special class (one and only one such class) in each module that has implements the method in the interface
IModule. In other words, our modules "know" that they are modules and can do some special things because they are modules. What those special things are is essentially up to you. In my library, the module is initialized with a Service Manager so that the module can register the services that it provides. That is described in the next article in this series. For now, we just need to know that the each module must provide a class that implements
protected virtual List<IModule> InstantiateRegistrants(List<Assembly> modules)
registrants = new List<IModule>();
IModule registrant = InstantiateRegistrant(m);
protected virtual IModule InstantiateRegistrant(Assembly module)
var classesImplementingInterface = module.GetTypes().
Where(t => t.IsClass).
Where(c => c.GetInterfaces().Where(i => i.Name == "IModule").Count() > 0);
Assert.That(classesImplementingInterface.Count() <= 1,
"Module can only have one class that implements IModule");
Assert.That(classesImplementingInterface.Count() != 0,
"Module does not have any classes that implement IModule");
Type implementor = classesImplementingInterface.Single();
IModule instance = Activator.CreateInstance(implementor) as IModule;
Each registrant in the module, once instantiated, can be initialized. The initialization method is a virtual stub in the
protected virtual void InitializeRegistrants(List<IModule> registrants)
If your modules need initialization, you would derive from
ModuleManager and implement the specific initialization you require.
Our demo solution consists of these four projects:
CommonInterface - This holds the
ModuleConsoleSpeak - Emits a message to the console
ModuleManager - A console demo of the module manager
ModuleVoiceSpeak - Speaks the message using .NET's voice synthesizer
The example program implements two "speakers", one that emits a console message, the other that speaks the message with .NET's speech synthesizer. The application is quite simple:
static void Main(string args)
IModuleManager mgr = new ModuleManager();
List<AssemblyFileName> moduleNames = GetModuleList(XmlFileName.Create("modules.xml"));
IModule module = mgr.Modules;
Note how the application is loading the modules from the "dll" sub-folder:
The "dll" folder contains the assemblies for the two modules:
Also note that a post-build step copies the module into the "dll" folder:
copy ModuleConsoleSpeak.dll ..\..\..\ModuleManager\bin\Debug\dll
copy ModuleVoiceSpeak.dll ..\..\..\ModuleManager\bin\Debug\dll
Now, by changing the modules.xml file, the application responds by emitting the text to the console window or by speaking it. To speak the text, use this:
To emit the text on the console window, use this:
The Application does not Reference the Modules
Note that in the demo application (called "
ModuleManager", a bad name!), there is no reference to the modules we're loading:
This implementation is for demonstration purposes only!
public interface IModule
void Say(string text);
Here, we see the problem I talked about earlier with runtime modular applications -- the common interface must exist in a separate assembly! If you're not careful, this can and will cause and explosion of assemblies that simply define interfaces and other shared types. We'll talk about this further in other articles.
The actual implementation of
IModule is tied in with the Service Manager (the next article):
public interface IModule
void InitializeServices(IServiceManager serviceManager);
public class Speak : IModule
public void Say(string text)
public class Speak : IModule
public void Say(string text)
SpeechSynthesizer synth = new SpeechSynthesizer();
A Word About the IModule Interface
This a demonstration only! Placing module-specific requirements into
IModule is not recommended. Modules are intended to implement a wide variety of things, and you can't and shouldn't describe those implementations in the
IModule interface. I am doing so here only because it's simple to demonstrate just the Module Manager, knowing that the two modules I've implemented are very limited and have common behavior. In any real application, I would never do this, which is why the next article is about the Service Manager.
The demo illustrates the purpose of the Module Manager -- to be able to change the application's behavior:
- Without recompiling the application
- Through a configuration file
That's all I intended to do with this article, as the Module Manager is the core component of The Clifton Method.
A Word about Modules vs. Dependency Injection
Another way to go about this while issue is with dependency injection -- using reflection and type information, instantiate an object that implements a particular interface and assign it a property of that interface type. Personally, I find DI to be overly complicated (you might say the same of what I'm doing here!) and the DI frameworks that I've seen are bloated, slow, and it becomes difficult to debug the application.
Besides, I use the Module Manager in a specific way -- to load assemblies that implement services. The registration process is simple to understand, simple to walk through with a debugger, and the entire implementation is very few lines of code, which makes it easier to maintain.
At the time of this writing, the Module Manager loads all modules immediately -- there is no concept of deferred loading. This might be useful to implement in the future -- an "on demand" loading of modules, but there are certain complexities, in that the Service Manager (which is the next article) would need to know what module to load for a particular implementation. I've not really encountered a requirement for this kind of behavior, so there's really no reason to implement it, yet.
Similarly, the idea of being able to unload a module at runtime is attractive in that you could potentially replace a module without bringing down the application. However, as I've written about regarding Application Domains, the performance and potential problems with event wire-ups makes this feature somewhat undesirable.
- 25th August, 2016: Initial version