如何使用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
关系的两侧命名为ChildTables
和ParentTables
,那么以下代码应该可以完成这项工作
//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);
如果Id
和ParentId
之间存在外键关系,那就足够了。 如果没有,你也应该搜索没有父母的孩子。 但是使用纯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,但实际上是这样。)