Plugin based development
I’m currently working on a software project for a well known Church based over in Los Angeles where we are installing an online ordering portal, the site in question uses ASP NET Webforms, but is an LOB (Line of Business) application used by many over vendors in our market. We need to integrate with the customer at the payment stage in order to:
- Update their internal system with the information from our system.
- Collect payment for the order.
As we need this system to be totally independent of our core system, we need to be able to reference these “Plugins” from an outside source, but we need to know what these interfaces actually do!
The answer is simple. Interfaces.
An interface is a method of declaring what properties, methods a particular CLR type is compatible with. Most of us use this without knowing about it every day, when you use:
For Each item in collection
You are actually referencing the IEnumerable(Of T) interface, which tells the compiler I am able to Enumerate and yield a value several times from this object.
So, we’ve decided that we need an Interface, but how do we actually load these dll’s at run time? It can be a big question at first, (it was for me) – but after consulting with Dr. Google I found the classes I needed.
First, System.Assembly. It provides us with all the gubbins we need to load and inspect an assembly dynamically at run time.
assemblyToLoad = System.Reflection.Assembly.LoadFile(file)
This function will physically load the CLR with this assembly, in order for us to inspect it using the System.Type class.
Dim interfaces() As System.Type
interfaces = assemblyToLoad.GetExportedTypes()
So a couple things to note on the above example, first, we are using “GetExportedTypes.” The reason you want this method is because it only returns the types that are availiable to anything external. If you use the other methods, you may find more interface implementations than you were supposed to, or, you’ll find internal .NET generated classes like MySettings classes. Public declarations were invented for a reason – Make sure you use them correctly!
So, if we put what we know together we could make this a nice generic function, and for extra type safety we include a base “IPlugin” interface, it allows us both sides to ensure the class/interface we are loaded was meant to be a plugin. Here’s the code in full:
Public Class Plugins
Public Interface IPlugin
Property AssemblyPath As String
Public Shared Function EnumerateFolder(Of T As IPlugin)(ByVal directory As String) As List(Of T)
Dim assembly As Reflection.Assembly
Dim foundPlugins As New List(Of T)
Dim types() As Type
Dim filter As Reflection.TypeFilter
Dim plugin As T
filter = New Reflection.TypeFilter(Function(m As Type, criteria As Object)
For Each file In IO.Directory.EnumerateFiles(directory, "*.dll", IO.SearchOption.TopDirectoryOnly)
assembly = System.Reflection.Assembly.LoadFile(file)
For Each foundType As System.Type In assembly.GetExportedTypes
types = foundType.FindInterfaces(filter, Nothing)
If types.Contains(GetType(T)) And types.Contains(GetType(IPlugin)) Then
plugin = Activator.CreateInstance(foundType)
plugin.AssemblyPath = file
Catch ex As Exception
'not a .net assembly