[英]How to insert an existing IQueryable element to a ThenInclude method in EF Core
How do I insert an existing IQueryable
element in a ThenInclude
method?如何在ThenInclude
方法中插入现有的IQueryable
元素?
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);
Interesting question.有趣的问题。
The problem is that Include
/ ThenInclude
chain is not composable.问题是Include
/ ThenInclude
链不可组合。 In theory the chain can be extracted from the IQueryable
expression and then Include
to be transformed to ThenInclude
.理论上,可以从IQueryable
表达式中提取链,然后将Include
转换为ThenInclude
。
But that's not enough.但这还不够。 All these calls return IIncludableQueryable<TEntity, TProperty>
, where the TEntity
is from the original IQueryable
.所有这些调用都返回IIncludableQueryable<TEntity, TProperty>
,其中TEntity
来自原始IQueryable
。 Hence the ThneInclude
calls also need to be remapped.因此ThneInclude
调用也需要重新映射。
Another problem is when the includable chain contains multiple Include
calls.另一个问题是当包含链包含多个Include
调用时。 Every Include
except the first "restarts" the chain, hence should apply the original chain before converting it to ThenInclude
.每个Include
除了第一个“重新启动”链,因此应该在将其转换为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);
}
}
The two custom ThenInclude
method overloads are to support both reference and collection navigation properties (similar to the standart ThenInclude
overloads).两个自定义ThenInclude
方法重载是为了支持引用和集合导航属性(类似于标准的ThenInclude
重载)。
Now your sample will compile and will insert the second query includes into the the first query include chain.现在您的示例将编译并将第二个查询包含插入到第一个查询包含链中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.