Entity Framework Core 2.0.1 Eager Loading 在所有嵌套的相关实体上 [英] Entity Framework Core 2.0.1 Eager Loading on all nested related entities

查看:21
本文介绍了Entity Framework Core 2.0.1 Eager Loading 在所有嵌套的相关实体上的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的问题,但似乎无法找到解决方法.我正在使用 Entity Framework Core 版本 2.0.1,并希望默认预先加载我的所有实体.

I have a simple problem, but cant seem to find a way around it. I am using Entity Framework Core version 2.0.1 and want to eager load all my entities by default.

示例:

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 为空

But when I load Order entity the related entity Customer and then inside it Address is null

我尝试过的:

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

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

This is just an example, I have entities with multiple nested levels and I want to load nested related data inside of a Generic Repository, so can't use Include and ThenInclude as I don't know the actual entity type when loading it.

示例:

    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();
    }

我错过了什么?我在存储库中做错了什么吗?感谢您对更好设计的任何帮助或指示(如果这就是问题所在).

What am I missing? Is there something wrong I am doing in the repository? Any help or pointer towards a better design (if that's what the issue is here) are appreciated.

谢谢

推荐答案

目前官方不存在此类功能(EF Core 2.0.2 以及即将推出的 2.1).在 Eager load all navigation properties #4851(已关闭)中已被请求,目前由基于规则的急切加载(包括)#2953允许在模型中声明聚合(例如定义包含的属性或通过其他方式)#1985(都在 Backlog 中,即没有具体时间表).

Such feature officially does not exist currently (EF Core 2.0.2 and also the incoming 2.1). It's been requested in Eager load all navigation properties #4851(Closed) and currently is tracked by Rule-based eager load (include) #2953 and Allow for declaring aggregates in the model (e.g. defining included properties or by some other means) #1985 (both in Backlog, i.e. no concrete schedule).

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

I can offer the following two custom extension methods:

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, int maxDepth = int.MaxValue)
        {
            if (maxDepth < 0) throw new ArgumentOutOfRangeException(nameof(maxDepth));
            var entityType = context.Model.FindEntityType(clrEntityType);
            var includedNavigations = new HashSet<INavigation>();
            var stack = new Stack<IEnumerator<INavigation>>();
            while (true)
            {
                var entityNavigations = new List<INavigation>();
                if (stack.Count <= maxDepth)
                {
                    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的便捷方式.

The first is just a convenient way of applying multiple string base Include.

第二个实际工作是使用 EF Core 提供的元数据收集类型的所有 Include 路径.它基本上是从传递的实体类型开始的有向循环图处理,不包括所包含路径的反向导航,并且只发出到叶"的路径.节点.

The second does the actual job of collecting all Include paths for a type using EF Core provided metadata. It's basically directed cyclic graph processing starting with the passed entity type, excluding the inverse navigations of the included paths and emitting only the paths to "leaf" nodes.

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

The usage in your example could be like this:

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();
}

这篇关于Entity Framework Core 2.0.1 Eager Loading 在所有嵌套的相关实体上的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