3

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".

Community
  • 1
  • 1
SMultani
  • 49
  • 1
  • 7
  • 3
    References and Imports are two separate concepts. Have you also added `System.Data` to the `ReferencedAssemblies` for the `CompilerParameters`? – Damien_The_Unbeliever Jan 27 '14 at 11:51
  • Yes I did. System.Data works in all other .vb files in same project and it's also in reference list in project properties. My errors are regarding Imports only. – SMultani Jan 27 '14 at 11:53
  • 2
    We're not talking about the references for the project that we're writing this code in (what you'll see in project properties), we're talking about the references that will be in scope for the piece of compilation that you're doing with CodeDOM - CodeDOM compiled code doesn't "inherit" references from the code performing the compilation. – Damien_The_Unbeliever Jan 27 '14 at 11:56
  • Okay. I understand it now. Thanks. But how can I add references to that dynamic code? – SMultani Jan 27 '14 at 11:58
  • In my first comment, I referred to `ReferencedAssemblies` and `CompilerParameters`. See if you can find those in the code in the answer you linked to. – Damien_The_Unbeliever Jan 27 '14 at 11:59
  • Okay. I try first, if I find any problem then will ask you. Thanks a lot. – SMultani Jan 27 '14 at 12:00

2 Answers2

7

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"
Steven Doggart
  • 43,358
  • 8
  • 68
  • 105
0

Example usage:

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

The simplest and easiest.

    '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

Don't forget the imports:

Imports Microsoft.VisualBasic
Imports System
Imports System.Text
Imports System.CodeDom.Compiler
Imports System.Reflection
ja0
  • 37
  • 4