繁体   English   中英

如何将现有的 IQueryable 元素插入到 EF Core 中的 ThenInclude 方法

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM