How to Evaluate a C# Expression Dynamically

How to evaluate a dynamic expression using IronJS?

Here's a sample from IronJS source, and here's another one.

So, your code will be something like this:

private static void Run()
{
var ctx = new IronJS.Hosting.CSharp.Context();

dynamic a = ctx.Execute("var a = (((2+5)*1000)/((30-20)*7)); a;");
Console.WriteLine(a);
}

P.S. As for me, I'd prefer to use C# scripting API instead. Stable version will be released with VS2015 update1 (coming soon) and nightly builds are in a good condition, I've had no troubles with them.

All you need to start coding: small intro and nuget package. Good luck!:)

How can I evaluate C# code dynamically?

DISCLAIMER: This answer was written back in 2008. The landscape has changed drastically since then.

Look at the other answers on this page, especially the one detailing Microsoft.CodeAnalysis.CSharp.Scripting.

Rest of answer will be left as it was originally posted but is no longer accurate.


Unfortunately, C# isn't a dynamic language like that.

What you can do, however, is to create a C# source code file, full with class and everything, and run it through the CodeDom provider for C# and compile it into an assembly, and then execute it.

This forum post on MSDN contains an answer with some example code down the page somewhat:

create a anonymous method from a string?

I would hardly say this is a very good solution, but it is possible anyway.

What kind of code are you going to expect in that string? If it is a minor subset of valid code, for instance just math expressions, it might be that other alternatives exists.


Edit: Well, that teaches me to read the questions thoroughly first. Yes, reflection would be able to give you some help here.

If you split the string by the ; first, to get individual properties, you can use the following code to get a PropertyInfo object for a particular property for a class, and then use that object to manipulate a particular object.

String propName = "Text";
PropertyInfo pi = someObject.GetType().GetProperty(propName);
pi.SetValue(someObject, "New Value", new Object[0]);

Link: PropertyInfo.SetValue Method

Use workflow to evaluate dynamic expression

You can use Workflow Foundation to evaluate expressions, but it is far easier to use almost any other option.

