Загрузите код 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