4

I have a C# Console Application project.

I have a logical expression that is stored in database as nvarchar.

For example, the stored expression is: ((34 > 0) || (US == ES)) && (4312 = 5691)

While my application running, I want to retrieve the expression and evaluate it so that the result will be true or false.

How can I do it at runtime?

Ahmet Altun
  • 3,910
  • 9
  • 39
  • 64

3 Answers3

7

Here's a rather unusual solution, involving JScript:

  • Create a JScript class with the following code:

    public class JsMath {
        public static function Eval(expression:String) : Object {
            return eval(expression);
        }
    }
    
  • Compile it into a DLL:

    jsc /target:library /out:JsMath.dll JsMath.js
    
  • In your C# project, reference JsMath.dll and Microsoft.JScript.dll

  • Now you can use the Eval method as follows:

    string expression = "((34 > 0) || ('US' == 'ES')) && (4312 == 5691)";
    bool result = (bool)JsMath.Eval(expression);
    

Benefits:

  • no work required to parse the expression, the JScript engine does it for you
  • no need to compile arbitrary code (which can be a big security hole if the code is entered by the user)
  • should work with any simple mathematical or logical expression, as long as it follows the JScript syntax

Drawbacks:

  • no way to pass variables (as far as I know)
  • requires a reference to the JScript assembly (not a big issue in most cases, but I'm not sure this assembly is available in the Client Profile or in Silverlight)
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • 1
    +1 Have used this technique successfully myself. Certainly a less well known technique. – Tim Lloyd Jan 04 '11 at 20:37
  • Haven't worked with it, but if you want to evaluate untrusted strings you'll probably need to take extra precautions to sandbox your javascript. – CodesInChaos Jan 04 '11 at 21:05
  • @CodeInChaos: actually I think it's quite safe. You can only use the built-in methods and objects (which are harmless, as far as I can tell) and namespaces that are imported in the enclosing context. In that case, the context is the JsMath.Eval method, which imports nothing. The only potentially harmful built-in object is ActiveXObject, and you can easily detect it in the input string. – Thomas Levesque Jan 04 '11 at 22:30
  • 2
    "easily detect it in the input string" I don't have a good feeling with that. In most dynamically typed languages it's easy to circumvent such a blacklist, for example using eval. The trick is creating the evil string in a form that doesn't occur as a string literal, and then use some language feature to access the variable/class/... with that name. – CodesInChaos Jan 04 '11 at 22:39
  • 1
    Actually, you can still do some weird things like `JsMath.Eval("JsMath.Eval(expression)")` (which predictably throws an exception "Out of stack space") – Thomas Levesque Jan 04 '11 at 22:39
  • 1
    @CodeInChaos: you're right... but you can also forbid the use of `eval` in the expression ;). Anyway, it's true that you need to be extra careful with the input you accept... – Thomas Levesque Jan 04 '11 at 22:41
3

You can parse the expression into the .NET Expression class and compile and run it in order to get the result.

The class already supports all the logical operations you have in your example, though it appears to be ambiguous (you are using both == and = in a very similar manner).

You will have to write your own parser/converter though.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
0

I have written a more compact and efficient version of K. Scott Allen's JScript inline Eval caller from here (https://odetocode.com/articles/80.aspx):

using System;
using System.CodeDom.Compiler;
using Microsoft.JScript;

class JS
{
    private delegate object EvalDelegate(String expr);
    private static EvalDelegate moEvalDelegate = null;

    public static object Eval(string expr)
    {
        return moEvalDelegate(expr);
    }

    public static T Eval<T>(string expr)
    {
        return (T)Eval(expr);
    }

    public static void Prepare()
    {
    }

    static JS()
    {
        const string csJScriptSource = @"package _{ class _{ static function __(e) : Object { return eval(e); }}}";
        var loParameters = new CompilerParameters() { GenerateInMemory = true };
        var loMethod = (new JScriptCodeProvider()).CompileAssemblyFromSource(loParameters, csJScriptSource).CompiledAssembly.GetType("_._").GetMethod("__");
        moEvalDelegate = (EvalDelegate)Delegate.CreateDelegate(typeof(EvalDelegate), loMethod);
    }
}

Just use it like this:

JS.Eval<Double>("1 + 4 + 5 / 99");

Returns:

5.05050505050505

You could extend it to pass in variable values as well if you wanted to, e.g. pass in a dictionary of names & values. First usage of the static class will take 100-200ms, after that its pretty much instantaneous and doesn't require a separate DLL. Call JS.Prepare() to pre compile to stop the initial delay if you want.

Aaron Murgatroyd
  • 1,506
  • 19
  • 22