.Net Dynamically Load DLL

Я пытаюсь написать некоторый код, который позволит мне динамично загружать 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
8
задан hermiod 10 March 2010 в 23:47
поделиться

3 ответа

Версия 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
4
ответ дан 5 December 2019 в 20:15
поделиться

Определен ли тип 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)
3
ответ дан 5 December 2019 в 20:15
поделиться

Несколько вещей, которые нужно искать в вашем коде

  • Выполните отладку и проверьте, что { Сборка {1}} загружена правильно, на случай, если произойдет сбой из-за проверки зависимостей
  • Вместо использования GetType используйте GetExportedType, чтобы у вас было меньшее подмножество для итерации по
  • . CreateInstance должен использовать ваш loadedType, а не чем интерфейс (вы не можете создать объект из интерфейса)
  • Лично мне не нравится называть свою переменную задницу, я бы вместо этого сократил ее до Assem :)
1
ответ дан 5 December 2019 в 20:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: