我使用实体框架核心 2.2 和代码优先方法。在数据库中有一个表,让我们将其简化为部门。
public class Department
{
public int Id { get; set; }
public int ? ParentId { get; set; }
public string DepartmentName { get; set; }
public ICollection<Department> InnerDepartments { get; set; }
}
我用Fluent API定义了配置,它生成了一个我想要的结构。
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);
}
例如,我可能有以下数据。
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
我想将此表中的数据排列为以下结构:
public class DepartmentShortInfoModel
{
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
public int DirectInnerDepartmentsCount { get; set; }
public int IndirectInnerDepartmentsCount { get; set; }
}
这意味着对于每个部门客户想要获得直接内部部门的数量——(一级深度子级计数),例如,移动部门有 2 个直属子部门——IOS 和 Android 部门。同时,客户想知道间接内部部门的数量,移动部门有4个间接子部门 - IOS,Android,Swift,Objective-C,因为Swift和Objective-C是移动部门的子级。
我尝试使用递归树遍历算法实现此功能。
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 ;
}
我从 DbContext 加载数据,并带有 Eager 加载。
using(var context = new DepartmentContext(options))
{
var department = context.Departments.Include(p => p.InnerDepartments)
.First(p => p.Id == 1);
}
但是,问题是 EF 仅加载 1 级子级(移动和 Web),对于以下级别,在 InnerDepartments 属性中返回 NULL。
如何请求 EF 也加载内部关卡?我读过类似的问题,社区建议使用SelectMany
,但这里的问题在于 InnerDepartments 属性上的 NULL,而不是使用 LINQ 查询获取内部值。
尝试以下操作:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication116
{
class Program
{
static DataTable dt;
static void Main(string[] args)
{
dt = new DataTable();
dt.Columns.Add("Id", typeof(int));
dt.Columns.Add("TopDepartmentId", typeof(int));
dt.Columns.Add("DepartmentName", typeof(string));
dt.Rows.Add(new object[] { 1, null, "Software Development" });
dt.Rows.Add(new object[] { 2, 1, "Mobile Development" });
dt.Rows.Add(new object[] { 3, 1, "Web Development" });
dt.Rows.Add(new object[] { 4, 2, "IOS Development" });
dt.Rows.Add(new object[] { 5, 2, "Android Development" });
dt.Rows.Add(new object[] { 6, 4, "Swift Development" });
dt.Rows.Add(new object[] { 7, 4, "Objective-C Development" });
GetTree(null, 0);
Console.ReadLine();
}
static void GetTree(int? parent, int level)
{
DataRow[] children = dt.AsEnumerable().Where(x => x.Field<int?>("TopDepartmentId") == parent).ToArray();
foreach (DataRow child in children)
{
int childId = child.Field<int>("Id");
Console.WriteLine("{0}ID : '{1}', TopDepartmentId : '{2}', DepartmentName : '{3}'",
new string(' ', 5 * level), //ident each level
childId.ToString(),
(child.Field<object>("TopDepartmentId") == null) ? "BOSS" : child.Field<int?>("TopDepartmentId").ToString(),
child.Field<string>("DepartmentName"));
GetTree(childId, level + 1);
}
}
}
}