8

Is there a way to dynamically execute code contained in a string using .net 2.0, in a similar way to eval() in javascript or using sp_executeSQL in tsql?

I have a string value in a variable that I want to manipulate at some point in my application - so the code would essentially be string manipulation. I don't know what different manipulations will be needed so i'd like them to be configurable.

I don't really care what language the dynamic code is written in, whatever is easiest to implement and simple enough to write.

For example I might want to replace instances of '.' character with '-', or strip out all spaces, or similar. If I was doing this in sql I'd use dynamic sql, but I want to execute it in .net code, something like this:

// Get the value to be manipulated
string s = ... // wherever s comes from

// Get the manipulation code, eg this might come from a database 
// setting that can be changed without recompiling the .net code.
string manipulation = Settings.GetSomeValue("ManipulationSetting");

// This is what I want to know how to do: apply some manipulation to the string.
string result = MagicDynamicEvalClass.Eval(manipulation, s);

// Now I would do stuff with the result.

I could possibly just use regex find/replace expressions. Since all i'm doing is string manipulation this should be sufficient provided I can write clever enough regular expressions. eg:

// Get the value to be manipulated
string s = ... // wherever s comes from

// Get the code to use to manipulate s, eg this might come from a database 
// setting that can be changed without recompiling the .net code.
string findRegex = Settings.GetSomeValue("RegexPattern");
string replaceRegex = Settings.GetSomeValue("RegexReplace");

// This is what I want to know how to do: apply some manipulation to the string.
string result = Regex.Replace(s, findRegex, replaceRegex);

// Now I can do stuff with the result.

But in some cases my manipulation requirement might exceed what's possible with regex, or I might want to apply multiple steps, eg replace '.' with '-' and also strip spaces. Perhaps I could store a list of find/replace regexes and iterate over them... but anyone have a better suggestion?

UPDATE - example using dynamic sql

I don't want a solution that requires me to know in advance what manipulations are possible, and I'm really looking for something simple. eg in sql I'd do something like this:

declare @s nvarchar(1000)
declare @manipulation nvarchar(1000)
declare @result nvarchar(1000)

-- ... Get the values from wherever they come from

-- Execute the manipulation dynamically
EXEC sp_ExecuteSQL @stmt = @manipulation
    , @params = N'@s nvarchar(1000), @result nvarchar(1000) OUTPUT'
    , @s = @s, @result = @result OUTPUT

Then I could put arbitrary sql into my @manipulation, something like this SET @result = REPLACE( REPLACE( @s, '.', '-'), ' ', '' )

Yes, this would require me to be careful about what values are allowed to be put into @manipulation, but it would give me the flexibility I need in future.

A similar approach would be possible in javascript I guess, using eval().

UPDATE - example using MSScript control from .net:

This seems like a possible approach, although perhaps overkill for the simple case I want to deal with. It uses the Microsoft Script Control library to allow execution of arbitrary VBScript.

4
  • Please don't do that. Find another technique.... Commented Feb 3, 2009 at 18:10
  • What would you like it to be executed as? C#? VB.NET? C++/CLI? IronPython?.NET isn't a single language, so how exactly should it be able to execute a string? You have to call the CodeDom functionality to access the compiler for the desired language. However, you should find a different approach. Commented Feb 3, 2009 at 18:20
  • @Justice: I have to agree, this is really just a horrible idea. Commented Feb 3, 2009 at 22:44
  • what's the horrible bit - making app logic configurable? using regular expressions? not always using compiled code? Commented Feb 5, 2009 at 22:34

8 Answers 8

14

It's not tooo hard ;) I put together a little example. This should help you decide if you want to use dynamic scripts.. or regexes.

What you can do is create an interface in your assembly, which your dynamic code will implement:

namespace CompileScriptExample
{
  public interface IStringManipulator
  {
    string processString(string aString);
  }
}

Then create a ScriptRunner class:

namespace CompileScriptExample
{ 
public class ScriptRunner
{

