.net 实体框架核心:指南大于寻呼

kzmpq1sx  于 2023-06-07  发布在  .NET
关注(0)|答案(2)|浏览(121)

SQL Server在大型表(> 1000000行)上使用Skip/Take会变得非常慢。表的键列类型是Guid,我知道最后读取的行。我试着加载下一页

var keyGuid = Guid.NewGuid(); // Key Guid of the last read row
// var result1 = DbContext.Entity.Where(x => x.Id > keyGuid).Take(10).ToList();
var result2 = DbContext.Entity.Where(x => x.Id.CompareTo(keyGuid) > 0).Take(10).ToList();

虽然第一种方法无法编译,但第二种方法在客户端计算查询(QueryClientEvaluationWarning),并且也没有用。
不幸的是,我无法以任何方式修改数据库。
有没有没有没有自定义SQL的“原生”EF Core解决方案?如果可以拦截SQL代码生成并手动解析表达式,那就没问题了(但是怎么做呢?)

qf9go6mv

qf9go6mv1#

EF Core 2.x

从v2.0开始,EF Core支持所谓的数据库标量函数Map。它没有很好的文档记录,通常用于Map一些数据库函数。但是fluent API还允许您通过HasTranslation方法提供自定义翻译:
设置一个回调,将调用该回调来执行此函数的自定义转换。回调接受与传递给函数调用的参数相对应的表达式集合。回调应该返回一个表示所需翻译的表达式。
下面的类通过定义几个自定义扩展方法来比较Guid值并为它们注册一个自定义转换,将方法调用表达式转换为二进制比较表达式,基本上模拟了缺少的>>=<<= Guid运算符,这允许将它们转换为SQL并正确执行服务器端。只要数据库支持它们(SqlServer支持)。
下面是实现:

public static class GuidFunctions
{
    public static bool IsGreaterThan(this Guid left, Guid right) => left.CompareTo(right) > 0;
    public static bool IsGreaterThanOrEqual(this Guid left, Guid right) => left.CompareTo(right) >= 0;
    public static bool IsLessThan(this Guid left, Guid right) => left.CompareTo(right) < 0;
    public static bool IsLessThanOrEqual(this Guid left, Guid right) => left.CompareTo(right) <= 0;
    public static void Register(ModelBuilder modelBuilder)
    {
        RegisterFunction(modelBuilder, nameof(IsGreaterThan), ExpressionType.GreaterThan);
        RegisterFunction(modelBuilder, nameof(IsGreaterThanOrEqual), ExpressionType.GreaterThanOrEqual);
        RegisterFunction(modelBuilder, nameof(IsLessThan), ExpressionType.LessThan);
        RegisterFunction(modelBuilder, nameof(IsLessThanOrEqual), ExpressionType.LessThanOrEqual);
    }
    static void RegisterFunction(ModelBuilder modelBuilder, string name, ExpressionType type)
    {
        var method = typeof(GuidFunctions).GetMethod(name, new[] { typeof(Guid), typeof(Guid) });
        modelBuilder.HasDbFunction(method).HasTranslation(parameters =>
        {
            var left = parameters.ElementAt(0);
            var right = parameters.ElementAt(1);
            return Expression.MakeBinary(type, left, right, false, method);
        });
    }
}

您只需要将以下行添加到您的上下文OnModelCreating覆盖:

GuidFunctions.Register(modelBuilder);

然后在查询中使用它们:

var result = DbContext.Entity
    .Where(x => x.Id.IsGreaterThan(keyGuid))
    .Take(10).ToList();

EF Core 3.0

HasTranslation现在接收并返回SqlExpression示例,因此

return Expression.MakeBinary(type, left, right, false, method);

应替换为

return new SqlBinaryExpression(type, left, right, typeof(bool), null);
8yparm6h

8yparm6h2#

EF 7

CompareTo方法可以工作并生成缩进的sql查询。

var result2 = DbContext.Entity
  .Where(x => x.Id.CompareTo(keyGuid) > 0)
  .OrderBy(x => x.Id)
  .Take(10)
  .ToList();

相关问题