Загрузите код VB.net из файла.txt и выполните его на лету, используя System.CodeDom.Compiler

Я нашел ответ на этот вопрос уже в этом посте: /questions/40600054/importirovat-kod-iz-teksta-vbnet/40600060#40600060

Но мой следующий запрос: когда я пытаюсь объявить либо DataTable, либо MsgBox внутри этого динамического кода, я получаю ошибку, что "Тип" DataTable "не определен" и "Тип" MsgBox "не определен". Если я добавлю импорт, используя любую первую строку в динамическом коде как:

Imports System.Data

или же

Imports System.Data.DataTable

или если я использую любой из следующего кода в функции GenerateScript() (см. функцию /questions/40600054/importirovat-kod-iz-teksta-vbnet/40600060#40600060 для функции GenerateScript())

Dim importDataNameSpace As String = GetType(DataTable).Namespace
Dim codeArray() As String = New String() {"Imports " & importDataNameSpace & Environment.NewLine & code}

или если я использую

Dim codeArray() As String = New String() {"Imports System.Data" & Environment.NewLine & code}

или же

Dim codeArray() As String = New String() {"Imports System.Data.DataTable" & Environment.NewLine & code}

Во всех вышеперечисленных случаях выдает ошибку "System.Data не содержит общедоступных членов или не найден".

2 ответа

Импорт пространств имен ничего не делает для вас, если вы сначала не ссылаетесь на библиотеку. Если на библиотеку не ссылаются, то импортируемое пространство имен будет фактически пустым.

Как уже упоминалось в комментариях выше, просто потому, что у вас есть System.Data.dll библиотека, на которую ссылается ваш проект, это не означает, что на нее также ссылается сборка, которую вы динамически компилируете. Каждая сборка должна напрямую ссылаться на все сборки, которые ей нужны. Динамически скомпилированные сборки не являются исключением.

Ссылки добавляются в динамическую сборку через CompilerParameters.ReferencedAssemblies.Add метод. Вы можете увидеть пример этого в моем ответе на вопрос, который вы связали. В этом примере у меня была ссылка динамической сборки на основную сборку, чтобы она могла использовать IScript интерфейс. Однако вы можете добавить столько ссылок, сколько захотите. Чтобы также добавить ссылку на System.Data.dll, вы можете сделать это так:

Public Function GenerateScript(code As String) As IScript
    Using provider As New VBCodeProvider()
        Dim parameters As New CompilerParameters()
        parameters.GenerateInMemory = True
        parameters.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location)
        parameters.ReferencedAssemblies.Add("System.Data.dll")
        parameters.ReferencedAssemblies.Add("System.Xml.dll")
        Dim interfaceNamespace As String = GetType(IScript).Namespace
        Dim codeArray() As String = New String() {"Imports " & interfaceNamespace & Environment.NewLine & code}
        Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, codeArray)
        If results.Errors.HasErrors Then
            Throw New Exception("Failed to compile script")
        Else
            Return CType(results.CompiledAssembly.CreateInstance("Script"), IScript)
        End If
    End Using
End Function

Так как System.Data.dll Сборка находится в GAC, вам не нужно указывать полный путь. Обратите также внимание, что для того, чтобы использовать DataTableВам также необходимо добавить ссылку на System.Xml.dll, Вы узнаете об этом, как только запустите код.

Итак, если у вас был определен вышеуказанный метод, и у вас был определен следующий интерфейс:

Public Interface IScript
    Function DoWork() As String
End Interface

Тогда вы сможете назвать это так:

Dim builder As New StringBuilder()
builder.AppendLine("Public Class Script")
builder.AppendLine("    Implements IScript")
builder.AppendLine("    Public Function DoWork() As String Implements IScript.DoWork")
builder.AppendLine("        Dim table As New System.Data.DataTable()")
builder.AppendLine("        table.TableName = ""Hello World""")
builder.AppendLine("        Return table.TableName")
builder.AppendLine("    End Function")
builder.AppendLine("End Class")
Dim script As IScript = GenerateScript(builder.ToString())
Console.WriteLine(script.DoWork())  ' Outputs "Hello World"

Пример использования:

      Eval("TextBox1.Text = TextBox1.Text")

Самый простой и легкий.

          'START EXECUTOR
    Public Function Eval(ByVal vbCode As String) As Object
            Dim c As VBCodeProvider = New VBCodeProvider
            Dim icc As ICodeCompiler = c.CreateCompiler()
            Dim cp As CompilerParameters = New CompilerParameters

            cp.ReferencedAssemblies.Add("system.dll")
            cp.ReferencedAssemblies.Add("system.xml.dll")
            cp.ReferencedAssemblies.Add("system.data.dll")
            ' Sample code for adding your own referenced assemblies
            'cp.ReferencedAssemblies.Add("c:\yourProjectDir\bin\YourBaseClass.dll")
            'cp.ReferencedAssemblies.Add("YourBaseclass.dll")
            cp.CompilerOptions = "/t:library"
            cp.GenerateInMemory = True
            Dim sb As StringBuilder = New StringBuilder("")
            sb.Append("Imports System" & vbCrLf)
            sb.Append("Imports System.Xml" & vbCrLf)
            sb.Append("Imports System.Data" & vbCrLf)
            sb.Append("Imports System.Data.SqlClient" & vbCrLf)
            sb.Append("Namespace PAB  " & vbCrLf)
            sb.Append("Class PABLib " & vbCrLf)

            sb.Append("public function  EvalCode() as Object " & vbCrLf)
            'sb.Append("YourNamespace.YourBaseClass thisObject = New YourNamespace.YourBaseClass()")
            sb.Append(vbCode & vbCrLf)
            sb.Append("End Function " & vbCrLf)
            sb.Append("End Class " & vbCrLf)
            sb.Append("End Namespace" & vbCrLf)
            Debug.WriteLine(sb.ToString()) ' look at this to debug your eval string
            Dim cr As CompilerResults = icc.CompileAssemblyFromSource(cp, sb.ToString())
            Dim a As System.Reflection.Assembly = cr.CompiledAssembly
            Dim o As Object
            Dim mi As MethodInfo
            o = a.CreateInstance("PAB.PABLib")
            Dim t As Type = o.GetType()
            mi = t.GetMethod("EvalCode")
            Dim s As Object
            s = mi.Invoke(o, Nothing)
            Return s
        End Function
        'END EXECUTOR

Не забывайте об импорте:

      Imports Microsoft.VisualBasic
Imports System
Imports System.Text
Imports System.CodeDom.Compiler
Imports System.Reflection
Другие вопросы по тегам