4

我有一个采用 IOrderedQueryable 和 Expression<Func<T, V>> 的方法,它用作 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 的记录很少,那么只需选择记录 >= [Page Start] AND < [Page End] 就可以非常快速地以每分钟大约一百万条记录的速度工作。在某些情况下,您处理的数据比传入的 blockSize 略少,但 min 和 max 之间的所有记录都会得到处理。

随着月份的进展,最小值会增加,因此假设 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();

我想将此作为 Expression<Func<T, V>> 上的扩展方法 NullableExpression() 以便我返回 ExpressionExpression<Func<T, Nullable<V>>> ,这样我就可以在其他我的代码中的位置。

我正在努力解决如何将 V 转换为 Nullable 或 V?在一个表达式中。

4

1 回答 1

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);
}
于 2013-08-23T05:50:00.317 回答