小编典典

实体框架核心2.0.1渴望在所有嵌套相关实体上加载

c#

我有一个简单的问题,但似乎无法解决。我正在使用Entity Framework Core 2.0.1版,并且希望在默认情况下急于加载我的所有实体。

例:

public class Order
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int CustomerId { get; set; }
    public Customer Customer { get; set; }
}

public class Customer
{
    public int Id { get; set; } 
    public string Name { get; set; }
    public int AddressId { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public int Id { get; set; }
    public string PostCode { get; set; }
    public string City { get; set; }
}

但是,当我加载 Order 实体时,相关实体 Customer 然后在其内部 Address 为null

我试过的

  • 尝试升级到2.1版并使用LazyLoadingProxies设置为false

这只是一个例子,我有多个嵌套级别的实体,并且我想在通用存储库中加载嵌套的相关数据,因此不能使用 IncludeThenInclude,
因为我不知道加载时的实际实体类型。

例:

    public virtual async Task<IEnumerable<T>> GetAllAsync(Expression<Func<T, bool>> predicate = null)
    {
        if (predicate == null)
        {
            return await Context.Set<T>().ToListAsync();
        }
        return await Context.Set<T>().Where(predicate).ToListAsync();
    }

我想念什么?我在存储库中有什么问题吗?任何对更好设计的帮助或指点(如果这就是这里的问题),将不胜感激。

谢谢


阅读 265

收藏
2020-05-19

共1个答案

小编典典

该功能目前正式不存在(EF Core 2.0.2以及即将推出的2.1)。已在“
急切加载”中请求所有导航属性#4851(已关闭),目前已通过基于规则的急切加载(包括)#2953允许在模型中声明聚合(例如,定义包含的属性或通过其他方式)进行跟踪#1985(均在积压中,即没有具体的时间表)。

我可以提供以下两种自定义扩展方法:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.Metadata;

namespace Microsoft.EntityFrameworkCore
{
    public static partial class CustomExtensions
    {
        public static IQueryable<T> Include<T>(this IQueryable<T> source, IEnumerable<string> navigationPropertyPaths)
            where T : class
        {
            return navigationPropertyPaths.Aggregate(source, (query, path) => query.Include(path));
        }

        public static IEnumerable<string> GetIncludePaths(this DbContext context, Type clrEntityType)
        {
            var entityType = context.Model.FindEntityType(clrEntityType);
            var includedNavigations = new HashSet<INavigation>();
            var stack = new Stack<IEnumerator<INavigation>>();
            while (true)
            {
                var entityNavigations = new List<INavigation>();
                foreach (var navigation in entityType.GetNavigations())
                {
                    if (includedNavigations.Add(navigation))
                        entityNavigations.Add(navigation);
                }
                if (entityNavigations.Count == 0)
                {
                    if (stack.Count > 0)
                        yield return string.Join(".", stack.Reverse().Select(e => e.Current.Name));
                }
                else
                {
                    foreach (var navigation in entityNavigations)
                    {
                        var inverseNavigation = navigation.FindInverse();
                        if (inverseNavigation != null)
                            includedNavigations.Add(inverseNavigation);
                    }
                    stack.Push(entityNavigations.GetEnumerator());
                }
                while (stack.Count > 0 && !stack.Peek().MoveNext())
                    stack.Pop();
                if (stack.Count == 0) break;
                entityType = stack.Peek().Current.GetTargetType();
            }
        }

    }
}

第一种只是应用多个字符串库的便捷方法Include

第二个步骤Include使用EF
Core提供的元数据来完成收集类型的所有路径的实际工作。它基本上是从所传递的实体类型开始的有向循环图处理,不包括所包含路径的逆向导航,仅发送到“叶”节点的路径。

您的示例中的用法可能是这样的:

public virtual async Task<IEnumerable<T>> GetAllAsync(Expression<Func<T, bool>> predicate = null)
{
    var query = Context.Set<T>()
        .Include(Context.GetIncludePaths(typeof(T));
    if (predicate != null)
        query = query.Where(predicate);
    return await query.ToListAsync();
}
2020-05-19