EF Core:表达式树 - GroupJoin 动态转换为 SelectMany
EF Core : expression trees - GroupJoin transform to SelectMany dynamically
我想转换以下查询:
(from documentType in entitySet
join userGroupId in Repository.ConvertToBigIntTable(userGroupIds, "userGroupId")
on documentType.Id equals userGroupId.Id into UserGroupIds
from userGroupId in UserGroupIds.DefaultIfEmpty()
join documentTypePermission in Repository.DocumentTypePermissions
on documentType.Id equals documentTypePermission.DocumentTypeId
join userSelectionParam in Repository.UserSelectionParams
on documentTypePermission.UserSelectionId equals userSelectionParam.Id
where documentTypePermission.IsActive &&
((userSelectionParam.UserGroupId != null && userGroupId.Id != null)
|| (userSelectionParam.UserId != null && userSelectionParam.UserId == CurrentUserId))
select documentTypePermission);
像这样:
var query =
from documentType in entitySet
from userGroupId in Repository.ConvertToBigIntTable(userGroupIds, "userGroupId")
.Where(userGroupId => documentType.Id == userGroupId.Id)
.DefaultIfEmpty()
join documentTypePermission in Repository.DocumentTypePermissions
on documentType.Id equals documentTypePermission.DocumentTypeId
join userSelectionParam in Repository.UserSelectionParams
on documentTypePermission.UserSelectionId equals userSelectionParam.Id
....
注:
我已准备好使用 QueryTranslationPreprocessor
拦截表达式树求值的设置
我需要逻辑来生成 .Call
、.SelectMany
等的输出,方法是使用第一个 linq,然后将其转换为上面显示的第二个 linq 查询
需要注意的是,表达式引擎在内部生成了如此多的匿名类型,以至于我无法编写一个通用代码来满足具有此类 groupjoins 的不同 linq 情况
这样做的原因:
EF Core 6 中有几种 GroupJoin
社区尚未准备好解决的问题
我无法直接对 Linq 查询进行更改,因为它们有 1000 个并且在几个地方
因此,在评估多个选项之后,执行上述操作的最佳方法如下所示,位于 VisitMethodCall(MethodCallExpression node)
if (node.Method.DeclaringType == typeof(Queryable) && node.Method.Name == nameof(Queryable.GroupJoin) && node.Arguments.Count == 5)
{
var outer = Visit(node.Arguments[0]);
var inner = Visit(node.Arguments[1]);
var outerKeySelector = Visit(node.Arguments[2]).UnwrapLambdaFromQuote();
var innerKeySelector = Visit(node.Arguments[3]).UnwrapLambdaFromQuote();
var resultSelector = Visit(node.Arguments[4]).UnwrapLambdaFromQuote();
var outerKey = outerKeySelector.Body.ReplaceParameter(outerKeySelector.Parameters[0], resultSelector.Parameters[0]);
var innerKey = innerKeySelector.Body;
var keyMatch = MatchKeys(outerKey, innerKey);
var innerQuery = Expression.Call(
typeof(Queryable), nameof(Queryable.Where), new[] { innerKeySelector.Parameters[0].Type },
inner, Expression.Lambda(keyMatch, innerKeySelector.Parameters));
var asEnumerableInnerQuery = Expression.Call(
typeof(Enumerable),
nameof(Enumerable.AsEnumerable),
new Type[] { innerKeySelector.Parameters[0].Type }, innerQuery);
var resultTypes = resultSelector.Parameters.Select(p => p.Type).ToArray();
var tempProjectionType = typeof(Tuple<,>).MakeGenericType(resultTypes);
var tempProjection = Expression.New(
tempProjectionType.GetConstructor(resultTypes),
new Expression[] { resultSelector.Parameters[0], asEnumerableInnerQuery },
tempProjectionType.GetProperty("Item1"), tempProjectionType.GetProperty("Item2"));
var tempQuery = Expression.Call(
typeof(Queryable), nameof(Queryable.Select), new[] { outerKeySelector.Parameters[0].Type, tempProjectionType },
outer, Expression.Lambda(tempProjection, resultSelector.Parameters[0]));
var tempResult = Expression.Parameter(tempProjectionType, "p");
var projection = resultSelector.Body
.ReplaceParameter(resultSelector.Parameters[0], Expression.Property(tempResult, "Item1"))
.ReplaceParameter(resultSelector.Parameters[1], Expression.Property(tempResult, "Item2"));
var query = Expression.Call(
typeof(Queryable), nameof(Queryable.Select), new[] { tempProjectionType, projection.Type },
tempQuery, Expression.Lambda(projection, tempResult));
return query;
}
return base.VisitMethodCall(node);
}
我想转换以下查询:
(from documentType in entitySet
join userGroupId in Repository.ConvertToBigIntTable(userGroupIds, "userGroupId")
on documentType.Id equals userGroupId.Id into UserGroupIds
from userGroupId in UserGroupIds.DefaultIfEmpty()
join documentTypePermission in Repository.DocumentTypePermissions
on documentType.Id equals documentTypePermission.DocumentTypeId
join userSelectionParam in Repository.UserSelectionParams
on documentTypePermission.UserSelectionId equals userSelectionParam.Id
where documentTypePermission.IsActive &&
((userSelectionParam.UserGroupId != null && userGroupId.Id != null)
|| (userSelectionParam.UserId != null && userSelectionParam.UserId == CurrentUserId))
select documentTypePermission);
像这样:
var query =
from documentType in entitySet
from userGroupId in Repository.ConvertToBigIntTable(userGroupIds, "userGroupId")
.Where(userGroupId => documentType.Id == userGroupId.Id)
.DefaultIfEmpty()
join documentTypePermission in Repository.DocumentTypePermissions
on documentType.Id equals documentTypePermission.DocumentTypeId
join userSelectionParam in Repository.UserSelectionParams
on documentTypePermission.UserSelectionId equals userSelectionParam.Id
....
注:
我已准备好使用
拦截表达式树求值的设置QueryTranslationPreprocessor
我需要逻辑来生成
.Call
、.SelectMany
等的输出,方法是使用第一个 linq,然后将其转换为上面显示的第二个 linq 查询需要注意的是,表达式引擎在内部生成了如此多的匿名类型,以至于我无法编写一个通用代码来满足具有此类 groupjoins 的不同 linq 情况
这样做的原因:
EF Core 6 中有几种
GroupJoin
社区尚未准备好解决的问题我无法直接对 Linq 查询进行更改,因为它们有 1000 个并且在几个地方
因此,在评估多个选项之后,执行上述操作的最佳方法如下所示,位于 VisitMethodCall(MethodCallExpression node)
if (node.Method.DeclaringType == typeof(Queryable) && node.Method.Name == nameof(Queryable.GroupJoin) && node.Arguments.Count == 5)
{
var outer = Visit(node.Arguments[0]);
var inner = Visit(node.Arguments[1]);
var outerKeySelector = Visit(node.Arguments[2]).UnwrapLambdaFromQuote();
var innerKeySelector = Visit(node.Arguments[3]).UnwrapLambdaFromQuote();
var resultSelector = Visit(node.Arguments[4]).UnwrapLambdaFromQuote();
var outerKey = outerKeySelector.Body.ReplaceParameter(outerKeySelector.Parameters[0], resultSelector.Parameters[0]);
var innerKey = innerKeySelector.Body;
var keyMatch = MatchKeys(outerKey, innerKey);
var innerQuery = Expression.Call(
typeof(Queryable), nameof(Queryable.Where), new[] { innerKeySelector.Parameters[0].Type },
inner, Expression.Lambda(keyMatch, innerKeySelector.Parameters));
var asEnumerableInnerQuery = Expression.Call(
typeof(Enumerable),
nameof(Enumerable.AsEnumerable),
new Type[] { innerKeySelector.Parameters[0].Type }, innerQuery);
var resultTypes = resultSelector.Parameters.Select(p => p.Type).ToArray();
var tempProjectionType = typeof(Tuple<,>).MakeGenericType(resultTypes);
var tempProjection = Expression.New(
tempProjectionType.GetConstructor(resultTypes),
new Expression[] { resultSelector.Parameters[0], asEnumerableInnerQuery },
tempProjectionType.GetProperty("Item1"), tempProjectionType.GetProperty("Item2"));
var tempQuery = Expression.Call(
typeof(Queryable), nameof(Queryable.Select), new[] { outerKeySelector.Parameters[0].Type, tempProjectionType },
outer, Expression.Lambda(tempProjection, resultSelector.Parameters[0]));
var tempResult = Expression.Parameter(tempProjectionType, "p");
var projection = resultSelector.Body
.ReplaceParameter(resultSelector.Parameters[0], Expression.Property(tempResult, "Item1"))
.ReplaceParameter(resultSelector.Parameters[1], Expression.Property(tempResult, "Item2"));
var query = Expression.Call(
typeof(Queryable), nameof(Queryable.Select), new[] { tempProjectionType, projection.Type },
tempQuery, Expression.Lambda(projection, tempResult));
return query;
}
return base.VisitMethodCall(node);
}