|
|
|
|
@ -4,6 +4,8 @@ using System.Diagnostics.CodeAnalysis;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Linq.Expressions;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using EntityFrameworkCore.Jet.Internal;
|
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
using Microsoft.EntityFrameworkCore.Query;
|
|
|
|
|
using Microsoft.EntityFrameworkCore.Query.Internal;
|
|
|
|
|
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
|
|
|
|
|
@ -22,6 +24,7 @@ public class JetSkipTakePostprocessor : ExpressionVisitor
|
|
|
|
|
private readonly IRelationalTypeMappingSource _typeMappingSource;
|
|
|
|
|
private readonly ISqlExpressionFactory _sqlExpressionFactory;
|
|
|
|
|
private Stack<SelectExpression> parent = new Stack<SelectExpression>();
|
|
|
|
|
private readonly QuerySplittingBehavior? _splittingBehavior;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
|
|
|
|
|
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
|
|
|
|
|
@ -30,9 +33,11 @@ public class JetSkipTakePostprocessor : ExpressionVisitor
|
|
|
|
|
/// </summary>
|
|
|
|
|
public JetSkipTakePostprocessor(
|
|
|
|
|
IRelationalTypeMappingSource typeMappingSource,
|
|
|
|
|
ISqlExpressionFactory sqlExpressionFactory)
|
|
|
|
|
ISqlExpressionFactory sqlExpressionFactory,
|
|
|
|
|
QuerySplittingBehavior? splittingBehavior)
|
|
|
|
|
{
|
|
|
|
|
(_typeMappingSource, _sqlExpressionFactory) = (typeMappingSource, sqlExpressionFactory);
|
|
|
|
|
_splittingBehavior = splittingBehavior;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
@ -61,55 +66,45 @@ public class JetSkipTakePostprocessor : ExpressionVisitor
|
|
|
|
|
return shapedQueryExpression.UpdateQueryExpression(Visit(shapedQueryExpression.QueryExpression));
|
|
|
|
|
case SelectExpression selectExpression:
|
|
|
|
|
{
|
|
|
|
|
if (selectExpression.Offset is not null && selectExpression.Limit is not null)
|
|
|
|
|
if (selectExpression.Orderings.Count == 0 && selectExpression.Offset is not null && _splittingBehavior == QuerySplittingBehavior.SplitQuery)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException(JetStrings.SplitQueryOffsetWithoutOrderBy);
|
|
|
|
|
}
|
|
|
|
|
else if (selectExpression.Offset is not null && selectExpression.Limit is not null)
|
|
|
|
|
{
|
|
|
|
|
SqlExpression offset = selectExpression.Offset!;
|
|
|
|
|
SqlExpression limit = selectExpression.Limit!;
|
|
|
|
|
var total = new SqlBinaryExpression(ExpressionType.Add, offset, limit, typeof(int),
|
|
|
|
|
RelationalTypeMapping.NullMapping);
|
|
|
|
|
MethodInfo? dynMethodO = selectExpression.GetType().GetMethod("set_Offset",
|
|
|
|
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
|
|
|
|
MethodInfo? dynMethod1 = selectExpression.GetType().GetMethod("set_Limit",
|
|
|
|
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
|
|
|
|
SqlExpression mynullexp = null!;
|
|
|
|
|
dynMethodO?.Invoke(selectExpression, new object[] { mynullexp });
|
|
|
|
|
dynMethod1?.Invoke(selectExpression, new object[] { mynullexp });
|
|
|
|
|
|
|
|
|
|
var total = new SqlBinaryExpression(ExpressionType.Add, offset, limit, typeof(int),
|
|
|
|
|
RelationalTypeMapping.NullMapping);
|
|
|
|
|
selectExpression.ApplyLimit(total);
|
|
|
|
|
dynMethod1?.Invoke(selectExpression, new object[] { total });
|
|
|
|
|
|
|
|
|
|
selectExpression.ReverseOrderings();
|
|
|
|
|
|
|
|
|
|
selectExpression.ApplyLimit(limit);
|
|
|
|
|
|
|
|
|
|
parent.TryPeek(out var parentselect);
|
|
|
|
|
if (parentselect != null)
|
|
|
|
|
if (parentselect != null && parentselect.Orderings.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
if (parentselect.Orderings.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
selectExpression.ReverseOrderings();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
selectExpression.ReverseOrderings();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parent.Push(selectExpression);
|
|
|
|
|
selectExpression = (SelectExpression)base.Visit(selectExpression);
|
|
|
|
|
var newselectExpression = (SelectExpression)base.Visit(selectExpression);
|
|
|
|
|
parent.Pop();
|
|
|
|
|
return selectExpression;
|
|
|
|
|
return newselectExpression;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return base.Visit(expression);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return base.Visit(expression);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|