【发布时间】:2018-08-09 08:41:33
【问题描述】:
我正在试图弄清楚如何从代表动作“语句”行的字符串集合生成动作...
using System.Linq.Dynamic;
Action<T> BuildAction<T>(T sourceObject, T destinationObject) where T : BaseThing
{
var source = Expression.Parameter(sourceObject.GetType(), "source");
var destination = Expression.Parameter(destinationObject.GetType(), "destination");
var statements = new[] {
"destination.Foo = source.Foo",
"destination.X = source.Y"
};
var parsedStatements = statements.Select(s => DynamicExpression.Parse(new[] { destination, source }, typeof(void), s);
return Expression.Lambda<Action<T>>(Expression.Block(parsedStatements));
}
我们的想法是最终得到类似 ...
Action<T> result = (destination, source) => {
destination.Foo = source.Foo;
destination.X = source.Y;
};
我遇到的另一个问题是源和目标不必是同一类型,它们只共享一个基本类型,因此在此示例中,目标可能没有 Y 属性,源可能没有 X 属性(因此映射)。
更新
所以我有一个部分解决方案,尽管这是我想删除的大量假设,它只映射 {destination}.Foo = {source}.Bar 类型的东西,目前无法深入研究认为这可能有助于其他人确定我的目标,从而帮助我找到更完整的解决方案......
正如我在 cmets 中解释的那样,这是我的工作流引擎工作方式的一小部分,其想法是执行活动,然后作为内部引擎的一部分,它生成此操作以将计算值复制到之前的下一个活动执行。
我有这个结构...
struct PropertySourceInfo
{
public Activity Source { get; set; }
public Activity Destination { get; set; }
public Link Link { get; set; }
}
在下面的代码中由“SourceInfoFor(activity, p)”返回,选择块是我问题的根本原因...
Action<Activity> BuildAssign(Activity activity, Flow flow)
{
var type = activity.GetType();
var destination = Expression.Parameter(typeof(Activity), "activity");
// build property mappings
var assigns = type.GetProperties()
.Where(p => IsPreviousActivityInput(activity, p))
.Select(p => {
var info = SourceInfoFor(activity, p, flow);
if (info != null)
{
var i = info.Value;
var sidx = activity.Previous.IndexOf(sa => sa == i.Source);
var sType = activity.Previous[sidx].GetType().GetCSharpTypeName();
// ok my assumption here is that I have something like this ...
// {destination}.Property = {Source}.Property
// ... so breaking it up I can then build the Expression needed for each part:
var assignParts = i.Link.Expression.Split(' ');
//TODO: do this more intelligently to handle "sub property value passing"
var destExpr = Expression.Property(Expression.Convert(destination, type), assignParts[0].Split(".".ToCharArray()).Last());
var destArray = Expression.Property(destination, type, "Previous");
var sourceActivity = Expression.ArrayAccess(destArray, Expression.Constant(sidx));
var sourceExpr = Expression.Property(Expression.Convert(sourceActivity, activity.Previous[sidx].GetType()), assignParts[2].Split(".".ToCharArray()).Last());
var result = Expression.Assign(destExpr, sourceExpr);
return result;
}
else
return null;
})
.Where(i => i != null)
.ToArray();
// the complete block as a single "Action<TActivity>" that can be called
if (assigns.Any())
{
var result = Expression.Lambda<Action<Activity>>(Expression.Block(assigns), destination);
log.Debug(result.ToString());
return result.Compile();
}
else
return null;
}
请注意
对于堆栈在提出问题时对我们要求的形式因素,我觉得提出我的完整问题域是一个太大的问题,所以虽然这个问题在这种情况下可能可以通过其他方式解决,但我需要以这种方式解决外部问题问题原因。
我也喜欢并希望对表达式树有更深入的了解!
【问题讨论】:
-
您能否解释一下您的业务案例/功能是什么?
-
我不确定它是如何相关的,但没关系......它是自定义工作流引擎的一部分,我在其中执行我的流中的一个活动,然后在下一个活动运行之前我在下一个活动“链接信息”并分配上一个活动的计算输出值。流定义在运行时根据前一组活动的执行流定义了确切的属性输入值,很像 WF。
-
这里值得注意...我在流程的“执行时间之前”运行此代码,这样我就可以“缓存我的编译流程”,然后将其与一整套数据一起重用以进行输入所以它不需要很快,但确实需要产生快速/合理干净的执行时间代码,我的理解是编译的动作会比做一些执行时间反射要好很多。
-
您收到什么错误信息?对我来说,你有一个名为
destination的变量,它包含一个名为source的参数表达式,这看起来有点奇怪。我希望destination是未知的例外。 -
你是对的,我已经修改了代码以使其更有意义
标签: c# dynamic expression-trees