实体框架核心:指导性强于分页

Flo*_*ian 1 .net c# entity-framework-core

在大型表(> 1000000行)上使用“跳过/执行”时,SQL Server变得非常慢。表的关键列类型为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();
Run Code Online (Sandbox Code Playgroud)

虽然第一种方法无法编译,但是第二种方法在客户端上评估查询(QueryClientEvaluationWarning),并且也没有用。

不幸的是,我无法以任何方式修改数据库。

是否有没有自定义SQL的“本机” EF Core解决方案?如果可以拦截SQL代码生成并手动解析表达式,那可能没问题(但是如何?)

Iva*_*oev 6

EF Core 2.x

从v2.0开始,EF Core支持所谓的数据库标量函数映射。它不是很好的文档,通常用于映射某些数据库功能。但是流利的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);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

您所需要做的就是将以下行添加到上下文OnModelCreating覆盖中:

GuidFunctions.Register(modelBuilder);
Run Code Online (Sandbox Code Playgroud)

然后在查询中简单地使用它们:

var result = DbContext.Entity
    .Where(x => x.Id.IsGreaterThan(keyGuid))
    .Take(10).ToList();
Run Code Online (Sandbox Code Playgroud)

EF Core 3.0

HasTranslation现在接收并返回SqlExpression实例,所以

return Expression.MakeBinary(type, left, right, false, method);
Run Code Online (Sandbox Code Playgroud)

应该替换为

return new SqlBinaryExpression(type, left, right, typeof(bool), null);
Run Code Online (Sandbox Code Playgroud)