2

Class structure

public clas Item 
{
   public Item Parent { get; set; }

   public string Code { get; set; }
}

example tree

AAA
 - AAB
    - BBB
CCC
 - CCA

So i want to filter tree by CODE == BBB and result should be

AAA
 - AAB
    - BBB

but if i filter like this

IQueryable<Item> itemsQuery = GetQuery();
itemsQuery = itemsQuery.Where(x => x.Code == "BBB")

result does not contain parent nodes. So, how to include parent nodes, if their child nodes satisfy certain conditions?

Tamir Vered
  • 10,187
  • 5
  • 45
  • 57
Maxim
  • 1,413
  • 2
  • 10
  • 15

3 Answers3

0

In simple way, you cannot get recursive tree using EF. EF returns flat collections. However, there are workarounds.

Variant 1:

Add to your Item public Item Root { get; set; } and public ICollection<Item> AllSiblings { get; set; } property, which points all items to the actual root and second is the other way (all nested items).

The query than make look like:

IQueryable<Item> itemsQuery = GetQuery().Include(x => x.AllSiblings);
itemsQuery = itemsQuery.Where(x => x.Code == "BBB" || x.AllSiblings.Any(s => s.Code == "BBB")).ToList();

Now you have all items in your app and you can than recursively make the tree in C#.

Variant 2:

You can make several SQL queries to get each parent of found items. This is not recommended, because it will get very slow on more found results.

Zapo
  • 211
  • 1
  • 6
0

It is hard to apply LinQ here since there is no reference from parent items to children. What about doing simple enumeration to parent after applying filter? It will give you list of all matched items in the tree and then you'll might need to print it in a "tree" manner. Here is a BFS example

        IQueryable<Item> itemsQuery = items.AsQueryable();
        itemsQuery = itemsQuery.Where(x => x.Code == "BBB");

        var bfsQueue = new Queue<Item>(itemsQuery);
        var matchedItemsSet = new HashSet<Item>();

        while (bfsQueue.Count > 0) {
            var item = bfsQueue.Dequeue();
            matchedItemsSet.Add(item);

            var parent = item.Parent;
            if (parent != null && !matchedItemsSet.Contains(parent))
            {
                bfsQueue.Enqueue(parent);
            }
        }

        foreach (var item in matchedItemsSet) {
            Console.WriteLine(item.Code);
        }
Tom Kris
  • 1,227
  • 1
  • 8
  • 15
0

I prefer universal approaches.

public static IEnumerable<T> SelectUntil<T>(this T element, Func<T, T> nextMemberSelector, Func<T, bool> stopCondition)
{
    while (!stopCondition(element))
    {
        yield return element;
        element = nextMemberSelector(element);
    }
}
public static IEnumerable<Item> GetAncestors(this Item e)
{
    // Or don't Skip(1) if you need the child itself included.
    return e.SelectUntil(T => T.Parent, T => T.Parent == null).Skip(1);
}

private static void Main(string[] args)
{
    IEnumerable<Item> itemsQuery = GetQuery();
    IEnumerable<Item> filter = itemsQuery.Where(T => T.Code == "BBB");

    foreach (Item item in filter)
    {
        Item[] allParents = item.GetAncestors().ToArray();
    }

}
AgentFire
  • 8,944
  • 8
  • 43
  • 90