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.
For Each item in collection item.PerformAction() Next item
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 End Interface 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) Return True End Function) For Each file In IO.Directory.EnumerateFiles(directory, "*.dll", IO.SearchOption.TopDirectoryOnly) Try 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 foundPlugins.Add(plugin) End If Next Catch ex As Exception 'not a .net assembly End Try Next Return foundPlugins End Function End Class