The key issue at play with your code was that you were not trying to evaluate the expression (with either VisualBasicValue or CSharpValue). Assigning InArgument`1.Expression is an attempt to set the value - not to set the value to the result of an expression.

Keep in mind that compiling expressions is fairly slow (>10ms), but the resultant compiled expression can be cached for quick executions.

Using Workflow:

class Program
{
static void Main(string[] args)
{
// this is slow, only do this once per expression
var evaluator = new PolicyExpressionEvaluator("Policy.Premium * 1.05");

// this is fairly fast

var policy1 = new Policy() { Premium = 100, Year = 2016 };
var result1 = evaluator.Evaluate(policy1);

var policy2 = new Policy() { Premium = 150, Year = 2016 };
var result2 = evaluator.Evaluate(policy2);

Console.WriteLine($"Policy 1: {result1}, Policy 2: {result2}");
}

}

public class Policy
{
public double Premium, Year;
}

class PolicyExpressionEvaluator
{
const string
ParamName = "Policy",
ResultName = "result";

public PolicyExpressionEvaluator(string expression)
{
var paramVariable = new Variable<Policy>(ParamName);
var resultVariable = new Variable<double>(ResultName);
var daRoot = new DynamicActivity()
{
Name = "DemoExpressionActivity",
Properties =
{
new DynamicActivityProperty() { Name = ParamName, Type = typeof(InArgument<Policy>) },
new DynamicActivityProperty() { Name = ResultName, Type = typeof(OutArgument<double>) }
},
Implementation = () => new Assign<double>()
{
To = new ArgumentReference<double>() { ArgumentName = ResultName },
Value = new InArgument<double>(new CSharpValue<double>(expression))
}
};
CSharpExpressionTools.CompileExpressions(daRoot, typeof(Policy).Assembly);
this.Activity = daRoot;
}

public DynamicActivity Activity { get; }

public double Evaluate(Policy p)
{
var results = WorkflowInvoker.Invoke(this.Activity,
new Dictionary<string, object>() { { ParamName, p } });

return (double)results[ResultName];
}
}

internal static class CSharpExpressionTools
{
public static void CompileExpressions(DynamicActivity dynamicActivity, params Assembly[] references)
{
// See https://docs.microsoft.com/en-us/dotnet/framework/windows-workflow-foundation/csharp-expressions
string activityName = dynamicActivity.Name;
string activityType = activityName.Split('.').Last() + "_CompiledExpressionRoot";
string activityNamespace = string.Join(".", activityName.Split('.').Reverse().Skip(1).Reverse());
TextExpressionCompilerSettings settings = new TextExpressionCompilerSettings
{
Activity = dynamicActivity,
Language = "C#",
ActivityName = activityType,
ActivityNamespace = activityNamespace,
RootNamespace = null,
GenerateAsPartialClass = false,
AlwaysGenerateSource = true,
ForImplementation = true
};

// add assembly references
TextExpression.SetReferencesForImplementation(dynamicActivity, references.Select(a => (AssemblyReference)a).ToList());

// Compile the C# expression.
var results = new TextExpressionCompiler(settings).Compile();
if (results.HasErrors)
{
throw new Exception("Compilation failed.");
}

// attach compilation result to live activity
var compiledExpression = (ICompiledExpressionRoot)Activator.CreateInstance(results.ResultType, new object[] { dynamicActivity });
CompiledExpressionInvoker.SetCompiledExpressionRootForImplementation(dynamicActivity, compiledExpression);
}
}

Compare to the equivalent Roslyn code - most of which is fluff that is not really needed:

public class PolicyEvaluatorGlobals
{
public Policy Policy { get; }

public PolicyEvaluatorGlobals(Policy p)
{
this.Policy = p;
}
}

internal class PolicyExpressionEvaluator
{
private readonly ScriptRunner<double> EvaluateInternal;

public PolicyExpressionEvaluator(string expression)
{
var usings = new[]
{
"System",
"System.Collections.Generic",
"System.Linq",
"System.Threading.Tasks"
};
var references = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => !a.IsDynamic && !string.IsNullOrWhiteSpace(a.Location))
.ToArray();

var options = ScriptOptions.Default
.AddImports(usings)
.AddReferences(references);

this.EvaluateInternal = CSharpScript.Create<double>(expression, options, globalsType: typeof(PolicyEvaluatorGlobals))
.CreateDelegate();
}

internal double Evaluate(Policy policy)
{
return EvaluateInternal(new PolicyEvaluatorGlobals(policy)).Result;
}
}

Roslyn is fully documented, and has the helpful Scripting API Samples page with examples.

Dynamically evaluating a property string with Expressions

It sounds like you're looking for something like this:

public object Eval(object root, string propertyString)
{
var propertyNames = propertyString.Split('.');
foreach(var prop in propertyNames)
{
var property = root.GetType().GetProperty(prop);
if (property == null)
{
throw new Exception(...);
}

root = property.GetValue(root, null);
}

return root;
}

To create an Expression use this:

public Expression Eval(object root, string propertyString)
{
var propertyNames = propertyString.Split('.');
ParameterExpression param = Expression.Parameter(root.GetType, "_");
Expression property = param;
foreach(var prop in propertyName)
{
property = Expression.PropertyOrField(property, prop);
}

return Expression.Lambda(property, param);
}

Dynamically created expression

Expression.Lambda method has a generic version that allows to specify the delegate type, the last line can be modified to:

var func = Expression.Lambda<Func<Client, bool>>(exp, parameter).Compile();

Example of use:

var list = new List<Client>
{
new Client() { Name = "X Y"}
};

var l2 = list.Where(func).ToList();

Working example: here.

In .NET how to dynamically evaluate a complex expression against an object?

You can't do it either with reflection or with dynamic keyword. What you can do is:

  1. parse the string in order to create an lambda expression tree that
    will be compiled and executed against your parameter(s)
  2. use CodeDOM
  3. use some dynamic language like IronRuby or IronPython


Related Topics



Leave a reply



Submit