使用C# LINQ过滤三层嵌套集合并保留原层级结构的技术实现
Let's fix this nested filtering issue step by step. Your current implementation uses SelectMany which flattens the hierarchy, resulting in just a list of matching Workflow objects instead of preserving the parent Repo and Collection structure. Here's how to adjust the logic to keep the full nested hierarchy while filtering correctly:
Key Requirements Recap
We need to:
- Match any level's
Nameproperty (Collection/Repo/Workflow) against the filter string (case-insensitive, based on your example) - Keep only matching items and their parent hierarchy
- Maintain the original nested structure (
List<Collection> → List<Repo> → List<Workflow>)
Correct LINQ Implementation
First, normalize the filter string to lowercase to ensure consistent case-insensitive matching:
string filterLower = FilterString.ToLower();
Then, build the filtered hierarchy by iterating from the innermost layer outwards, and only retaining parent nodes if they either match the filter themselves or have child nodes that do:
var filteredCollections = AllCollections .Select(collection => new Collection { Name = collection.Name, // Filter Repos for this Collection Repos = collection.Repos .Select(repo => new Repo { Name = repo.Name, // Filter Workflows for this Repo Workflows = repo.Workflows .Where(workflow => workflow.Name.ToLower().Contains(filterLower)) .ToList() }) // Keep Repos that either match the filter OR have matching Workflows .Where(repo => repo.Name.ToLower().Contains(filterLower) || repo.Workflows.Any()) .ToList() }) // Keep Collections that either match the filter OR have matching Repos .Where(collection => collection.Name.ToLower().Contains(filterLower) || collection.Repos.Any()) .ToList();
How This Works
Let's walk through your example where FilterString = "Copy":
- Workflow Layer: Only the
Workflownamed "Copy" in the "VisualBasic" Repo matches, so it's retained. - Repo Layer: The "VisualBasic" Repo is kept because it has a matching Workflow. All other Repos are filtered out since none of their Workflows match and their names don't include "Copy".
- Collection Layer:
CollectionBis kept because it has a retained Repo.CollectionAis filtered out entirely since none of its Repos or their Workflows match.
The final result is a List<Collection> containing only CollectionB, which contains only the "VisualBasic" Repo, which contains only the "Copy" Workflow—exactly the structure you expected.
Why Your Original Code Failed
Your original code used SelectMany twice, which flattens the nested collections into a single sequence of Workflow objects. This discards all parent context (Repo and Collection), which is why you lost the hierarchy.
Entity Class Reference
For completeness, here are the entity definitions you provided:
public class Collection { public string Name { get; set; } public List<Repo> Repos { get; set; } } public class Repo { public string Name { get; set; } public List<Workflow> Workflows { get; set; } } public class Workflow { public string Name { get; set; } }
内容的提问来源于stack exchange,提问作者fuzzybear

