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代码生成并手动解析表达式,那可能没问题(但是如何?)
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)