【发布时间】:2010-09-26 06:25:17
【问题描述】:
我正在为跨数据源 IQueryProvider 构建表达式树依赖分析器。
也就是说,我有一个带有一些元素的 IQueryable,这些元素可以在内存中针对某个任意提供者(例如实体框架)在本地执行。 IQueryable 中的其他一些元素违背了我需要进行远程 WCF 调用的实体。 WCF 操作采用序列化的表达式树,对其进行反序列化,针对其自己的本地数据存储(也可以说是实体框架)执行 LINQ 查询,然后将结果发回给我(尽管这种机制可以很容易地成为 WCF 数据服务DataServiceQuery ......但我没有使用它,因为它的功能支持水平是有限的......充其量)。从 WCF 服务返回结果后,我将在内存中针对本地执行的 LINQ 查询执行 LINQ 查询的结果。
那么,这有什么难的呢?好吧,我需要确定表达式树的依赖关系,以便我的本地底层查询提供程序不会在尝试执行我的 LINQ 查询时爆炸,该查询具有只能在远程 WCF 服务上执行的组件……反之亦然。
让我们来看一个简单的场景:
var result =
(from entityX in new Query<MyEntityX>()
from entityY in new Query<MyEntityY>()
where entityX.SomeProperty == "Hello" &&
entityY.SomeOtherProperty == "Hello 2" && entityX.Id == entityY.XId).ToList();
Query<T> 是一个简单的可查询包装器,它带有我自己的提供程序,它有机会解析树并找出在用不同的查询提供程序交换根之前要做什么。因此,在上述情况下,我需要:
-
使用本地对象上下文对 MyEntityA 执行查询并仅应用
myEntityX.SomeProperty == "Hello"条件。即在本地运行以下命令:// assume the functionality for replacing new Query<MyEntityA> with new
// ObjectContext<MyEntityA>() is already there...
var resultX = (from entityX in new Query<MyEntityX>()
where entityX.SomeProperty == "Hello").ToList().AsQueryable(); -
发送以下序列化并让它在我的远程 WCF 服务上执行,然后返回结果。
// Send the preceeding expression over the over the wire
// and get the results back (just take my word this already works)
var resultY = (from entityY in new Query<MyEntityY>()
where entityY.SomeOtherProperty == "Hello 2").ToList().AsQueryable(); -
在内存中执行以下:
var finalResult = (from entityX in resultX
from entityY in resultY
where entityX.SomeProperty == "Hello" &&
entityY.SomeOtherProperty == "Hello 2" &&
entityX.Id == entityY.XId).ToList();
请注意,解决方案必须包含一种累积标准的方法,该标准也从投影中指定......就像
var result =
(from i in
(from entityX in new Query<MyEntityX>()
from entityY in new Query<MyEntityY>()
select new { PropX = entityX, PropY = entityY })
where
i.PropX.SomeProperty == "Hello" && i.PropY.SomeOtherProperty == "Hello 2"
&& i.PropX.Id == i.PropY.XId
select i)
.ToList();
这应该会导致在内存中评估其余查询之前实际发出上述相同的两个单独的查询。顺便说一句,我想我可能会使用 PLINQ 和/或 DRYAD 来运行内存中的操作并提高性能。
所以,我有一些想法(比如与访问者一起在树上进行一些传递并为给定实体类型积累候选者),但我正在寻找其他人关于如何积累我的表达式树部分的建议可以针对给定的上下文执行...也就是说,知道一个条件适用于一个基础的新Query<T> 而另一个条件适用于另一个条件...这样我就可以弄清楚我可以对数据做什么存储 1,我可以对数据存储 2 做什么以及我需要在内存中做什么,并相应地执行树的不同部分。它有点像 Funcletizer,但更复杂一些......
感谢您的帮助。
【问题讨论】:
-
我有点不清楚这里的问题是什么。你在问什么?
-
我正在寻求有关如何确定表达式树的依赖关系的建议。也就是说,当访问树时,知道条件 LambdaExpression Where(x => x.SomeProperty == "Hello") 中的 MemberExpression x.SomeProperty 植根于 new Query
() 中的 ConstantExpression。 -
我查看了编辑历史。为什么要通过 WCF 发送表达式树?听起来你在试图保护程序员免受他们自己的伤害——它永远不会奏效。我的建议是将您的查询分解为组件并分别执行。
-
为什么保护程序员免受他们自己的伤害永远行不通?这不是许多现代编程设计模式的原则吗?为什么我们不直接编写包含业务逻辑的存储过程来提供最佳性能和精细控制?
-
因为抽象是有漏洞的,当你需要比抽象提供更多的控制时,它就会崩溃,失去所有好处。我并不是说抽象不好,但如果你有适当的关注点分离,它们就不成问题了。在您的情况下,听起来您需要从许多来源中提取数据。为什么不拥有一个知道如何查询各个来源、编译信息并将其返回给调用者的查询服务呢?推迟查询、拆分查询并针对不同的数据源运行其中的一部分是没有好的方法。
标签: c# linq lambda expression-trees