如何使用LINQ对1表返回父级和子级

一直在寻找解决方案,但到目前为止还没找到。

我很确定它可以通过一个linq调用但是无法解决它。

我有以下数据结构

Id ParentId Name ValidFlag 1 NULL parent 1 1 2 NULL parent 2 1 3 NULL parent 3 0 4 1 child 1 1 5 1 child 2 1 6 2 child 3 1 7 2 child 4 1 8 3 child 5 1 9 3 child 6 1 

现在我要做的是返回所有有效的父母和他们的孩子,所以这种情况下我将返回除Id = 3(父母3)之外的所有内容。

使用LINQ有一种简单的方法吗? 我猜测有,但我的LinqIQ非常有限,我知道基础知识,但除此之外,我需要很多帮助。

这是ToLookup()或Union的情况吗?

更新:

更具体的是,由于我没有这样做,两种类型的记录都在同一个表中,我想在可能的情况下返回所有记录,无论它是父查询还是子查询。

它并不像选择ValidFlag = 1的所有记录那么简单。源数据库是一团糟,获取所有记录的唯一方法是找到“有效”父母,然后找到“有效”父母的所有子项。 我知道我可以只做一个简单的查询来返回所有有效的父记录,然后单独查询以查找这些父项的所有子项,但合并到1 LINQ查询是我失败的地方,我希望这是可能的。 在这种情况下,可能存在无效父母的有效子条目,因此需要该问题

这应该可以解决问题,(编辑:请参阅下面的不使用Distinct的版本。)

 (from parents in collection from all in collection where parents.ValidFlag == 1 && parents.ParentId == null && all.ValidFlag == 1 && (all.ParentId == null || all.ParentId == parents.Id) select all).Distinct(); 

上面的代码应该有希望生成一些与它本身在SQL中看起来非常相似的东西,可能除了distinct之外可能会导致它返回实际需要在客户端上过滤的更多数据。 如果有大量数据,可能会成为一个问题,主要是如果有很多父母,因为它会返回重复的那些)

这是在没有明确调用的情况下重新编写的查询

 from parents in collection // parents is only used to decide which children to get from all in collection // this is where we will actually grab our data from where parents.ValidFlag == 1 && // only include parents that are valid parents.ParentId == null && // and that are parents all.ValidFlag == 1 && // only include entries that are valid ( (all.ParentId == null && all.Id == parents.Id) || // If entry is a parent, match with itself to limit returns to 1 all.ParentId == parents.Id // otherwise, parentid should match one of the valid parents. ) select all 

这应该做到这一点。 创建包含该数据结构的对象类型的通用列表。 然后使用.Where扩展,它返回相同类型的IEnumerable。

  List list = new List(); IEnumerable validItems = list.Where(x=>x.ValidFlag=1); 

考虑以下Linq to Sql实体:

在此处输入图像描述

假设我们已经将OneToMany关系的两侧命名为ChildTablesParentTables ,那么以下代码应该可以完成这项工作

 //create data context MyTableDataContext dc = new MyTableDataContext("Your connection string"); //find all children, ie the entities with ParentId set var allChildEntities = dc.MyTable.Where(t=>t.ParentId.HasValue); //find all valid parents, which have no parent and no children var allParentsWithChild = dc.MyTable.Where(c => !c.ParentId.HasValue && !c.ChildTables.Any()); //union the results var result = allChildEntities.Union(allParentsWithChild); 

如果IdParentId之间存在外键关系,那就足够了。 如果没有,你也应该搜索没有父母的孩子。 但是使用纯sql可能会更容易

我打算使用GroupJoin,但这应该满足你的要求。

  var query = dataContext.YourTable.Where(x => x.ValidFlag == 1 && (x.ParentId == null || dataContext.YourTable.Where( y => y.ParentId == x.Id) .First().ValidFlag == 1)) .ToList(); . 

对于您的示例数据,这将起作用:

  var validData = from d in data where (!d.ParentID.HasValue && d.IsValid) //select all valid parents || (d.ParentID.HasValue && data.Where(p => !p.ParentID.HasValue && p.IsValid).Select(p => p.ID).Contains(d.ParentID.Value)) //select children select d; 

但如果您的数据中存在多级层次结构,并且您也想要选择子子级,则它将无法工作。

另外,我不确定上述内容是否适用于linq-to-sql或其他linq提供程序,但它确实适用于内存数据。

如果您正在使用Entity Framework并具有导航属性,则可以执行以下操作。 但问题是否是这种情况并不清楚。

 var query = db.YourTable .Where(x => x.Parent != null && x.Parent.ValidFlag == 1) .GroupBy(x => x.ParentId) .Select(g => new { ParentId = g.Key, Children = g.ToList() }) .ToList(); 

考虑到这种“错误”的方式,你想要的SQL是:

 SELECT * FROM MyTable WHERE IsValid = 1 AND (ParentID IS NULL -- Parents OR ParentID IN (SELECT ID FROM MyTable WHERE IsValid = 1 AND ParentID IS NULL)) -- Children 

所以你想要的LINQ是:

 var result = from d in MyTable where d.ValidFlag == 1 && (d.ParentId == null || (from p in MyTable where p.ValidFlag == 1 && p.ParentId == null && p.Id == d.ParentId select p.Id).Any()) select d; 

(不完全相同的SQL,但实际上是这样。)