![](/img/trans.png)
[英]How to write Repository method for .ThenInclude in EF Core 2
[英]How to insert an existing IQueryable element to a ThenInclude method in EF Core
如何在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);
有趣的问题。
问题是Include
/ ThenInclude
链不可组合。 理论上,可以从IQueryable
表达式中提取链,然后将Include
转换为ThenInclude
。
但这还不够。 所有这些调用都返回IIncludableQueryable<TEntity, TProperty>
,其中TEntity
来自原始IQueryable
。 因此ThneInclude
调用也需要重新映射。
另一个问题是当包含链包含多个Include
调用时。 每个Include
除了第一个“重新启动”链,因此应该在将其转换为ThenInclude
之前应用原始链。
话虽如此,以下是执行此操作的示例实现:
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
重载)。
现在您的示例将编译并将第二个查询包含插入到第一个查询包含链中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.