Database Table:
I tried this approach to map the category table to EF core:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Category>(entity =>
{
entity
.HasMany(e => e.Children)
.WithOne(e => e.Parent)
.HasForeignKey(e => e.ParentId);
});
}
Entity:
[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; }
}
Then in the repository:
public override IEnumerable<Category> GetAll()
{
IEnumerable<Category> categories = Table.Where(x => x.Parent == null).Include(x => x.Children).ThenInclude(x=> x.Children);
return categories;
}
This worked but anything after 3 levels was not returned no matter how many times you call Include() or ThenInclude().
I ended up writing the code myself to populate the child categories with a recursive function:
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;
}
Does anyone know a better way to write a stored procedure to get the table hierarchy and map to the Category entity I have provided in the example?
Map Entity to Table. Code-First will create the database tables with the name of DbSet properties in the context class, Students and Standards in this case. You can override this convention and give a different table name than the DbSet properties, as shown below.
Keep using EF6 if the data access code is stable and not likely to evolve or need new features. Port to EF Core if the data access code is evolving or if the app needs new features only available in EF Core. Porting to EF Core is also often done for performance.
EF Core is an object-relational mapper (ORM). Object-relational mapping is a technique that enables developers to work with data in object-oriented way by performing the work required to map between objects defined in an application's programming language and data stored in relational datasources.
EF (and LINQ in general) has issues loading tree like data due to lack of recursive expression/CTE support.
But in case you want to load the whole tree (as opposed to filtered tree branch), there is a simple Include
based solution. All you need is a single Include
and then the EF navigation property fixup will do the work for you. And when you need to get only the root nodes as in your sample, the trick is to apply the filter after the query has been materialized (and navigation properties being fixed) by switching to LINQ to Objects context (using AsEnumerable()
as usual).
So the following should produce the desired result with single SQL query:
public override IEnumerable<Category> GetAll()
{
return Table
.AsEnumerable()
.Where(x => x.ParentId == null)
.ToList();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With