使用LINQ查询嵌套类型的C#代码

4

我有一个如下所示的模型:

public class MyType{
 public string Id {get;set;}
 public string Name{get;set;}
 public List<MyType> Children{get;set;}
}

在我的数据中,我只有两个层级的数据,这意味着我的对象看起来像:

{
 MyType{"1","firstParent",
 {
  MyType{"2","firstChild",null},
  MyType{"3","secondChild",null}}
 },

 MyType{"4","secondParent",
 {
  MyType{"5","firstChild",null},
  MyType{"6","secondChild",null}}
 }
}

我该如何查询具有特定ID的MyType对象,其中它可能是父对象或子对象?
以下代码仅返回父对象。
collection.FirstOrDefault(c => c.id==id)

通常在数据库中,您会有一个Id和一个ParentId,以便您可以从中构建树形结构。我没有在您的数据中看到这些信息? - Rob
你怎么知道我的数据库是 SQL 数据库? - Yahya Hussein
我不假设那个。无论你使用什么样的数据库,你都必须在某个地方定义你的对象的层次结构。 - Rob
类型为“List<MyType>”的属性名称是什么? - Alexander
4个回答

7
您可以使用Any和递归本地函数来查找任何级别上的对象(您的数据结构似乎表明更深层次是可能的)。
bool hasIdOrChildren(MyType t, string localId)
{
    return t.Id == localId || (t.Children != null && t.Children.Any(o => hasIdOrChildren(o, localId)));
}
collection.FirstOrDefault(c => hasIdOrChildren(c, id));

或使用C#7之前的语法:

Func<MyType, string, bool> hasIdOrChildren = null;
hasIdOrChildren = (MyType t, string localId) =>
{
    return t.Id == localId || (t.Children != null && t.Children.Any(o => hasIdOrChildren(o, localId)));
};
collection.FirstOrDefault(c => hasIdOrChildren(c, id));

如果您只对一个级别感兴趣,可以取消隐私保护:
collection.FirstOrDefault(c => c.Id == id || (c.Children != null && c.Children.Any(o => o.Id == id)));

编辑

以上代码会返回拥有id的任意子元素的父元素,你也可以使用SelectMany和递归函数来展开整个树形结构:

IEnumerable<MyType> flattenTree(MyType t)
{
    if(t.Children == null)
    {
        return new[] { t };
    }
    return new[] { t }
        .Concat(t.Children.SelectMany(flattenTree));
};
collection
    .SelectMany(flattenTree)
    .FirstOrDefault(c => c.Id == id);

这种方法适用于需要将树形结构展平的任何类型处理。


2
这将只返回父对象,对吗?如果子对象具有正确的ID,难道他不想返回它们吗? - Onyx Caldin
@OnyxCaldin 是的,我的错,一开始没有理解.. 我也添加了一个适用版本,非常感谢 :) - Titian Cernicova-Dragomir

3
你可以构建一个包括所有子元素的 MyType 列表,然后像这样进行查询:
collection.SelectMany(c => c.Children).Concat(collection).Where(c => c.id == id)

1
我认为您正在寻找的是:


var flattenedList = IEnumerable.SelectMany(i => i.ItemsInList);

这将压平列表并返回一个包含所有项目的列表。 在您的情况下,您需要选择


collection.SelectMany(c => c.Type).Concat(collection).Where(item => item.Id == 5);

MSDN

你仍然可以在这里找到你的父母关系中的孩子,但你也可以将它们删除或忽略。


1

我认为,你应该使用SelectMany方法来扁平化collection,然后使用FirstOrDefaultid获取元素:

MyType selected = collection
    .SelectMany(obj => new MyType[] {obj, obj.NestedList})
    .FirstOrDefault(obj => obj.id == id);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接