How do I select recursive nested entities using LINQ to Entity

后端 未结 3 1495
臣服心动
臣服心动 2020-12-31 12:56

I have an entity called Category and the entity contains a IEnumerable called ChildCategories. A category can have these child categories which can have it\'s own child cate

3条回答
  •  醉酒成梦
    2020-12-31 13:57

    You won't be able to do something like this with just LINQ alone; LINQ doesn't have any support for traversing an unknown level of nodes out-of-the-box.

    Additionally, you don't have any real way of flattening the structure, the number of properties that is required is unknown (as it's tied to the tree depth, which is also unknown).

    I'd recommend using iterators in C# to flatten the tree, something like this:

    static IEnumerable Flatten(this IEnumerable source, 
        Func> childrenSelector)
    {
        // Do standard error checking here.
    
        // Cycle through all of the items.
        foreach (T item in source)
        {
             // Yield the item.
             yield return item;
    
             // Yield all of the children.
             foreach (T child in childrenSelector(item).
                 Flatten(childrenSelector))
             {
                 // Yield the item.
                 yield return child;
             }            
        }
    }
    

    Then, you can call the extension method and place the results in a List; it's about as flat as you are going to get.

    Note, you could very easily throw a StackOverflowException if the hierarchy is deep enough. To that end, you'd really want to use this non-recursive method:

    static IEnumerable Flatten(this IEnumerable source, 
        Func> childSelector)
    {
        // Do standard error checking here.
    
        // Create a stack for recursion.  Push all of the items
        // onto the stack.
        var stack = new Stack(source);
    
        // While there are items on the stack.
        while (stack.Count > 0)
        {
            // Pop the item.
            T item = stack.Pop();
    
            // Yield the item.
            yield return item;
    
            // Push all of the children on the stack.
            foreach (T child in childSelector(item)) stack.Push(child);
        }
    }
    

    The Stack instance lives on the heap and not on the call stack, so you won't run out of call stack space.

    Also, you can change the Stack to Queue if you want different return semantics (or you can traverse through the children in different ways) if you require a certain order.

    If you need a very specific order, I'd only recommend changing the ordering in the method if you have a large number of items that need to be traversed which makes calling OrderBy on the return value prohibitive.

提交回复
热议问题