|
At work (my real job) today, an interesting discussion came up about how
nice it is to be able to use the Windows Scripting Host Object Model
and / or Javascript to parse and execute arbitrary strings of code "on
the fly". The .NET Platform doesn't have, in any of the supported
languages I've seen, an "EVAL" method to do this. However, after
a little research, I found out that it's almost a trivial matter to "roll
your own"!
The key built - in .NET classes with which this can be so easily achieved
are the CodeProvider classes (VBCodeProvider and CSharpCodeProvider,
for example) in the System.CodeDoMCompiler namespace. Two entire book
chapters could be written about how to use these, along with Reflection,
and some authors probably already have, so I leave the research to you.
Here my objective is simply to illustrate how easily these namespaces
and classes can be put to use for something truly useful.
Think about this: Your application does a lot of business logic, some of
which requires complicated logical strings of code that may change over
time to meet certain business conditions or metadata. Wouldn't it be
great if you could pull the most current string of code to be run out
of your
database based on certain stored procedure input parameters, and be sure
it's run and you get back the desired result? In fact, the returned string
of code may even be dynamically created based on some of the input parameters
from the sproc itself. . . Well, that's what this exercise is all about!.
Without further discussion, I present the basic framework for the class:
Imports Microsoft.VisualBasic
Imports System
Imports System.Text
Imports System.CodeDom.Compiler
Imports System.Reflection
Imports System.IO
Namespace PAB.Util
Public Class EvalProvider
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 Class
End Namespace
|
What does it do? Well, it creates the CodeCompiler instance and the
ICodeCompiler interface, sets it all up, and takes your input string
(which needs to look just like valid code (VB.NET in this case, though
its just as easy to use the CSharpCodeProvider). It creates a public
method, EvalCode() with a return value of Object, compiles it, loads
the compiled assembly into memory, and using Reflection, instantiates
an instance and calls your method which is "wrapped' into the generic
EvalCode method. It then returns the result (whatever Object that may
be) and you are "good to go!".
An example of code that you could send into this method might be:
Dim strConn As String = "Server=(local);dataBase=Northwind;User
id=sa;Password=;"
Dim cmd As New SqlCommand()
Dim cn As New SqlConnection(strConn)
cn.Open()
cmd.Connection = cn
cmd.CommandText = "select * from employees"
cmd.CommandType = CommandType.Text
Dim ds As DataSet = New DataSet()
Dim da As New SqlDataAdapter()
da.SelectCommand = cmd
da.Fill(ds)
Return ds
This code could come from your database, or wherever,
and be dynamically assembled before you send it in. The downloadable
solution has a Winforms test harness illustrating sample usage with
the code snippet shown above, and uses it to populate a DataGrid on
the form. Enjoy!
Download the code that accompanies this article
| Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform. Pete's samples at GotDotNet.com have been downloaded over 41,000 times. You can read Peter's UnBlog Here. --><-- NOTE: Post QUESTIONS on FORUMS! |  | Do you have a question or comment about this article? Have a programming problem you need to solve? Post it at eggheadcafe.com forums and receive immediate email notification of responses.
|