【问题标题】:Entity Framework Core count with expression parameter带有表达式参数的实体框架核心计数
【发布时间】:2020-03-27 07:04:02
【问题描述】:

我正在尝试使用 Entity Framework Core 来生成一个高性能的服务器端 SQL 查询,它计算几组中的记录。例如,假设我有一张桌子:

CREATE TABLE ExOrders
(
    Id UNIQUEIDENTIFIER,
    Column1 VARCHAR(250),
    Column2 INT,
    ColumnN VARCHAR(500),
)

结果应该是选择查询:

select 
    count(<count1Condition>) as C1,
    count(<count2Condition>) as C2
from 
    ExOrders
where 
    <whereGenericCondition>

对于每个条件,我已经生成了一个 Expression&lt;Func&lt;T, bool&gt;&gt; 表达式。

到目前为止我尝试了什么

  1. Linq Count() 函数

    我试图通过如下查询获得上述结果:

Expression<Func<T, bool>> whereGenericCondition = GetExpression1();
Expression<Func<T, bool>> count1Condition = GetExpression2();
Expression<Func<T, bool>> count2Condition = GetExpression3();

var countRequestS1 = _dbcontext.Set<T>()
    .Where(whereGenericCondition)
    .GroupBy(s => 0)
    .Select(agg => new
                   {
                       C1 = agg.Count(count1Condition), // <- parameter error
                       C2 = agg.Count(count2Condition)  // <- parameter error
                   });

问题是Count 扩展不支持表达式参数。

错误:

参数 2:无法从 'System.Linq.Expressions.Expression>' 转换为 'System.Func'

  1. 带有AsQueryable() 的Linq Count() 函数

    我尝试在Count 方法之前调用AsQueryable

var countRequestS2 = _dbcontext.Set<T>()
    .Where(whereGenericCondition)
    .GroupBy(s => 0) // <- from this point onward it is executed clientside
    .Select(agg => new
    {
        C1 = agg.AsQueryable().Count(count1Condition), 
        C2 = agg.AsQueryable().Count(count2Condition) 
    });

但在这种情况下,它只是将所有数据放入应用程序,并在本地处理(在我的情况下是不可接受的情况,因为有数十万行)。

  1. 预编译表达式

我还尝试在使用 count1Conditioncount2Condition 表达式之前编译它们:

var countRequestS3 = _dbcontext.Set<T>()
    .Where(whereGenericCondition)
    .GroupBy(s => 0) // <- from this point onward it is executed clientside
    .Select(agg => new
    {
        C1 = agg.Count(x => count1Condition.Compile()(x)),
        C2 = agg.Count(x => count2Condition.Compile()(x))
    });

但在这种情况下,它只是将所有数据获取到应用程序中,与 #2 相同。

  1. 实现我自己的Count()

我最后一次尝试是实现我自己的CountAfterGroupByMethod,但我得到了一个与泛型 T 类型函数相关的错误。
在上下文模型构建器中,这会崩溃:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasDbFunction(typeof(CountAfterGroupByExtensions).GetMethod("CountAfterGroupBy"), options =>
    {
        options.HasTranslation(CountAfterGroupByExpressionTranslator.Translate);
    });
    // where CountAfterGroupBy is 
    // public static int CountAfterGroupBy<TSource>(this IEnumerable<TSource> source, Expression<Func<TSource, bool>> predicate)
}

有错误:

System.ArgumentException: 'DbFunction 'CountAfterGroupByExtensions.CountAfterGroupBy' 是通用的。不支持泛型方法。'

#3 或#4 中是否有我看不到的缺陷?或者还有什么我可以尝试的吗?

【问题讨论】:

  • 你希望它在这里返回的sql是什么?通常在 sql 中,如果您计算两列(这似乎是您正在做的),除非您尝试执行某种 case 语句,否则您将获得两次相同的结果。这是另一个类似的问题:stackoverflow.com/questions/39590025/…
  • 我很确定 LINQ 翻译不支持您尝试执行的操作。您可能需要运行两个单独的查询并在 EF 核心 GitHub 页面上提交功能提案。
  • 确切的 EF Core 版本是什么(不幸的是,这很重要)?

标签: c# sql-server linq entity-framework-core expression


【解决方案1】:

伯爵不接受条件,对。所以放一个

.Where(count1Condition).Count() 

如果你的 Count 在前面,并且你有你的条件 Count。

【讨论】:

  • 看起来好像操作正在尝试对不同的标准进行两项计数,但这不是只支持一个标准吗?
  • 不,它是正确的地方,正确的结构,一切都很好,你可以做 X.where(something).Count 和 X.where(somethingelse).Count 一起。最后,SQL-Provider 会决定他是否可以将其转换为 SQL,但大多数人都可以。
  • 但这是两个单独的查询,而不是一个包含两列的查询。往返的开销效率不高。
  • @KieranDevlin 末尾有一个分号。这是一个查询。你怎么知道它可能是两个?
  • 因为count的方法签名返回的是一个int,那么如何从一个整数返回一个查询呢? public static int Count&lt;TSource&gt;(this IQueryable&lt;TSource&gt; source)
猜你喜欢
  • 2017-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-09
  • 1970-01-01
  • 1970-01-01
  • 2020-05-30
  • 1970-01-01
相关资源
最近更新 更多