【发布时间】:2015-11-03 13:42:11
【问题描述】:
我正在使用基本的表达式树优化器来构建查询计划。在解析树时,我可以决定如何“最好”地构造它,这取决于我可以分配给每个操作的权重。
如果我有一个简单的树,有两个关于如何执行操作的选择,我希望能够生成树的两种变体,然后可以比较每个变体的权重,看看什么是最有效的.
例如,下面的代码将允许我构造表达式树连接操作的两种变体:一种带有MergeJoinExpression,另一种带有NestedLoopJoinExpression
class Customer
{
public int Id { get; set; }
}
class Orders
{
public int Id { get; set; }
public int CustomerId { get; set; }
}
class MergeJoinExpresion : JoinExpression
{
}
class NestLoopJoinExpresion : JoinExpression
{
}
class Visitor : ExpressionVisitor
{
public List<Expression> GetPlans(Expression expr)
{
// ???
}
override VisitJoin(JoinExpression join)
{
// For this join, I can return the following (trite example)
// return MergeJoinExpresion
// return NestLoopJoinExpresion
return base.VisitJoin(join);
}
}
我如何构造一个方法来生成树的每个变体并将它们返回给我?
class Program
{
static void Main(string[] args)
{
var query = from c in customers
join o in orders on c.Id equals o.CustomerId
select new
{
CustomerId = c.Id,
OrderId = o.Id
};
var plans = new Visitor().GetPlans(query);
}
}
谁能告诉我如何修改Visitor 类GetPlans 方法来生成这些变体?
编辑 - 类似:
class Visitor : ExpressionVisitor
{
private List<Expression> exprs = new List<Expression>();
public List<Expression> GetPlans(Expression expr)
{
Visit(expr);
return exprs;
}
override VisitJoin(JoinExpression join)
{
// For this join, I can return the following (trite example)
// return MergeJoinExpresion
// return NestLoopJoinExpresion
var choices = new Expression[] { MergeJoinExpresion.Create(join), NestLoopJoinExpresion.Create(join) };
foreach(var choice in choices)
{
var cloned = Cloner.Clone(choice);
var newTree = base.VisitJoin(cloned);
exprs.Add(newTree);
}
return base.VisitJoin(join);
}
}
【问题讨论】:
-
不确定访问者是否是最好的方法。 VisitJoin 必须返回它生成的多个变体。所有调用者都必须支持多个变体,并且它们自己可能会生成多个变体。无论如何,加入计划通常不会以这种简单详尽的方式完成,因为时间复杂度是指数级的。
-
连接只是我在一个非常简单的例子中能想到的最简单的例子。我正在使用表达式树来生成不同的计划 - 并且需要一种生成每个可能结果的方法(无论是用于联接还是其他)。所以问题是如何生成可以在期间生成的不同版本的树访问节点。这样我就可以通过检查详尽的搜索来仔细检查我选择的查询计划是不是最好的......
-
我猜你可以让它工作,如果你让访问者方法返回 IEnumerable
而不是 Expression。 -
我认为这不是一个好主意。访问者必须符合基本访问者(并且只返回表达式)。需要发生的事情(尽管我在这里遇到了一个心理障碍)是 - 计算出可以做出多少变化并为每个变化构建一个新树。
-
您还需要更改“基本访问者”。没有办法通过一个表达式来引导多个表达式。您不仅需要一个连接节点上的变体,还需要变体本身的变体。顺便说一句,如果您编写递归方法来遍历树,这将是简单而明显的。返回值将是一个 IEnumerable。唯一的问题是如何调整访问者模式来做到这一点。
标签: c# tree expression expression-trees visitor-pattern