I have found answer to this question already in this post : https://stackoverflow.com/a/14711110/1764912
But my next query is, When I try to declare either a DataTable or MsgBox inside this dynamic code, it give me an error that "Type 'DataTable' is not defined" and "Type 'MsgBox' is not defined" is. If I add imports using either first line in dynamic code as :
Imports System.Data
or
Imports System.Data.DataTable
or if I use any of the following code in GenerateScript() function (Please refer https://stackoverflow.com/a/14711110/1764912 for GenerateScript() function)
Dim importDataNameSpace As String = GetType(DataTable).Namespace
Dim codeArray() As String = New String() {"Imports " & importDataNameSpace & Environment.NewLine & code}
or if I use
Dim codeArray() As String = New String() {"Imports System.Data" & Environment.NewLine & code}
or
Dim codeArray() As String = New String() {"Imports System.Data.DataTable" & Environment.NewLine & code}
In all above cases, it give me an error "System.Data does not contain any public members or couldn't found".
Importing namespaces does nothing for you unless you first also reference the library. If the library isn't referenced, then the namespace that you're importing will effectively be empty.
As others have mentioned in the comments above, just because you have the System.Data.dll
library referenced in your project, that doesn't mean that it is also referenced by the assembly that you are dynamically compiling. Each assembly needs to directly reference all of the assemblies that it needs. Dynamically compiled assemblies are no exception.
References are added to the dynamic assembly via the CompilerParameters.ReferencedAssemblies.Add
method. You can see an example of that in my answer to the question that you linked to. In that example, I had the dynamic assembly reference back to the main assembly so that it could use the IScript
interface. You can, however, add as many references as you like. To also add a reference to System.Data.dll
, you could do it like this:
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
Since the System.Data.dll
assembly is in the GAC, you don't need to specify a full path. Notice also, that in order to use DataTable
, you'll also need to add a reference to System.Xml.dll
. You'd find that out as soon as you ran the code.
So, if you had the above method defined, and you had the following interface defined:
Public Interface IScript
Function DoWork() As String
End Interface
Then, you'd be able to call it like this:
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"