I have a tree structure in the DB with TreeNodes table. the table has nodeId, parentId and parameterId. in the EF, The structure is like TreeNode.Children where each child is a TreeNode... I also have a Tree table with contain id,name and rootNodeId.
At the end of the day I would like to load the tree into a TreeView but I can't figure how to load it all at once. I tried:
var trees = from t in context.TreeSet.Include("Root").Include("Root.Children").Include("Root.Children.Parameter")
.Include("Root.Children.Children")
where t.ID == id
select t;
This will get me the the first 2 generations but not more. How do I load the entire tree with all generations and the additional data?
For an example of loading in child objects, I'll give the example of a Comment object that holds a comment. Each comment has a possible child comment.
Now if you were having something that has collections of itself you would need to use Collection instead of Reference and do the same sort of diving down. At least that's the approach I took in this scenario as we were dealing with Entity and SQLite.
This is an old question, but the other answers either had n+1 database hits or their models were conducive to bottom-up (trunk to leaves) approaches. In this scenario, a tag list is loaded as a tree, and a tag can have multiple parents. The approach I use only has two database hits: the first to get the tags for the selected articles, then another that eager loads a join table. Thus, this uses a top-down (leaves to trunk) approach; if your join table is large or if the result cannot really be cached for reuse, then eager loading the whole thing starts to show the tradeoffs with this approach.
To begin, I initialize two
HashSet
s: one to hold the root nodes (the resultset), and another to keep a reference to each node that has been "hit."Next, I grab all of the leaves that the client requested, placing them into an object that holds a collection of children (but that collection will remain empty after this step).
Now, let's grab the tag self-join table, and load it all into memory:
Now, for each tag in startingTags, add that tag to the allTags collection, then travel down the tree to get the ancestors recursively:
Lastly, here's the nested recursive method that builds the tree:
Under the covers, I am relying on the properties of a
HashSet
to prevent duplicates. To that end, it's important that the intermediate object that you use (I used AncestralTagDto here, and its Children collection is also aHashSet
), override the Equals and GetHashCode methods as appropriate for your use-case.When you use
Include()
, you are asking the Entity Framework to translate your query into SQL. So think: How would you write an SQL statement which returns a tree of an arbitrary depth?Answer: Unless you are using specific hierarchy features of your database server (which are not SQL standard, but supported by some servers, such as SQL Server 2008, though not by its Entity Framework provider), you wouldn't. The usual way to handle trees of arbitrary depth in SQL is to use the nested sets model rather than the parent ID model.
Therefore, there are three ways which you can use to solve this problem:
I wanted to post up my answer since the others didn't help me.
My database is a little different, basically my table has an ID and a ParentID. The table is recursive. The following code gets all children and nests them into a final list.
For completeness, here's my model:
I wrote something recently that does N+1 selects to load the whole tree, where N is the number of levels of your deepest path in the source object.
This is what I did, given the following self-referencing class
I wrote the following DbSet helper
Whenever you need to load a whole tree you simply call this method, passing in three things
For example
Alternatively, if you can add a
RootId
column to the table then for each non-root entry you can set this column to the ID of the root of the tree. Then you can fetch everything with a single selectDataContext.SomeEntity.Where(x => x.Id == rootId || x.RootId == rootId)
I had this problem recently and stumbled across this question after I figured a simple way to achieve results. I provided an edit to Craig's answer providing a 4th method, but the powers-that-be decided it should be another answer. That's fine with me :)
My original question / answer can be found here.
This works so long as your items in the table all know which tree they belong to (which in your case it looks like they do:
t.ID
). That said, it's not clear what entities you really have in play, but even if you've got more than one, you must have a FK in the entityChildren
if that's not aTreeSet
Basically, just don't use
Include()
:This will bring back ALL the items for the tree and put them all in the root of the collection. At this point, your result set will look like this:
Since you probably want your entities coming out of EF only hierarchically, this isn't what you want, right?
.. then, exclude descendants present at the root level:
Fortunately, because you have navigation properties in your model, the child entity collections will still be populated as you can see by the illustration of the result set above. By manually iterating over the result set with a
foreach()
loop, and adding those root items to anew List<TreeSet>()
, you will now have a list with root elements and all descendants properly nested.If your trees get large and performance is a concern, you can sort your return set ASCENDING by
ParentID
(it'sNullable
, right?) so that all the root items are first. Iterate and add as before, but break from the loop once you get to one that is not null.And now
subset
will look like this:About Craig's solutions: