Я пытаюсь написать некоторый код, который позволит мне динамично загружать DLLs в свое приложение, в зависимости от параметра настройки приложения. Идея состоит в том, что база данных, к которой получат доступ, установлена в параметрах настройки приложения, и затем это загружает соответствующий DLL и присваивает его экземпляру интерфейса для моего приложения к доступу.
Это - мой код в данный момент:
Dim SQLDataSource As ICRDataLayer
Dim ass As Assembly = Assembly. _
LoadFrom("M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\SQLServer.dll")
Dim obj As Object = ass.CreateInstance(GetType(ICRDataLayer).ToString, True)
SQLDataSource = DirectCast(obj, ICRDataLayer)
MsgBox(SQLDataSource.ModuleName & vbNewLine & SQLDataSource.ModuleDescription)
У меня есть свой интерфейс (ICRDataLayer), и SQLServer.dll содержит реализацию этого интерфейса. Я просто хочу загрузить блок и присвоить его объекту SQLDataSource.
Вышеупомянутый код просто не работает. Нет никаких выданных исключений, даже Msgbox не появляется. Я ожидал бы, по крайней мере, messagebox, появляющийся ни с чем в нем, но даже этого не происходит!
Есть ли способ определить, реализует ли загруженный блок определенный интерфейс. Я попробовал ниже, но это также, кажется, ничего не делает!
For Each loadedType As Type In ass.GetTypes
If GetType(ICRDataLayer).IsAssignableFrom(loadedType) Then
Dim obj1 As Object = ass.CreateInstance(GetType(ICRDataLayer).ToString, True)
SQLDataSource = DirectCast(obj1, ICRDataLayer)
End If
Next
Править: Новый код от примеров Vlad:
Module CRDataLayerFactory
Sub New()
End Sub
' class name is a contract,
' should be the same for all plugins
Private Function Create() As ICRDataLayer
Return New SQLServer()
End Function
End Module
Выше Модуль в каждом DLL, преобразованном из примера Vlad C#.
Ниже мой код для введения DLL:
Dim SQLDataSource As ICRDataLayer
Dim ass As Assembly = Assembly. _
LoadFrom("M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\SQLServer.dll")
Dim factory As Object = ass.CreateInstance("CRDataLayerFactory", True)
Dim t As Type = factory.GetType
Dim method As MethodInfo = t.GetMethod("Create")
Dim obj As Object = method.Invoke(factory, Nothing)
SQLDataSource = DirectCast(obj, ICRDataLayer)
Править: Реализация на основе кода Paul Kohler
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
For Each assemblyType In Assembly.LoadFrom(file).GetTypes
Dim s As System.Type() = assemblyType.GetInterfaces
For Each ty As System.Type In s
If ty.Name.Contains("ICRDataLayer") Then
MsgBox(ty.Name)
plugin = DirectCast(Activator.CreateInstance(assemblyType), ICRDataLayer)
MessageBox.Show(plugin.ModuleName)
End If
Next
Я получаю следующую ошибку с этим кодом:
Не мог бросить объект типа 'SQLServer. CRDataSource. SQLServer' для ввода 'DynamicAssemblyLoading. ICRDataLayer'.
Фактический DLL находится в другом проекте под названием SQLServer в том же решении как мой код реализации. CRDataSource является пространством имен, и SQLServer является фактическим именем класса DLL. Класс SQLServer реализует ICRDataLayer, таким образом, я не понимаю, почему он не смог бы бросить его. Именование, значительное здесь, я не думал бы, что это будет.
Заключительный Рабочий код
Содержание PluginUtility:
enter code here Public Shared Function GetInstances1(Of Type)(ByVal baseDir As String, ByVal searchPattern As String) As System.Type()
Dim tmpInstances As New List(Of Type)
Try
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
For Each assemblyType In Assembly.LoadFrom(file).GetTypes
Dim s As System.Type() = assemblyType.GetInterfaces
Return s.ToArray()
Next
Next
Catch exp As TargetInvocationException
If (Not exp.InnerException Is Nothing) Then
Throw exp.InnerException
End If
End Try
End Function
Код для загрузки DLL:
enter code here
Dim basedir As String = "M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\"
Dim searchPattern As String = "*SQL*.dll"
Dim plugin As CRDataLayer.ICRDataLayer
Try
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
For Each assemblyType In Assembly.LoadFrom(file).GetExportedTypes
If assemblyType.GetInterface("CRDataLayer.ICRDataLayer") IsNot Nothing Then
plugin = DirectCast(Activator.CreateInstance(assemblyType), CRDataLayer.ICRDataLayer)
MessageBox.Show(plugin.ModuleDescription)
End If
Next
Next
Catch exp As TargetInvocationException
If (Not exp.InnerException Is Nothing) Then
Throw exp.InnerException
End If
Catch ex As Exception
MsgBox(ex.Message)
Clipboard.SetText(ex.Message)
End Try
Версия 2 - Этот пример загружает DLL из текущего каталога. Есть 2 проекта, 1 проект консольного приложения и проект "модуля" (модуль «копирует» свою DLL в рабочий каталог консольного приложения).
Пример ниже просто демонстрирует динамическую загрузку библиотеки DLL, реализующей интерфейс. Интерфейс IModule
просто сообщает свое имя. PlugInUtility.GetInstances (Of IModule) (Environment.CurrentDirectory, «* .Module.dll»)
создаст экземпляр любого экземпляра IModule
, найденного в DLL в текущем каталоге, заканчивающемся на ".Module.dll". Это отраженная версия VB.NET прямо из Mini SQL Query.
Имея это в виду, что-то вроде:
Dim modules As IModule() = PlugInUtility.GetInstances(Of ICRDataLayer)(Environment.CurrentDirectory, "*.Server.dll")
Должно удовлетворить ваше требование. Тогда вам просто нужно выбрать, какой из них выполнить!
Код:
В «VB.LoaderDemo Colsole App»
' IModule.vb
Public Interface IModule
Property ModuleName() As String
End Interface
' PlugInUtility.vb
Imports System.IO
Imports System.Reflection
Public Class PlugInUtility
Public Shared Function GetInstances(Of T)(ByVal baseDir As String, ByVal searchPattern As String) As T()
Dim tmpInstances As New List(Of T)
Try
Dim file As String
For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly)
Dim assemblyType As Type
For Each assemblyType In Assembly.LoadFrom(file).GetTypes()
If (Not assemblyType.GetInterface(GetType(T).FullName) Is Nothing) Then
tmpInstances.Add(DirectCast(Activator.CreateInstance(assemblyType), T))
End If
Next
Next
Catch exp As TargetInvocationException
If (Not exp.InnerException Is Nothing) Then
Throw exp.InnerException
End If
End Try
Return tmpInstances.ToArray()
End Function
End Class
' MainModule.vb
Module MainModule
Sub Main()
Dim plugins As IModule() = PlugInUtility.GetInstances(Of IModule)(Environment.CurrentDirectory, "*.Module.dll")
Dim m As IModule
For Each m In plugins
Console.WriteLine(m.ModuleName)
Next
End Sub
End Module
В «Sample1 DLL» (ссылается на 'VB.LoaderDemo' для IModule)
Imports VB.LoaderDemo
Public Class MyModule1
Implements IModule
Dim _name As String
Public Sub New()
_name = "Sample 1, Module 1"
End Sub
Public Property ModuleName() As String Implements IModule.ModuleName
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
End Class
Результат:
> Sample 1, Module 1
Определен ли тип ICRDataLayer
в загружаемой библиотеке DLL? Если это так, похоже, вы уже ссылаетесь на DLL в настройках вашего проекта.
Вам нужно работать только с отражением:
Dim obj As Object = ass.CreateInstance("ICRDataLayer", True)
Dim t as Type = obj.GetType()
Dim method as MethodInfo = t.GetMethod("DoSomething")
method.Invoke(obj, ...)
Изменить: если ICRDataLayer
реализован в приложении, а плагин просто реализует интерфейс, вам понадобится плагин, чтобы предоставить вам фабрику: ( извините за код C #, я не знаком с синтаксисом VB.NET)
// in each of plugins:
static class CRDataLayerFactory // class name is a contract,
{ // should be the same for all plugins
static ICRDataLayer Create()
{
return new CRDataLayerImplementation();
}
}
Код приложения должен выглядеть так:
Dim factory As Object = ass.CreateInstance("CRDataLayerFactory", True)
Dim t as Type = factory.GetType()
Dim method as MethodInfo = t.GetMethod("Create")
Dim obj as Object = method.Invoke(factory, null)
SQLDataSource = DirectCast(obj, ICRDataLayer)
Несколько вещей, которые нужно искать в вашем коде