转换表达式<func <t,v =“”>&gt;表达式<func <t,nullable <v =“”>&gt;&gt; </func <t,> </func <t,>

时间:2013-08-23 05:43:39

标签: c# linq lambda expression-trees nullable

我有一个方法,它采用IOrderedQueryable和Expression&lt; Func&lt; T,V&gt;&gt;它用作SQL数据库中的过滤器和页面记录。

var query = contexBills.AsNoTracking().Where(x => x.Complete==true).OrderBy(x => x.BillID);

var reader = new BulkReader<Bill>(query, x => x.BillId, 10000);

批量阅读器在整个代码中被广泛使用,用于分页大量记录并批量处理它们,并且定义如下

public BulkReader(IOrderedQueryable<T> queryable, Expression<Func<T, Object>> selector, int blockSize = 1000)

对于优化,分页从表中找到的最小值开始,以最大值结束。由于使用Skip()每月在数据库中有数百万条记录。当你达到表中数百万的数据时,Take()方法会降低到每页13秒左右,然后处理整个月的数据可能需要很多小时。

假设集合中的记录很少被标记为完整== false,那么只需选择记录&gt; = [Page Start] AND&lt; [Page End]每分钟大约有一百万条记录可以很快地运行。在某些情况下,您的处理程度略低于传入的blockSize,但处理了最小值和最大值之间的所有记录。

随着月份的进展,最小值会增加,所以假设0最小浪费很多SQL调用什么都没有返回。

所以我必须得到这些值

var min = queryable.Select(selector).DefaultIfEmpty(0).Min();
var max = queryable.Select(selector).DefaultIfEmpty(0).Max();

哪个产生看起来像这样的SQL

SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
MIN([Join1].[A1]) AS [A1]
FROM ( SELECT 
    CASE WHEN ([Project1].[C1] IS NULL) THEN 0 ELSE [Project1].[PrintSummaryID] END AS [A1]
    FROM   ( SELECT 1 AS X ) AS [SingleRowTable1]
    LEFT OUTER JOIN  (SELECT 
        [Extent1].[PrintSummaryID] AS [PrintSummaryID], 
        cast(1 as tinyint) AS [C1]
        FROM [dbo].[tblPrintSummary] AS [Extent1] ) AS [Project1] ON 1 = 1
)  AS [Join1]
)  AS [GroupBy1]
GO

如果我手动编码(作为测试)来进行这样的调用

var min = queryable.Min(x =>(int?)x.BillID) ?? 0;
var max = queryable.Max(x =>(int?)x.BillID) ?? 0;

然后生成的SQL是

SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
MIN([Extent1].[PrintSummaryID]) AS [A1]
FROM [dbo].[tblPrintSummary] AS [Extent1]
)  AS [GroupBy1]
GO

通过声明以下内容可以实现同样的目的:

Expression<Func<Bill, int?>> selector2 = x => x.BillID;

这提供了更简单,更快速的SQL执行的好处,并允许代码成为:

var min = queryable.Select(selector2).Min() ?? 0;
var max = queryable.Select(selector2).Max() ?? 0;

采用明确重新定义所有选择器并为其提供替代的方法将意味着在整个应用程序中进行重复复制和重新编码

我怎样才能使用原始选择器并转换为可以为空的等效版本,而不是必须对每个选择器进行显式编码。

var selector2 = selector.NullableExpression();

我想将此作为表达式&lt; Func&lt; T,V&gt;&gt;上的扩展方法NullableExpression()。这样我就返回一个ExpressionExpression&lt; Func&lt; T,Nullable&lt; V&gt;&gt;&gt;这样我就可以在我的代码中的其他位置使用它。

我正在努力将V转换为Nullable或V?在表达中。

1 个答案:

答案 0 :(得分:5)

非常简单,真的。诀窍是使用源表达式的主体,同时重用其参数。

public static Expression<Func<T, V?>> ToNullableExpression<T, V> 
    (this Expression<Func<T, V>> source) where V : struct
{ 
    if(source == null)
       throw new ArgumentNullException("source");

    var body = Expression.Convert(source.Body, typeof(V?));
    var parameters = source.Parameters;

    return Expression.Lambda<Func<T, V?>>(body, parameters);
}