    public static string RunScript(string scriptCode, string scriptParameter)
    {

        CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();

        //configure parameters
        CompilerParameters parameters = new CompilerParameters();
        parameters.GenerateExecutable = false;
        parameters.GenerateInMemory = true;
        parameters.IncludeDebugInformation = false;
        string reference;
        // Set reference to current assembly - this reference is a hack for the example..
        reference = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        parameters.ReferencedAssemblies.Add(reference+"\\CompileScriptExample.exe");

        //compile
        CompilerResults results = provider.CompileAssemblyFromSource(parameters, new string[] { scriptCode });

        if (results.Errors.Count == 0)
        {
            IStringManipulator compiledScript=(IStringManipulator)FindInterface(results.CompiledAssembly, "IStringManipulator");
            return compiledScript.processString(scriptParameter);//run the script, pass the string param..
        }
        else
        {
            foreach(CompilerError anError in results.Errors)
            {
                MessageBox.Show(anError.ErrorText);
            }
            //handle compilation errors here
            //..use results.errors collection
            throw new Exception("Compilation error...");
        }
    }

    private static object FindInterface(Assembly anAssembly, string interfaceName)
    {
        // find our interface type..
        foreach (Type aType in anAssembly.GetTypes())
        {
            if (aType.GetInterface(interfaceName, true) != null)
                return anAssembly.CreateInstance(aType.FullName);
        }
        return null;
    }
}

}

Now all you have to do is create a script string with code that implements your interface like..

string myScriptString=@"using CompileScriptExample;
public class MyStringManipulator : IStringManipulator
{
  public string processString(string aString)
  {
        return aString+aString;
  }
};

and then.. in your code, make use of the ScriptRunner to process your string with your dynamic code:

string processedString = ScriptRunner.RunScript(myScriptString, "hello");
Sign up to request clarification or add additional context in comments.

1 Comment

Working and clean sample. I am impressed.
5

I know you're after C# but the code I have for this is in VB. You could translate it easily enough using Developer Fusion's VB to C# converter. I used it on a project to allow users to add complex calculations into their application at runtime. It compiles their VB code into a library in memory and then runs the code returning the resulting output. It could be repurposed fairly easily for what you're attempting to do.

Imports System.Reflection
Imports System.CodeDom.Compiler
Imports System.Text.RegularExpressions
Imports System.Math

Module Module1

  Function Evaluate(ByVal Expression As String, ByVal Args() As Object) As Object

    If Expression.Length > 0 Then

        'Replace each parameter in the calculation expression with the correct values
        Dim MatchStr = "{(\d+)}"
        Dim oMatches = Regex.Matches(Expression, MatchStr)
        If oMatches.Count > 0 Then
            Dim DistinctCount = (From m In oMatches _
                                 Select m.Value).Distinct.Count
            If DistinctCount = Args.Length Then
                For i = 0 To Args.Length - 1
                    Expression = Expression.Replace("{" & i & "}", Args(i))
                Next
            Else
                Throw New ArgumentException("Invalid number of parameters passed")
            End If
        End If

        Dim FuncName As String = "Eval" & Guid.NewGuid.ToString("N")
        Dim FuncString As String = "Imports System.Math" & vbCrLf & _
                                   "Namespace EvaluatorLibrary" & vbCrLf & _
                                   "  Class Evaluators" & vbCrLf & _
                                   "    Public Shared Function " & FuncName & "() As Double" & vbCrLf & _
                                   "      " & Expression & vbCrLf & _
                                   "    End Function" & vbCrLf & _
                                   "  End Class" & vbCrLf & _
                                   "End Namespace"

        'Tell the compiler what language was used
        Dim CodeProvider As CodeDomProvider = CodeDomProvider.CreateProvider("VB")

        'Set up our compiler options...
        Dim CompilerOptions As New CompilerParameters()
        With CompilerOptions
            .ReferencedAssemblies.Add("System.dll")
            .GenerateInMemory = True
            .TreatWarningsAsErrors = True
        End With

        'Compile the code that is to be evaluated
        Dim Results As CompilerResults = _
            CodeProvider.CompileAssemblyFromSource(CompilerOptions, FuncString)

        'Check there were no errors...
        If Results.Errors.Count > 0 Then
        Else
            'Run the code and return the value...
            Dim dynamicType As Type = Results.CompiledAssembly.GetType("EvaluatorLibrary.Evaluators")
            Dim methodInfo As MethodInfo = dynamicType.GetMethod(FuncName)
            Return methodInfo.Invoke(Nothing, Nothing)
        End If

    Else
        Return 0

    End If

    Return 0

  End Function

