← Back to articles

Extension support for .Net Core application

Plugins for a .NET Core app boil down to loading .dlls at runtime, finding the interesting interfaces and registering them in IoC. Since IoC already gives you loose coupling, dynamic loading is a small step — shown here.

Tedd

There are various reasons why one would want to support plugins or extensions for an application. It allows third party to extend application, one can isolate development of parts of the application, and pick what to load at runtime.

Whatever the motivation, this means loading .dll’s after the application has started, finding interfaces of interest and registering the classes associated with these interfaces in IoC.

Since IoC already provides the losely coupled instancing required extensions, a bit of dynamic loading is not that far off.

In my previous blog post I showed a basic boilerplate for IoC in .Net Core. “ConfigureService” is the place to add services, and hence a good place to do some dynamic loading.

Loading .dll-files dynamically

var file = "somefile.dll";
var absFile = new FileInfo(file).FullName;
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(absFile); 

Loading an assembly has changed slightly from .Net to .Net core, but its still fairly straight forward. It does want a proper file name though. Running it through FileInfo to get the full path fixes that for us.

Locating a particular class

var iti = typeof(ISomeInterface).GetTypeInfo();
var classTypes = assembly
 .DefinedTypes
 .Where(type => type.IsClass && type.IsPublic && iti.IsAssignableFrom(type.AsType()))
 .ToList(); 

First we need to get type info of the interface, then look through all defined types of the assembly. We want only the ones that are a public class, and that implement our interface.

Instancing a class

foreach (var ct in classTypes)
{
 var classInstance = (T)Activator.CreateInstance(ct);
} 

Since we may have found multiple classes that implement our interface we have a list of types. To create an instance from a type we use Activator.CreateInstance. At this point we could add ctor parameters, but it may be cleaner to have a common interface that implements some sort of Init(params) instead and call that later.

Written by Tedd · Oslo, Norway

Follow the journal via RSS