将表达式<Func <T,V >>转换为表达式<Func <T,Nullable <V >>>

Cod*_*ely 4 c# linq lambda nullable expression-trees

我有一个方法,它采用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);
Run Code Online (Sandbox Code Playgroud)

批量阅读器广泛用于整个代码中,以分页大量记录并批量处理它们,并按此定义

public BulkReader(IOrderedQueryable<T> queryable, Expression<Func<T, Object>> selector, int blockSize = 1000)
Run Code Online (Sandbox Code Playgroud)

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

鉴于集合中的记录很少被标记为完整== false,那么只选择记录> = [Page Start] AND <[Page End]可以非常快速地运行,每分钟大约有一百万条记录.在某些情况下,您处理的块数略小于传入的blockSize,但处理了最小值和最大值之间的所有记录.

随着月份的进展,最小值会增加,因此假设0最小浪费很多SQL调用根本没有返回任何内容.

所以我必须得到这些价值观

var min = queryable.Select(selector).DefaultIfEmpty(0).Min();
var max = queryable.Select(selector).DefaultIfEmpty(0).Max();
Run Code Online (Sandbox Code Playgroud)

这产生了看起来像这样的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
Run Code Online (Sandbox Code Playgroud)

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

var min = queryable.Min(x =>(int?)x.BillID) ?? 0;
var max = queryable.Max(x =>(int?)x.BillID) ?? 0;
Run Code Online (Sandbox Code Playgroud)

然后生成的SQL是

SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
MIN([Extent1].[PrintSummaryID]) AS [A1]
FROM [dbo].[tblPrintSummary] AS [Extent1]
)  AS [GroupBy1]
GO
Run Code Online (Sandbox Code Playgroud)

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

Expression<Func<Bill, int?>> selector2 = x => x.BillID;
Run Code Online (Sandbox Code Playgroud)

这样可以实现更简单,更快速的SQL执行,并使代码成为:

var min = queryable.Select(selector2).Min() ?? 0;
var max = queryable.Select(selector2).Max() ?? 0;
Run Code Online (Sandbox Code Playgroud)

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

我怎么能拿一个原始的选择器并一般转换到可空的版本等效,而不是必须明确地编码每一个.

var selector2 = selector.NullableExpression();
Run Code Online (Sandbox Code Playgroud)

我想将此作为表达式<Func <T,V >>的扩展方法NullableExpression(),以便我返回一个ExpressionExpression <Func <T,Nullable <V >>>这样我就可以在其他方面使用它我的代码中的位置.

我正在努力解决如何将V转换为Nullable或V的问题?在表达中.

Ani*_*Ani 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);
}
Run Code Online (Sandbox Code Playgroud)