I use Entity Framework Core 2.2 and code-first approach. In database there is a table, let's simplify it as Department.
public class Department
{
public int Id { get; set; }
public int ? ParentId { get; set; }
public string DepartmentName { get; set; }
public ICollection<Department> InnerDepartments { get; set; }
}
I defined configuration with Fluent API and it generated a structure I wanted.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Department>().HasKey(p => p.Id);
modelBuilder.Entity<Department>().HasMany(p => p.InnerDepartments)
.WithOne(p => p.TopDepartment)
.HasForeignKey(p => p.TopDepartmentId);
base.OnModelCreating(modelBuilder);
}
For example, I may have the following data.
Id TopDepartmentId DepartmentName
-----------------------------------------
1 NULL Software Development
2 1 Mobile Development
3 1 Web Development
4 2 IOS Development
5 2 Android Development
6 4 Swift Development
7 4 Objective-C Development
I want to arrange data from this table to following structure:
public class DepartmentShortInfoModel
{
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
public int DirectInnerDepartmentsCount { get; set; }
public int IndirectInnerDepartmentsCount { get; set; }
}
That means that for each department customer wants to get the number of direct inner departments - (first-level-depth children count), for example, Mobile Department has 2 direct children - IOS and Android departments. In the same time, customer wants to know number of indirect inner departments, Mobile Department has 4 indirect children - IOS, Android, Swift, Objective-C, as Swift and Objective-C are children of children for Mobile Department.
I tried to implement this feature with recursive tree traversal algorithm.
private int GetChildrenDepartmentCount(
Department department,
bool useRecursionForTraversal = false)
{
int numberOfChildrenInCurrentLevel = department.InnerDepartments?.Count() ?? 0;
if (useRecursionForTraversal == false)
return numberOfChildrenInCurrentLevel;
if (folder.Children != null)
return numberOfChildrenInCurrentLevel +
folder.Children.Select(p => GetChildrenDepartmentCount(p, useRecursionForTraversal)).Sum();
else return numberOfChildrenInCurrentLevel ;
}
I load data from DbContext with Eager-loading.
using(var context = new DepartmentContext(options))
{
var department = context.Departments.Include(p => p.InnerDepartments)
.First(p => p.Id == 1);
}
However, problem is that EF loads only 1-st level children (Mobile and Web) and for the following levels returns NULL in InnerDepartments property.
How can I request EF to load inner-levels too? I read similar questions where community suggested to use SelectMany
, but problem here is in NULL on InnerDepartments property, not in getting inner values with LINQ query.