私は簡単な問題を抱えていますが、それを回避する方法を見つけることができないようです。 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
私が試したもの:
これは単なる例です。複数のネストされたレベルを持つエンティティがあり、ジェネリックリポジトリ内にネストされた関連データをロードするため、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();
}
私は何が欠けていますか?リポジトリで何か間違ったことはありますか?より良い設計に向けた助けやポインタ(問題がここにある場合)は高く評価されています。
ありがとう
そのような機能は現在公式には存在していません(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();
}