问题描述
如何在 ThenInclude
方法中插入现有的 IQueryable
元素?
How do I insert an existing IQueryable
element in a ThenInclude
method?
public IQueryable<Store> Store => GetDbSet<Store>()
.Include(st => st.App)
.ThenInclude(app => app.Client)
.ThenInclude(cl => cl.Country)
.ThenInclude(co => co.Culture)
.Include(st => st.Features)
.ThenInclude(this.StoreFeatures);
public IQueryable<StoreFeatures> StoreFeatures => GetDbSet<StoreFeatures>()
.Include(ft => ft.Cultures)
.ThenInclude(ct => ct.Culture);
推荐答案
有趣的问题.
问题是 Include
/ ThenInclude
链不可组合.从理论上讲,可以从 IQueryable
表达式中提取链,然后从 Include
转换为 ThenInclude
.
The problem is that Include
/ ThenInclude
chain is not composable. In theory the chain can be extracted from the IQueryable
expression and then Include
to be transformed to ThenInclude
.
但这还不够.所有这些调用都返回 IIncludableQueryable< TEntity,TProperty>
,其中 TEntity
来自原始的 IQueryable
.因此, ThneInclude
调用也需要重新映射.
But that's not enough. All these calls return IIncludableQueryable<TEntity, TProperty>
, where the TEntity
is from the original IQueryable
. Hence the ThneInclude
calls also need to be remapped.
另一个问题是当包含的链包含多个 Include
调用时.除第一个重新启动"链外的每个 Include
,因此应在将原始链转换为 ThenInclude
之前应用原始链.
Another problem is when the includable chain contains multiple Include
calls. Every Include
except the first "restarts" the chain, hence should apply the original chain before converting it to ThenInclude
.
话虽如此,以下是实现此目的的示例实现:
With that being said, following is a sample implementation which does that:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query;
namespace Microsoft.EntityFrameworkCore
{
public static class IncludeExtensions
{
const string IncludeMethodName = nameof(EntityFrameworkQueryableExtensions.Include);
const string ThenIncludeMethodName = nameof(EntityFrameworkQueryableExtensions.ThenInclude);
public static IQueryable<TEntity> ThenInclude<TEntity, TProperty>(
this IIncludableQueryable<TEntity, IEnumerable<TProperty>> source,
IQueryable<TProperty> includes) => source.Include(includes);
public static IQueryable<TEntity> ThenInclude<TEntity, TProperty>(
this IIncludableQueryable<TEntity, TProperty> source,
IQueryable<TProperty> includes) => source.Include(includes);
static IQueryable<TEntity> Include<TEntity, TProperty>(
this IQueryable<TEntity> source, IQueryable<TProperty> includes)
{
var targetChain = GetIncludeChain(includes.Expression);
if (targetChain.Count == 0) return source;
var sourceChain = GetIncludeChain(source.Expression);
var result = source.Expression;
foreach (var targetInclude in targetChain)
{
bool isInclude = targetInclude.Method.Name == IncludeMethodName;
if (isInclude && result != source.Expression)
{
result = sourceChain.Aggregate(result, (r, i) =>
Expression.Call(i.Method, r, i.Arguments[1]));
}
var typeArgs = targetInclude.Method.GetGenericArguments();
var prevPropertyType = isInclude ? typeof(TProperty) : typeArgs[1];
var propertyType = typeArgs[isInclude ? 1 : 2];
result = Expression.Call(
typeof(EntityFrameworkQueryableExtensions), ThenIncludeMethodName,
new[] { typeof(TEntity), prevPropertyType, propertyType },
result, targetInclude.Arguments[1]);
}
return source.Provider.CreateQuery<TEntity>(result);
}
static Stack<MethodCallExpression> GetIncludeChain(Expression source)
{
var result = new Stack<MethodCallExpression>();
while (source is MethodCallExpression methodCall && methodCall.IsIncludeOrThenInclude())
{
result.Push(methodCall);
source = methodCall.Arguments[0];
}
return result;
}
static bool IsIncludeOrThenInclude(this MethodCallExpression source)
=> source.Method.DeclaringType == typeof(EntityFrameworkQueryableExtensions)
&& source.Method.IsGenericMethod
&& (source.Method.Name == IncludeMethodName || source.Method.Name == ThenIncludeMethodName);
}
}
这两个自定义的 ThenInclude
方法重载将同时支持引用和集合导航属性(类似于标准的 ThenInclude
重载).
The two custom ThenInclude
method overloads are to support both reference and collection navigation properties (similar to the standart ThenInclude
overloads).
现在您的示例将进行编译,并将第二个查询包含项插入到第一个查询的包含链中.
Now your sample will compile and will insert the second query includes into the the first query include chain.
这篇关于如何将现有的IQueryable元素插入EF Core中的ThenInclude方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!