End Module

I set up my dynamic code like this:

Dim Expr As String = "  If ({0} < 20000) Then" & vbCrLf & _
                     "    Return Max(15, Min(75,0.12*{0}))" & vbCrLf & _
                     "  Else" & vbCrLf & _
                     "    Return Max(75,0.05*{0})" & vbCrLf & _
                     "  End If"

And then set up some arguments for the expression and execute:

Dim Args As New List(Of String)
While True
    Dim Val As String = Console.ReadLine
    Args.Clear()
    If IsNumeric(Val) Then
        Args.Add(Val)
        Dim dblOut As Object = Evaluate(Expr, Args.ToArray)
        Console.WriteLine(dblOut)
    Else
        Exit While
    End If
End While

Comments

1

like the others already mentioned its not really possible to compile c# in an eval() function. that functionality is planed for a latter release of the clr which anders demoed at the PDC.

as a diffrent solutionm, if your application is able to run on mono you can just use its eval function which can dynamicly compile c# code, just like javascript. it is basicly already doing what .net will be able to do in a year or two.

as an alternative if you cant use mono you could write the part that does the string manipulation in ironruby which has eval(). the rest of your code wont even know you are using ruby for that class/assambly.

the link you posted in the update looks pretty complicated for such a simple use case. using ironruby all you would have to do is write the MyDynamicEvalClass something like this:

class MyDynamicEvalClass
  def eval(someString,transformString)
    eval(someString,transformString)
  end
end

and replacing "ManipulationSetting" with some ruby code that returns a new string

1 Comment

I didn't end up using this, but I think the elegance of switching to a language that has eval() makes it the most practical for what I was trying to achieve.
1

I think it's possible using reflection.emit and codedom to do this, but it's not at all trivial and I advise against it.

As an alternative, you could try configuring a format string, possibly in addition to the regex.

1 Comment

String.Format({0}{1}{2}, "Format ", "string ", "example.");
1

How about my solution for Eval

how to execute string path on dynamic type?

Comments

0

While you could use an enumeration to indicate the action you want to take, or use CodeDom to emit code dynamically, what it comes down to is that you want to define a transformation of some kind, which means that you have inputs, and an output.

Figuring out the output is easy in this case, you have a string. For the inputs, it would seem like you can have a variable number of inputs. That would be defined as an IEnumerable<string>.

With that in mind, you can define an interface like so:

public interface IStringManipulation
{
  string Manipulate(IEnumerable<string> parameters);
}

Then, it would be easy to define implementations of this type and then place the type names in your config.

You really want to do this instead of dynamically compiling code from strings. In using strings, you have a great deal of flexibility, yes, but you have no compile time checking, and are opening yourself up to bugs and security issues.

Also, the time it is going to take to write a piece of code to emit code based on the string fragment you provide is going to be quite tedious as well, as you have to build the assembly, the class, the method, then compile, and then call the method you compile dynamically through reflection (unless you have it implement an interface, in which case, you might as well do what I'm suggesting anyways).

1 Comment

I don't know in advance what manipulations might be necessary, and I don't want to compile and deploy additional types to make these config changes (because of my app architecture this isn't feasible). Therefore I value the configurability of strings over the advantages you describe.
0

I just ran across something the other day which does just this in another .NET language: http://reverseblade.blogspot.com/2009/02/dont-wait-for-c-5-use-nemerle.html

I would imagine you could write your string-processing code in nemerle, compile, and reference from your C# or VB app.

I would still favor an extensible design like casperOne suggests. By doing dynamic scripting, you'll just be pushing compilation into the app and deployment into whatever process gets the programming strings to the app. But it sounds like you have your reasons, so the only other thing I'd consider here is security. Ideally, you want to limit the user as much as possible to just manipulating strings, in which case List-of-Regex seems like a pretty good option.

Comments

0

There is no C# built-in method for calling eval() at runtime.

However, my C# eval program does allow for evaluating C# code. It provides for evaluating C# code at runtime and supports many C# statements. In fact, this code is usable within any .NET project, however, it is limited to using C# syntax. Have a look at my website, http://csharp-eval.com, for additional details.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.