データベーステーブル:
このアプローチを試して、カテゴリテーブルをEFコアにマッピングしました。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Category>(entity =>
{
entity
.HasMany(e => e.Children)
.WithOne(e => e.Parent)
.HasForeignKey(e => e.ParentId);
});
}
エンティティ:
[Table("Category"]
public class Category : EntityBase
{
[DataType(DataType.Text), MaxLength(50)]
public string Name { get; set; }
public int? ParentId { get; set; }
public int? Order { get; set; }
[ForeignKey("ParentId")]
public virtual Category Parent { get; set; }
public virtual ICollection<Category> Children { get; set; }
}
次に、リポジトリ内:
public override IEnumerable<Category> GetAll()
{
IEnumerable<Category> categories = Table.Where(x => x.Parent == null).Include(x => x.Children).ThenInclude(x=> x.Children);
return categories;
}
これは機能しましたが、Include()またはThenInclude()を何度呼び出しても、3レベル以降は返されませんでした。
子カテゴリに再帰関数を設定するコードを自分で作成することになりました。
public override IEnumerable<Category> GetAll()
{
IEnumerable<Category> categories = Table.Where(x => x.Parent == null).ToList();
categories = Traverse(categories);
return categories;
}
private IEnumerable<Category> Traverse(IEnumerable<Category> categories)
{
foreach(var category in categories)
{
var subCategories = Table.Where(x => x.ParentId == category.Id).ToList();
category.Children = subCategories;
category.Children = Traverse(category.Children).ToList();
}
return categories;
}
テーブル階層を取得し、例で提供したカテゴリエンティティにマップするストアドプロシージャを作成するためのより良い方法を知っている人はいますか?
EF(および一般的にLINQ)には、再帰式/ CTEサポートがないため、データのようなツリーの読み込みに問題があります。
ただし、(フィルタリングされたツリーブランチではなく)wholeツリーをロードする場合は、単純なInclude
ベースのソリューションがあります。必要なのは単一のInclude
だけで、EFナビゲーションプロパティの修正が自動的に機能します。また、サンプルのようにルートノードのみを取得する必要がある場合は、クエリがマテリアライズされた後(およびナビゲーションプロパティが修正された後)に、LINQ to Objectsコンテキストに切り替えて(AsEnumerable()
を使用)、フィルターを適用するのがコツです。いつものように)。
したがって、以下は単一のSQLクエリで目的の結果を生成するはずです。
public override IEnumerable<Category> GetAll()
{
return Table
.Include(x => x.Children)
.AsEnumerable()
.Where(x => x.Parent == null)
.ToList();
}