如何在 linq to sql 的表达式树中包含仅计算一次的 InitationExpression?
我正在尝试手动组合表达式树以实现一定程度的模块化,这似乎暗示我使用标准 linq 运算符。 该代码本质上创建了一棵表达式树,它使用一个表达式来决定调用其他两个表达式中的哪一个。 其他表达式之一需要一个额外的参数,该参数本身是使用另一个表达式获得的。 此参数用于获取多个值,但对于每次访问,都会重复检索该参数的表达式。 我已经包含了代码和输出以更好地解释:
public void Test() {
var parameters = ProjectionOne.Parameters;
Expression<Func<Foo, bool>> isType = f => f.TypeId == 1;
Expression<Func<Foo, Satellite>> satSelector = f => f.Satellites.Single();
var satelliteSelector = Expression.Invoke(satSelector, parameters[0]);
var test = Expression.Lambda<Func<Foo, Bar>>(
Expression.Condition(
Expression.Invoke(isType, parameters[0]),
Expression.Invoke(ProjectionOne, parameters[0]),
Expression.Invoke(ProjectionTwo, parameters[0], satelliteSelector)), parameters);
}
public Expression<Func<Foo, Bar>> ProjectionOne {
get {
return foo => new Bar() {
Id = foo.Id
};
}
}
public Expression<Func<Foo, Satellite, Bar>> ProjectionTwo {
get {
return (foo, sat) => new Bar() {
Id = foo.Id,
Start = sat.Start,
End = sat.End
};
}
}
当我在数据库上运行此查询时,生成的 SQL 如下:
SELECT [t0].[value], [t0].[value2] AS [Start], [t0].[value3] AS [End], [t0].[Id] AS [Id]
FROM (
SELECT
(CASE
WHEN [t0].[TypeId] = @p0 THEN 1
WHEN NOT ([t0].[TypeId] = @p0) THEN 0
ELSE NULL
END) AS [value], (
SELECT [t2].[Start]
FROM [dbo].[Satellite] AS [t2]
WHERE [t2].[Id] = [t0].[Id]
) AS [value2], (
SELECT [t2].[End]
FROM [dbo].[Satellite] AS [t2]
WHERE [t2].[Id] = [t0].[Id]
) AS [value3], [t0].[Id]
FROM [dbo].[Foo] ) AS [t0]
问题是重复的子选择。 一个检索“开始”值,而另一个检索“结束”值。 如果它们都从单个子选择中检索,那就更好了。 如何更改表达式树的构造以强制执行此操作? 另外,我确实知道有更简单的方法来执行此查询,但是它是此处未显示的更大框架的一部分,只能通过能够从大量可重用表达式中手动组装表达式树来实现。
I'm trying to manually combine expression trees to accomplish a level of modularity that seems to allude me using the standard linq operators. The code essentially creates an expression tree that uses one expression to decide which of two other expressions to call. One of the other expressions requires an extra parameter that is itself obtained using another expression. This parameter is used to obtain multiple values, but for each access the expression that retrieves the parameter is repeated. I've included the code and output to better explain:
public void Test() {
var parameters = ProjectionOne.Parameters;
Expression<Func<Foo, bool>> isType = f => f.TypeId == 1;
Expression<Func<Foo, Satellite>> satSelector = f => f.Satellites.Single();
var satelliteSelector = Expression.Invoke(satSelector, parameters[0]);
var test = Expression.Lambda<Func<Foo, Bar>>(
Expression.Condition(
Expression.Invoke(isType, parameters[0]),
Expression.Invoke(ProjectionOne, parameters[0]),
Expression.Invoke(ProjectionTwo, parameters[0], satelliteSelector)), parameters);
}
public Expression<Func<Foo, Bar>> ProjectionOne {
get {
return foo => new Bar() {
Id = foo.Id
};
}
}
public Expression<Func<Foo, Satellite, Bar>> ProjectionTwo {
get {
return (foo, sat) => new Bar() {
Id = foo.Id,
Start = sat.Start,
End = sat.End
};
}
}
When I run this query on a database the SQL is produced as follows:
SELECT [t0].[value], [t0].[value2] AS [Start], [t0].[value3] AS [End], [t0].[Id] AS [Id]
FROM (
SELECT
(CASE
WHEN [t0].[TypeId] = @p0 THEN 1
WHEN NOT ([t0].[TypeId] = @p0) THEN 0
ELSE NULL
END) AS [value], (
SELECT [t2].[Start]
FROM [dbo].[Satellite] AS [t2]
WHERE [t2].[Id] = [t0].[Id]
) AS [value2], (
SELECT [t2].[End]
FROM [dbo].[Satellite] AS [t2]
WHERE [t2].[Id] = [t0].[Id]
) AS [value3], [t0].[Id]
FROM [dbo].[Foo] ) AS [t0]
The problem is the duplicate sub selects. One retrieves the 'Start' value, while the other retrieves the 'End' value. It would be much better if they were both retrieved from a single sub select. How do I change the construction of the expression tree to enforce this? Also, I do understand that there are simpler ways to perform this query, however it is part of a larger framework not shown here that can only be achieved by being able to manually assemble expression trees from a large set of re-useable expressions.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我怀疑您已经完成了您能做的一切(因为您已经在使用投影,因此引擎选择来表达为单独的子选择)。
我能想到的唯一其他事情是(例如)使用表值 UDF(通过数据上下文)来获取开始/结束 - 看看它是否将其扁平化到查询中。
您是否对查询进行了分析并与您喜欢的布局进行了比较? 很可能配置文件是相同的(即 TSQL 优化器已经处理过它)。 这种情况下,无需过度担心。
I suspect that you've already done everything you can (since you are already using a projection, that the engine is choosing to express as individual sub-selects).
The only other things I can think of are (for example) using a table-valued UDF (via the data-context) to get the start/end - see whether it flattens that into the query.
Have you profiled the query and compared to your preferred layout? It might well be that the profile is identical (i.e. the TSQL optimizer has dealt with it). In which case, no need to be overly concerned.