使用工作流来评估动态表达式

我想将一个对象和表达式传递给动态创建的工作流,以模仿许多语言中的Eval函数。 任何人都能帮我解决我做错的事吗? 下面的代码是一个非常简单的例子,如果接受一个Policy对象,将其溢价加倍1.05,然后返回结果。 它抛出了exception:

附加信息:处理工作流树时遇到以下错误:

‘DynamicActivity’:活动’1:DynamicActivity’的私有实现具有以下validation错误:未提供所需活动参数’To’的值。

和代码:

using System.Activities; using System.Activities.Statements; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Policy p = new Policy() { Premium = 100, Year = 2016 }; var inputPolicy = new InArgument(); var theOutput = new OutArgument(); Activity dynamicWorkflow = new DynamicActivity() { Properties = { new DynamicActivityProperty { Name="Policy", Type=typeof(InArgument), Value=inputPolicy } }, Implementation = () => new Sequence() { Activities = { new Assign() { To = theOutput, Value=new InArgument() { Expression = "Policy.Premium * 1.05" } } } } }; WorkflowInvoker.Invoke(dynamicWorkflow); } } public class Policy { public int Premium { get; set; } public int Year { get; set; } } } 

您可以使用Workflow Foundation来评估表达式,但使用几乎任何其他选项都要容易得多。

与您的代码一起使用的关键问题是您没有尝试评估表达式(使用VisualBasicValueCSharpValue )。 分配InArgument`1.Expression是尝试设置值 – 而不是将值设置为表达式的结果。

请记住,编译表达式相当慢(> 10ms),但可以缓存生成的编译表达式以便快速执行。

使用工作流程:

 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(ParamName); var resultVariable = new Variable(ResultName); var daRoot = new DynamicActivity() { Name = "DemoExpressionActivity", Properties = { new DynamicActivityProperty() { Name = ParamName, Type = typeof(InArgument) }, new DynamicActivityProperty() { Name = ResultName, Type = typeof(OutArgument) } }, Implementation = () => new Assign() { To = new ArgumentReference() { ArgumentName = ResultName }, Value = new InArgument(new CSharpValue(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() { { 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); } } 

与等效的Roslyn代码相比 – 其中大部分是不需要的绒毛:

 public class PolicyEvaluatorGlobals { public Policy Policy { get; } public PolicyEvaluatorGlobals(Policy p) { this.Policy = p; } } internal class PolicyExpressionEvaluator { private readonly ScriptRunner 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(expression, options, globalsType: typeof(PolicyEvaluatorGlobals)) .CreateDelegate(); } internal double Evaluate(Policy policy) { return EvaluateInternal(new PolicyEvaluatorGlobals(policy)).Result; } } 

Roslyn已完整记录,并提供了有用的Scripting API Samples页面和示例。