Efficiently loading assemblies at runtime with .NET 6*
Back in the days of .NET framework, it was straightforward to load assemblies at runtime using the ‘System.Reflection’ library alone. However, when platform agnostic .NET Core was introduced, it came with a lot of modern designs that broke conventional implementations. Loading runtime assemblies was one of them.
I spent a good amount of time figuring out a generic way of achieving this and developed a reusable & simple open-source library. This library is tested and works on .NET 6 and onward.
What this library does?
Loading assemblies is an important part of many programs. In most cases, your static dependencies will be loaded automatically, but dynamic dependencies require the active use of various assembly loader APIs. This library provides a ready-to-use single API that can resolve both managed and unmanaged dependencies of an application.
Why to use this library?
DynamicDependencyLoader library allows you to develop and deploy pluggable solutions easily and efficiently without any 3rd party (free or paid) dependencies.
Library location: https://github.com/dheerajawale-svg/dynamic-dependency-loader
Nuget package: https://www.nuget.org/packages/DynamicDependencyLoader
How to use?
Here is what you do when you have a main application in which you want to dynamically load another assembly/plugin.
- Add the 'DynamicDependencyLoader' library to your main application project.
- Get the path of the assembly/plugin you want to load at runtime.
- Call RuntimeAssemblyLoader.CreateFromAssemblyFile(assembly_Path) function and pass the path to it.
- Now when you want to use this assembly just call LoadDefaultAssembly() at the point of invocation.
Then use the below function and pass application settings to it.
High level functioning of the library
- The main application project will reference 'DynamicDependencyLoader' library.
- After the application starts, a user must decide if the plugin assembly should be loaded and determine the path of the assembly.
- The application then tells RuntimeAssemblyLoader class of DynamicDependencyLoader to load the assembly from the given file path.
- Plugin assembly is now part of the main application, and its features are available to the main application.
How it works
The library makes use of ‘System.Reflection’ and ‘System.Runtime.Loader’ DLLs to accomplish the dynamic loading of assemblies.
Below are the purposes for using the mentioned components:
System.Reflection
Purpose:
- The classes in the System.Reflection namespace provide a managed view of loaded assemblies and the types defined within them (such as classes, interfaces, and value types).
- You can use reflection to obtain information about these elements dynamically.
- For example, you can retrieve details about a class, its constructors, and its methods.
SYSTEM.RUNTIME.LOADER
Purpose:
- The classes in System.Runtime.Loader allows you to load assemblies independently by reading *.deps.json, isolate them, and unload them when no longer needed.
- It also helps in loading different versions of the same assembly if needed.
ABOUT SYSTEM.RUNTIME.LOADER
System.Runtime.Loader reference is the heart of this library. Hence, let us explore it below.
System.Runtime.Loader exposes AssemblyLoadContext class that helps implementing dynamic loading.
WHAT IS THE ASSEMBLYLOADCONTEXT?
Every .NET Core and .NET 5+ application implicitly uses AssemblyLoadContext. It is the runtime's provider for locating and loading dependencies. Whenever a dependency is loaded, an AssemblyLoadContext instance is invoked to locate it.
- AssemblyLoadContext provides a service for locating, loading, and caching managed assemblies and other dependencies.
- To support dynamic code loading and unloading, it creates an isolated context for loading code and its dependencies in its AssemblyLoadContext instance.
You can find the step-by-step official algorithm of how this class works here.
Further technical details could be part of a future blog in the same series. But for a major population, this level of detail should suffice to decide on the usage of the library.
KEY TAKEAWAYS
- With modern design, Binding redirects do not apply to .NET Core or .NET 5+ applications.
- When assemblies cannot be loaded in a traditional way, System.Runtime.Loader provides a bunch of methods that are used to resolve assemblies at runtime.
- With the help of the 'DynamicDependencyLoader' library, you can dynamically load/resolve assemblies with only a couple of lines of code.
This blog is written by Dheeraj Awale, Associate Software Architect at Decos. He is expert in Full-stack development as well as Windows development which includes technologies like Angular, React, Blazor, Azure, DotNet Core, WPF etc. and comes with wealth of experience in medical device software development.
Decos is a cutting-edge technology services partner ready to meet your diverse needs across various industries, including the medical domain. If you have a question on one of our projects or would like advice on your project or a POC, contact Devesh Agarwal. We’d love to get in touch with you!