首页 > 解决方案 > 如何在 Linq 查询中使用方法或表达式?

问题描述

我们在 Entity Framework 6 中将存储过程转换为 Linq 查询。

它有一个子查询not exists,我们用它翻译成Any(). 因为它有相当多的条件运算符,我想提取一个或方法。我可以用 an 来做一次检查,Expression<Func<TableRecord , bool>> 但我不能用 AND 来扩展它。

这是(简化的)原始 SQL 查询:

SELECT
    main.Id,
    main.VersionNumber,
    main.ClosingVersion
FROM TableRecord main
WHERE
    and not main.status = 'in progress'
    and not exists(select sub.Id from TableRecord sub                
                    where 
                        (sub.tbrId = main.tbrId
                            and 
                                ( sub.VersionNumber > main.VersionNumber
                                or
                                (sub.VersionNumber = main.VersionNumber and 
                                    sub.ClosingVersion = 'z' and main.ClosingVersion is null)
                                )
                        )
                )

这是 EF Linq 语句(其中部分):

TableRecord.Where(main => 
                !TableRecord.Any(sub =>
                    sub.tbrId == main.tbrId &&
                    (sub.VersionNumber > main.VersionNumber
                     ||
                     sub.VersionNumber == main.VersionNumber && 
                     sub.ClosingVersion == "z" && main.ClosingVersion == null)));

如果我用一个表达式提取整个子查询,那仍然给我留下相同的运算符和一个异常:System.InvalidOperationException:从范围''引用的'TableRecord'类型的变量'main',但它没有定义

public void MainMethod()
{
   ...

    TableRecord.Where(main => 
                !TableRecord.Any(SubQueryExpression(main)));
}

private static Expression<Func<TableRecord , bool>> SubQueryExpression(TableRecord main)
{
    return sub =>
        sub.tbrId == main.tbrId &&
                    (sub.VersionNumber > main.VersionNumber
                     ||
                     sub.VersionNumber == main.VersionNumber && 
                     sub.ClosingVersion == "z" && main.ClosingVersion == null)));
}

最初我正在考虑使用本地函数:

private static Expression<Func<TableRecord , bool>> SubQueryExpression(TableRecord main)
{
    return sub =>
        sub.tbrId == main.tbrId &&
                    (sub.VersionNumber > main.VersionNumber
                     ||
                     sub.VersionNumber == main.VersionNumber && 
                     VersionEqualAndClosing(sub));
    
    bool VersionEqualAndClosing(TabelregelVersie sub)
    {
        return sub.VersionNumber == main.VersionNumber && sub.ClosingVersion == "z" && main.ClosingVersion == null;
    }
}   

有没有办法提取方法或表达式?让 Sonar 更易于阅读并让 Sonar 满意。

标签: c#linq

解决方案


安装 LINQKit 并重写你的方法:

[Expandable(nameof(SubQueryExpressionImpl))]
private static bool SubQueryExpression(TableRecord main, TableRecord sub)
{
    throw new NotImplementedException();
}

private static Expression<Func<TableRecord, TableRecord, bool>> SubQueryExpressionImpl()
{
    return (main, sub) =>
        sub.tbrId == main.tbrId &&
                    (sub.VersionNumber > main.VersionNumber
                     ||
                     sub.VersionNumber == main.VersionNumber && 
                     VersionEqualAndClosing(main, sub));
}   

[Expandable(nameof(VersionEqualAndClosingImpl))]
public static bool VersionEqualAndClosing(TabelregelVersie main, TabelregelVersie sub)
{
    throw new NotImplementedException();
}

private static Expresion<Func<TabelregelVersie, TabelregelVersie, bool>> VersionEqualAndClosingImpl()
{
    return (main, sub) => sub.VersionNumber == main.VersionNumber && sub.ClosingVersion == "z" && main.ClosingVersion == null;
}

然后您可以通过以下方式使用它们:

public void MainMethod()
{
   ...

    TableRecord
        .AsExpandable()
        .Where(main => 
                !TableRecord.Any(sub => SubQueryExpression(main, sub)));
}

推荐阅读