web-dev-qa-db-ja.com

Entity Framework Core 2.0.1すべてのネストされた関連エンティティでのイーガーロード

私は簡単な問題を抱えていますが、それを回避する方法を見つけることができないようです。 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 entity関連するエンティティCustomerを読み込んでから、その中にAddressがnull

私が試したもの:

  • バージョン2.1へのアップグレードを試行し、LazyLoadingProxiesをfalseに設定して使用しました

これは単なる例です。複数のネストされたレベルを持つエンティティがあり、ジェネリックリポジトリ内にネストされた関連データをロードするため、IncludeおよびThenIncludeを使用できませんロードするときに実際のエンティティタイプがわかりません。

例:

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

私は何が欠けていますか?リポジトリで何か間違ったことはありますか?より良い設計に向けた助けやポインタ(問題がここにある場合)は高く評価されています。

ありがとう

11
Jinish

そのような機能は現在公式には存在していません(EF Core 2.0.2および着信2.1)。 すべてのナビゲーションプロパティ#4851 (Closed)でリクエストされ、現在 ルールベースのeager load(include)#295 および Allow forモデルで集計を宣言する(たとえば、含まれるプロパティを定義する、または他の方法で)#1985 (両方ともバックログ、つまり具体的なスケジュールなし)。

次の2つのカスタム拡張メソッドを提供できます。

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を適用する便利な方法です。

2番目は、EF Coreが提供するメタデータを使用して、タイプのすべてのIncludeパスを収集する実際のジョブを実行します。基本的には、渡されたエンティティタイプから開始し、含まれるパスの逆ナビゲーションを除外し、「リーフ」ノードへのパスのみを出力する循環グラフ処理です。

あなたの例の使用法は次のようになります。

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