在 Entity Framework 中获取 parent 个部门节点
Get parent department node in Entity Framework
我有一个 SQL table 这样的:
DepartmentID
是部门的 parent。我通过这个 table(在 ASP.net (C#) 项目中):
构建了一棵树
上面树中的记录是:
我需要在这棵树中获得 parents。
我可以在 SQL 服务器中这样做(例如 id=2
,id
是输入参数):
with cte1
as
(
select id,name,DepartmentID, 0 AS level
from Department
where id =2
union all
select Department.ID,Department.name,Department.DepartmentID, level+1
from Department
inner join cte1 on Department.ID=cte1.DepartmentID
)
select * from cte1
输出(id=2 (A))
输出(id=4 (A1))
我知道 EF 不支持 cte
,但我需要在 EF 中得到这个结果。
如果有人能解释这个问题的解决方案,那将非常有帮助。
我能想到的最简单的方法是在 EF 中映射关系,然后检索所有部门,然后从该列表中获取根父级。所有这些都应该加载到内存中,EF 将通过映射处理树结构。或者,您可以启用延迟加载并只获取父项,但随后对于每个子项或子集,EF 在检索期间将执行一个查询。
型号
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public int? DepartmentId { get; set; }
public Department ParentDepartment { get; set; }
public virtual ICollection<Department> ChildDepartments { get; set; }
}
映射(使用流利的)
public DbSet<Department> Departments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// other mapping code
modelBuilder.Entity<Department>()
.HasOptional(x => x.ParentDepartment)
.WithMany(x => x.ChildDepartments)
.HasForeignKey(x => x.DepartmentId);
// other mapping code
}
急切检索根父级
using (var context = new YourDbContext())
{
var allDepartments = context.Departments.ToList(); // eagerly return everything
var rootDepartment = allDepartments.Single(x => x.DepartmentId == null);
}
仅检索根父级然后使用延迟加载,请注意 DbContext 需要可用于延迟加载才能工作,并且还必须在 DbContext 上启用它
using (var context = new YourDbContext())
{
var rootDepartment = context.Departments.Single(x => x.DepartmentId == null);
// do other stuff, as soon as context is disposed you cant lazy load anymore
}
尝试其中之一,
1-
int _ID = 2; // ID criteria
List<object> result = new List<object>(); // we will use this to split parent at child, it is object type because we need Level
var departments = entites.Departments.Where(x => x.ID == _ID).SelectMany(t => entites.Departments.Where(f => f.ID == t.DepartmentID),
(child, parent) => new { departmentID = child.DepartmentID, Name = child.Name, ID = child.ID, level = 0,
Parent = new { DepartmentID = parent.DepartmentID, Name = parent.Name, ID = parent.ID, level = 1 }});
// first we check our ID (we take A from where criteria), then with selectmany T represents the Department A, we need
// department A's departmentID to find its parent, so another where criteria that checks ID == DepartmentID, so we got T and the new list
// basically child from first where parent from second where, and object created.
// for showing the results
foreach (var item in departments)
{
result.Add(new { DepartmentID = item.departmentID,ID = item.ID, level= item.level,Name = item.Name}); // child added to list
result.Add(new { DepartmentID = item.Parent.DepartmentID, ID = item.Parent.ID, level = item.Parent.level, Name = item.Parent.Name }); // parent added to list
}
2-
List<object> childParent = new List<object>();
// basically get the child first
Departments child1 = entites.Departments.Where(x => x.ID == _ID).FirstOrDefault();
// find parent with child object
Departments parent1 = entites.Departments.Where(x => x.ID == child1.DepartmentID).FirstOrDefault();
// create child object with level
childParent.Add(new { child1.DepartmentID, child1.ID,child1.Name , level = 0});
// create parent object with level
childParent.Add(new { parent1.DepartmentID,parent1.ID,parent1.Name, level = 1 });
编辑 1:
3-
另一种方法是,通过将 ID 作为输入并假设 ID 列是唯一的,因此数组中始终有 2 个值,并且通过返回列表,项目的索引实际上代表了它们的级别。 (不会添加结果,因为它们相同 :))。顺便说一句,您也可以使用 Union
而不是 Concat
。
var ress = list.Where(x=> x.ID ==2)
.SelectMany(x=> list.Where(c=> c.ID == x.ID).Concat(list.Where(s => s.ID == x.DepartmentID))).ToList();
DataTable dt = new DataTable();
dt.Columns.Add("DepartmentID");
dt.Columns.Add("ID");
dt.Columns.Add("Name");
dt.Columns.Add("Level");
for (int i = 0; i < ress.Count(); i++)
{
dt.Rows.Add(ress[i].DepartmentID, ress[i].ID, ress[i].Name, i);
}
dataGridView1.DataSource = dt;
编辑 2
linq没有cte,基本都是用view,sp是首选,这里有一个解决办法,可能有点push。无论如何它给出了结果。
List<Departments> childParent = new List<Departments>();
// or basically get the child first
Departments child1 = entites.Departments.Where(x => x.ID == 7).FirstOrDefault();
// find parent with child object
Departments parent1 = entites.Departments.Where(x => x.ID == child1.DepartmentID).FirstOrDefault();
// create child object with level
Departments dep = new Departments(); // I add to department class a string level field
dep.DepartmentID = child1.DepartmentID;
dep.ID = child1.ID;
dep.Name = child1.Name;
dep.level = 0; // first item
childParent.Add(dep);
// create parent object with level
dep = new Departments();
dep.DepartmentID = parent1.DepartmentID;
dep.ID = parent1.ID;
dep.Name = parent1.Name;
dep.level = 1; // parent one
childParent.Add(dep);
while (childParent.Select(t => t.DepartmentID).Last() != null) // after added to list now we always check the last one if it's departmentID is null, if null we need to stop searching list for another parent
{
int? lastDepID = childParent.Last().DepartmentID; // get last departmentID
Departments tempDep = entites.Departments.Single(x => x.ID == lastDepID); // find as object
tempDep.level = childParent.Last().level + 1; // increase last level
childParent.Add(tempDep); // add to list
}
(添加了另一个C1来检查第4级)
希望有所帮助,
下面是简单的控制台项目程序 class 代码。
GetParentSet方法的入参可以用不同的ID来查看。
class Program
{
static void Main(string[] args)
{
Program p = new Program();
var result= p.GetParentSet(6);
foreach(var a in result)
{
Console.WriteLine(string.Format("{0} {1} {2}",a.ID,a.Name,a.DepartmentId));
}
Console.Read();
}
private List<Department> GetParentSet(int id)
{
List<Department> result = new List<Department>(); //Result set
using (RamzDBEntities context = new RamzDBEntities())
{
var nodeList = context.Departments.Where(t=>t.ID<=id).ToList(); //Get All the the entries where ID is below or greater than the given to the list
var item = nodeList.Where(a => a.ID == id).SingleOrDefault(); //Get the default item for the given ID
result.Add(item); //Add it to the list. This will be the leaf of the tree
int size = nodeList.Count(); //Get the nodes count
for (int i = size; i >= 1;i--)
{
var newItem= nodeList.Where(j => j.ID == item.DepartmentId).SingleOrDefault(); //Get the immediate parent. This can be done by matching the leaf Department ID against the parent ID
if (item!=null && !result.Contains(newItem)) //If the selcted immediate parent item is not null and it is not alreday in the list
{
result.Add(newItem); //Add immediate parent item to the list
}
if (newItem.ID == 1) //If the immediate parent item ID is 1 that means we have reached the root of the tree and no need to iterate any more.
break;
item = newItem; //If the immediate parent item ID is not 1 that means there are more iterations. Se the immediate parent as the leaf and continue the loop to find its parent
}
}
return result; //return the result set
}
}
代码本身是不言自明的。但是下面是解释。希望这会有所帮助!
- 首先,ID小于或等于给定ID的所有条目是
分配给列表
- 然后获取树的叶子并将其添加到名为result的列表中。这是我们结果集的第一个元素
- 我们按降序遍历检索到的条目。通过将父 ID 等同于叶的部门 ID
来获取叶的直接父级
- 如果此直接父级不为 null 并且它不在列表中,请将其添加到列表中。
- 将直接父项作为叶子继续循环,这样我们就可以得到直接父项的父项。
- 继续这个直到我们到达树的根。
- 如果直接父 ID=1,这意味着我们已经到达树的根,我们可以打破循环。
由于您生成了 edmx,因此您已经为 DbContext 和模型 Classes 生成了代码,包括 this 屏幕截图中的部门。
您不应修改它们,因为它们可能(将)在任何模型操作中被 EF 工具覆盖。幸运的是,两个 classes 都生成为 partial
,因此创建者考虑了希望安全地自定义它的人们。
下面的示例是为了简化实施而不是为了获得最佳性能。我假设包含 Departments 的 table 不是很大,层次结构中的嵌套级别也不是很深。
在您的项目中创建一个新的 Class(*.cs 文件)并通过您的自定义方法或 属性 扩展自动生成的部门 class:
using System;
using System.Collections.Generic;
using System.Linq;
namespace CustomEF.EFStuff
{
public partial class Departments
{
public List<Departments> Hierarchy {
get {
List<Departments> retVal = new List<Departments>();
retVal.Add(this);
using (YourAutoGeneratedContext ctx = new YourAutoGeneratedContext())
{
Departments tmp = this;
while(tmp.DepartmentID != null)
{
tmp = ctx.Departments.First(d => d.ID == tmp.DepartmentID);
retVal.Add(tmp);
}
}
return retVal;
}
private set { }
}
}
}
当您扩展部分 class 时,请确保将其放在相同的命名空间中。在我的例子中,我将我的项目命名为 CustomEF,并将 edmx 文件放在 EFStuff 子文件夹中,因此生成器将自动生成的 class 放在 CustomEF.EFStuff
命名空间中。
上面的示例将允许您获取任何 Departments 对象的层次结构,例如
int level = 0;
foreach(Departments d in someDepartmentObject.Hierarchy)
{
Console.WriteLine(d.ID.ToString() + ", " + d.DepartmentID.ToString() + ", " + d.Name +", " +(level++).ToString());
}
如果您还需要从一些您有 ID 但没有对象的代码中获取层次结构,您可以另外创建另一个 class(*.cs 文件)我将扩展自动生成的上下文。
using System.Collections.Generic;
using System.Linq;
namespace CustomEF.EFStuff
{
public partial class YourAutoGeneratedContext
{
public List<Departments> GetDepartmentHierarchy(int departmentId)
{
Departments mydep = this.Departments.FirstOrDefault(d => d.ID == departmentId);
if (mydep == null)
{
throw new System.Data.Entity.Core.ObjectNotFoundException("There is no department with ID = " + departmentId.ToString());
}
return mydep.Hierarchy;
}
}
}
或者在这种情况下,您可能希望将实现完全移至上下文 class,而根本不扩展部门 class(并且您不必创建额外的实例您的上下文,您将可以使用 this
。
using System.Collections.Generic;
using System.Linq;
namespace CustomEF.EFStuff
{
public partial class YourAutoGeneratedContext
{
public List<Departments> GetDepartmentHierarchy(int departmentId)
{
Departments tmp = this.Departments.FirstOrDefault(d => d.ID == departmentId);
if (tmp == null)
{
throw new System.Data.Entity.Core.ObjectNotFoundException("There is no department with ID = " + departmentId.ToString());
}
List<Departments> retVal = new List<Departments>();
retVal.Add(tmp);
while (tmp.DepartmentID != null)
{
tmp = this.Departments.First(d => d.ID == tmp.DepartmentID);
retVal.Add(tmp);
}
return retVal;
}
}
}
作为另一个简单的使用示例:
YourAutoGeneratedContext ctx = new YourAutoGeneratedContext();
level = 0;
foreach (Departments currentHier in ctx.GetDepartmentHierarchy(10))
{
Console.WriteLine(currentHier.ID.ToString() + ", " + currentHier.DepartmentID.ToString() + ", " + currentHier.Name + ", " + (level++).ToString());
}
我不知道您对数据库中的数据有多信任。您可能需要执行一些检查,包括交叉引用部门以防止无限循环。
请注意,正式术语 'to extend a class' 可能适用于扩展方法,而不是 partial
classes。我用这个词是因为找不到更好的词了。如果出于某种原因,您需要 method/property 返回 EF 本机 DbSet<>
而不是 List<>
,那么您可能想要使用扩展方法。在这种情况下,您可能需要查看:https://shelakel.co.za/entity-framework-repository-pattern/
这些 post 与您的 question.please 相似,请参阅:
writing-recursive-cte-using-entity-framework-fluent-syntax-or-inline-syntax
converting-sql-statement-that-contains-with-cte-to-linq
我认为没有办法编写单个 LINQ to SQL 查询来获取所有的查询 但是,LINQ 支持执行查询的方法(奇怪的是称为 DataContext.ExecuteQuery)。看起来您可以使用它来调用 SQL 的任意片段并将其映射回 LINQ。
看到这个post:
common-table-expression-in-entityframework
在 EF6 中获取所有父节点到根节点的示例。
public class Department
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public virtual Department Parent { get; set; }
public virtual ICollection<Department> Children { get; set; }
private IList<Department> allParentsList = new List<Department>();
public IEnumerable<Department> AllParents()
{
var parent = Parent;
while (!(parent is null))
{
allParentsList.Add(parent);
parent = parent.Parent;
}
return allParentsList;
}
}
使用 include 关键字。
_context.Invoices.Include(x => x.Users).Include(x => x.Food).ToList();
我有一个 SQL table 这样的:
DepartmentID
是部门的 parent。我通过这个 table(在 ASP.net (C#) 项目中):
上面树中的记录是:
我需要在这棵树中获得 parents。
我可以在 SQL 服务器中这样做(例如 id=2
,id
是输入参数):
with cte1
as
(
select id,name,DepartmentID, 0 AS level
from Department
where id =2
union all
select Department.ID,Department.name,Department.DepartmentID, level+1
from Department
inner join cte1 on Department.ID=cte1.DepartmentID
)
select * from cte1
输出(id=2 (A))
输出(id=4 (A1))
我知道 EF 不支持 cte
,但我需要在 EF 中得到这个结果。
如果有人能解释这个问题的解决方案,那将非常有帮助。
我能想到的最简单的方法是在 EF 中映射关系,然后检索所有部门,然后从该列表中获取根父级。所有这些都应该加载到内存中,EF 将通过映射处理树结构。或者,您可以启用延迟加载并只获取父项,但随后对于每个子项或子集,EF 在检索期间将执行一个查询。
型号
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public int? DepartmentId { get; set; }
public Department ParentDepartment { get; set; }
public virtual ICollection<Department> ChildDepartments { get; set; }
}
映射(使用流利的)
public DbSet<Department> Departments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// other mapping code
modelBuilder.Entity<Department>()
.HasOptional(x => x.ParentDepartment)
.WithMany(x => x.ChildDepartments)
.HasForeignKey(x => x.DepartmentId);
// other mapping code
}
急切检索根父级
using (var context = new YourDbContext())
{
var allDepartments = context.Departments.ToList(); // eagerly return everything
var rootDepartment = allDepartments.Single(x => x.DepartmentId == null);
}
仅检索根父级然后使用延迟加载,请注意 DbContext 需要可用于延迟加载才能工作,并且还必须在 DbContext 上启用它
using (var context = new YourDbContext())
{
var rootDepartment = context.Departments.Single(x => x.DepartmentId == null);
// do other stuff, as soon as context is disposed you cant lazy load anymore
}
尝试其中之一,
1-
int _ID = 2; // ID criteria
List<object> result = new List<object>(); // we will use this to split parent at child, it is object type because we need Level
var departments = entites.Departments.Where(x => x.ID == _ID).SelectMany(t => entites.Departments.Where(f => f.ID == t.DepartmentID),
(child, parent) => new { departmentID = child.DepartmentID, Name = child.Name, ID = child.ID, level = 0,
Parent = new { DepartmentID = parent.DepartmentID, Name = parent.Name, ID = parent.ID, level = 1 }});
// first we check our ID (we take A from where criteria), then with selectmany T represents the Department A, we need
// department A's departmentID to find its parent, so another where criteria that checks ID == DepartmentID, so we got T and the new list
// basically child from first where parent from second where, and object created.
// for showing the results
foreach (var item in departments)
{
result.Add(new { DepartmentID = item.departmentID,ID = item.ID, level= item.level,Name = item.Name}); // child added to list
result.Add(new { DepartmentID = item.Parent.DepartmentID, ID = item.Parent.ID, level = item.Parent.level, Name = item.Parent.Name }); // parent added to list
}
2-
List<object> childParent = new List<object>();
// basically get the child first
Departments child1 = entites.Departments.Where(x => x.ID == _ID).FirstOrDefault();
// find parent with child object
Departments parent1 = entites.Departments.Where(x => x.ID == child1.DepartmentID).FirstOrDefault();
// create child object with level
childParent.Add(new { child1.DepartmentID, child1.ID,child1.Name , level = 0});
// create parent object with level
childParent.Add(new { parent1.DepartmentID,parent1.ID,parent1.Name, level = 1 });
编辑 1:
3-
另一种方法是,通过将 ID 作为输入并假设 ID 列是唯一的,因此数组中始终有 2 个值,并且通过返回列表,项目的索引实际上代表了它们的级别。 (不会添加结果,因为它们相同 :))。顺便说一句,您也可以使用 Union
而不是 Concat
。
var ress = list.Where(x=> x.ID ==2)
.SelectMany(x=> list.Where(c=> c.ID == x.ID).Concat(list.Where(s => s.ID == x.DepartmentID))).ToList();
DataTable dt = new DataTable();
dt.Columns.Add("DepartmentID");
dt.Columns.Add("ID");
dt.Columns.Add("Name");
dt.Columns.Add("Level");
for (int i = 0; i < ress.Count(); i++)
{
dt.Rows.Add(ress[i].DepartmentID, ress[i].ID, ress[i].Name, i);
}
dataGridView1.DataSource = dt;
编辑 2
linq没有cte,基本都是用view,sp是首选,这里有一个解决办法,可能有点push。无论如何它给出了结果。
List<Departments> childParent = new List<Departments>();
// or basically get the child first
Departments child1 = entites.Departments.Where(x => x.ID == 7).FirstOrDefault();
// find parent with child object
Departments parent1 = entites.Departments.Where(x => x.ID == child1.DepartmentID).FirstOrDefault();
// create child object with level
Departments dep = new Departments(); // I add to department class a string level field
dep.DepartmentID = child1.DepartmentID;
dep.ID = child1.ID;
dep.Name = child1.Name;
dep.level = 0; // first item
childParent.Add(dep);
// create parent object with level
dep = new Departments();
dep.DepartmentID = parent1.DepartmentID;
dep.ID = parent1.ID;
dep.Name = parent1.Name;
dep.level = 1; // parent one
childParent.Add(dep);
while (childParent.Select(t => t.DepartmentID).Last() != null) // after added to list now we always check the last one if it's departmentID is null, if null we need to stop searching list for another parent
{
int? lastDepID = childParent.Last().DepartmentID; // get last departmentID
Departments tempDep = entites.Departments.Single(x => x.ID == lastDepID); // find as object
tempDep.level = childParent.Last().level + 1; // increase last level
childParent.Add(tempDep); // add to list
}
(添加了另一个C1来检查第4级)
希望有所帮助,
下面是简单的控制台项目程序 class 代码。
GetParentSet方法的入参可以用不同的ID来查看。
class Program
{
static void Main(string[] args)
{
Program p = new Program();
var result= p.GetParentSet(6);
foreach(var a in result)
{
Console.WriteLine(string.Format("{0} {1} {2}",a.ID,a.Name,a.DepartmentId));
}
Console.Read();
}
private List<Department> GetParentSet(int id)
{
List<Department> result = new List<Department>(); //Result set
using (RamzDBEntities context = new RamzDBEntities())
{
var nodeList = context.Departments.Where(t=>t.ID<=id).ToList(); //Get All the the entries where ID is below or greater than the given to the list
var item = nodeList.Where(a => a.ID == id).SingleOrDefault(); //Get the default item for the given ID
result.Add(item); //Add it to the list. This will be the leaf of the tree
int size = nodeList.Count(); //Get the nodes count
for (int i = size; i >= 1;i--)
{
var newItem= nodeList.Where(j => j.ID == item.DepartmentId).SingleOrDefault(); //Get the immediate parent. This can be done by matching the leaf Department ID against the parent ID
if (item!=null && !result.Contains(newItem)) //If the selcted immediate parent item is not null and it is not alreday in the list
{
result.Add(newItem); //Add immediate parent item to the list
}
if (newItem.ID == 1) //If the immediate parent item ID is 1 that means we have reached the root of the tree and no need to iterate any more.
break;
item = newItem; //If the immediate parent item ID is not 1 that means there are more iterations. Se the immediate parent as the leaf and continue the loop to find its parent
}
}
return result; //return the result set
}
}
代码本身是不言自明的。但是下面是解释。希望这会有所帮助!
- 首先,ID小于或等于给定ID的所有条目是 分配给列表
- 然后获取树的叶子并将其添加到名为result的列表中。这是我们结果集的第一个元素
- 我们按降序遍历检索到的条目。通过将父 ID 等同于叶的部门 ID 来获取叶的直接父级
- 如果此直接父级不为 null 并且它不在列表中,请将其添加到列表中。
- 将直接父项作为叶子继续循环,这样我们就可以得到直接父项的父项。
- 继续这个直到我们到达树的根。
- 如果直接父 ID=1,这意味着我们已经到达树的根,我们可以打破循环。
由于您生成了 edmx,因此您已经为 DbContext 和模型 Classes 生成了代码,包括 this 屏幕截图中的部门。
您不应修改它们,因为它们可能(将)在任何模型操作中被 EF 工具覆盖。幸运的是,两个 classes 都生成为 partial
,因此创建者考虑了希望安全地自定义它的人们。
下面的示例是为了简化实施而不是为了获得最佳性能。我假设包含 Departments 的 table 不是很大,层次结构中的嵌套级别也不是很深。
在您的项目中创建一个新的 Class(*.cs 文件)并通过您的自定义方法或 属性 扩展自动生成的部门 class:
using System; using System.Collections.Generic; using System.Linq; namespace CustomEF.EFStuff { public partial class Departments { public List<Departments> Hierarchy { get { List<Departments> retVal = new List<Departments>(); retVal.Add(this); using (YourAutoGeneratedContext ctx = new YourAutoGeneratedContext()) { Departments tmp = this; while(tmp.DepartmentID != null) { tmp = ctx.Departments.First(d => d.ID == tmp.DepartmentID); retVal.Add(tmp); } } return retVal; } private set { } } } }
当您扩展部分 class 时,请确保将其放在相同的命名空间中。在我的例子中,我将我的项目命名为 CustomEF,并将 edmx 文件放在 EFStuff 子文件夹中,因此生成器将自动生成的 class 放在
CustomEF.EFStuff
命名空间中。上面的示例将允许您获取任何 Departments 对象的层次结构,例如
int level = 0; foreach(Departments d in someDepartmentObject.Hierarchy) { Console.WriteLine(d.ID.ToString() + ", " + d.DepartmentID.ToString() + ", " + d.Name +", " +(level++).ToString()); }
如果您还需要从一些您有 ID 但没有对象的代码中获取层次结构,您可以另外创建另一个 class(*.cs 文件)我将扩展自动生成的上下文。
using System.Collections.Generic; using System.Linq; namespace CustomEF.EFStuff { public partial class YourAutoGeneratedContext { public List<Departments> GetDepartmentHierarchy(int departmentId) { Departments mydep = this.Departments.FirstOrDefault(d => d.ID == departmentId); if (mydep == null) { throw new System.Data.Entity.Core.ObjectNotFoundException("There is no department with ID = " + departmentId.ToString()); } return mydep.Hierarchy; } } }
或者在这种情况下,您可能希望将实现完全移至上下文 class,而根本不扩展部门 class(并且您不必创建额外的实例您的上下文,您将可以使用
this
。using System.Collections.Generic; using System.Linq; namespace CustomEF.EFStuff { public partial class YourAutoGeneratedContext { public List<Departments> GetDepartmentHierarchy(int departmentId) { Departments tmp = this.Departments.FirstOrDefault(d => d.ID == departmentId); if (tmp == null) { throw new System.Data.Entity.Core.ObjectNotFoundException("There is no department with ID = " + departmentId.ToString()); } List<Departments> retVal = new List<Departments>(); retVal.Add(tmp); while (tmp.DepartmentID != null) { tmp = this.Departments.First(d => d.ID == tmp.DepartmentID); retVal.Add(tmp); } return retVal; } } }
作为另一个简单的使用示例:
YourAutoGeneratedContext ctx = new YourAutoGeneratedContext(); level = 0; foreach (Departments currentHier in ctx.GetDepartmentHierarchy(10)) { Console.WriteLine(currentHier.ID.ToString() + ", " + currentHier.DepartmentID.ToString() + ", " + currentHier.Name + ", " + (level++).ToString()); }
我不知道您对数据库中的数据有多信任。您可能需要执行一些检查,包括交叉引用部门以防止无限循环。
请注意,正式术语 'to extend a class' 可能适用于扩展方法,而不是 partial
classes。我用这个词是因为找不到更好的词了。如果出于某种原因,您需要 method/property 返回 EF 本机 DbSet<>
而不是 List<>
,那么您可能想要使用扩展方法。在这种情况下,您可能需要查看:https://shelakel.co.za/entity-framework-repository-pattern/
这些 post 与您的 question.please 相似,请参阅:
writing-recursive-cte-using-entity-framework-fluent-syntax-or-inline-syntax
converting-sql-statement-that-contains-with-cte-to-linq
我认为没有办法编写单个 LINQ to SQL 查询来获取所有的查询 但是,LINQ 支持执行查询的方法(奇怪的是称为 DataContext.ExecuteQuery)。看起来您可以使用它来调用 SQL 的任意片段并将其映射回 LINQ。
看到这个post: common-table-expression-in-entityframework
在 EF6 中获取所有父节点到根节点的示例。
public class Department
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public virtual Department Parent { get; set; }
public virtual ICollection<Department> Children { get; set; }
private IList<Department> allParentsList = new List<Department>();
public IEnumerable<Department> AllParents()
{
var parent = Parent;
while (!(parent is null))
{
allParentsList.Add(parent);
parent = parent.Parent;
}
return allParentsList;
}
}
使用 include 关键字。
_context.Invoices.Include(x => x.Users).Include(x => x.Food).ToList();