So I have a SQL table which is basically
ID, ParentID, MenuName, [Lineage, Depth]
The last two columns are auto-computed to help with searching so we can ignore them for now.
I'm creating a drop down menu system with multiple categories.
Unfortunately EF I don't think plays nice with Self referencing tables more than 1 level deep. So I'm left with a few options
1) Create query, order by depth and then create a custom class in C#, populating it one depth at a time.
2) Find some way to eager load the data in EF, I don't think it is possible for an unlimited amount of levels, only a fixed amount.
3) Some other way I'm not even sure about.
Any inputs would be welcomed!
I knew that there must be something wrong with this solution. It is not simple. Using this solution, EF6 require another package of hacks to manage a simple tree (fe. deletions). So finally I've found a simple solution but combined with this approach.
First of all leave entity simple: just Parent and list of Children is enough. Also mapping should be simple:
Then add migration (code first: migrations: package console: Add-Migration Hierarchy) or in other ways a stored procedure:
Then when you will try to receive your tree nodes from database just do it in two steps:
It all. This query helps you to get a root node and load all children. Without playing with introducing Ancestors and Descendants.
Remember also when you will try to save sub-node, then do it just this way:
Do it that way, not by adding children to root node.
@danludwig thanks for your answer
I write some function for update Node, It work's perfect. My code is it good or I should write it in other way?
I have successfully mapped hierarchical data using EF.
Take for example an
Establishment
entity. This can represent a company, university, or some other unit within a larger organizational structure:Here is how the Parent / Children properties are mapped. This way, when you set the Parent of 1 entity, the Parent entity's Children collection is automatically updated:
Note that so far I haven't included your Lineage or Depth properties. You are right, EF doesn't work well for generating nested hierarchical queries with the above relationships. What I finally settled on was the addition of a new gerund entity, along with 2 new entity properties:
While writing this up, hazzik posted an answer that is very similar to this approach. I'll continue writing up though, to provide a slightly different alternative. I like to make my Ancestor and Offspring gerund types actual entity types because it helps me get the Separation between the Ancestor and Offspring (what you referred to as Depth). Here is how I mapped these:
... and finally, the identifying relationships in the Establishment entity:
Also, I did not use a sproc to update the node mappings. Instead we have a set of internal commands that will derive / compute the Ancestors and Offspring properties based on the Parent & Children properties. However ultimately, you end up being able to do some very similar querying as in hazzik's answer:
The reason for the bridge entity between the main entity and its Ancestors / Offspring is again because this entity lets you get the Separation. Also, by declaring it as an identifying relationship, you can remove nodes from the collection without having to explicitly call DbContext.Delete() on them.
You could use supporting hierarchy table to do eager loading of unlimited levels of tree.
So, you need to add two collections
Ancestors
andDescendants
, both collection should be mapped as many-to-many to supporting table.Ancestors will contain all ancestors (parent, grand-parent, grand-grand-parent, etc.) of the entity and
Descendants
will contain all the descendants (children, grand-children, grand-grand-children, etc) of the entity.Now you have to map it with EF Code First:
Now with this structure you could do eager fetch like following
This query will load entity with
id
and all of it descenadnts.You could populate the supporting table with following stored procedure:
Or even you could map supporting table to a view:
I've already spent a while trying to fix a bug in your solution. The stored procedure really don't generate children, grandchildren, etc. Below you will find fixed stored procedure:
Mistake: wrong reference. Translating @hazzik code it was:
but should be
also I've added code that allows you to update TreeHierarchy table not only when you will populate it.
And the magic. This procedure or rather TreeHierarchy allows you to load Children just by including Ancestors (not Children and not Descendants).
Now the YourDbContext will return a rootNode with loaded children, children of rootName's children (grandchildren), and so on.
Another implementation option that I've recently worked on...
My tree is very simple.
My requirements, not so much.
An analogy would be a file structure on disk. The current user has access to a subset of files on the system. As the user opens nodes in the file system tree, we only want to show that user nodes that will, eventually, lead them to the files they can see. We don't want to show them file paths to files they do not have access to (for security reasons, e.g., leaking the existence of a document of a certain type).
We want to be able to express this filter as an
IQueryable<T>
, so we can apply it to any node query, filtering out unwanted results.To do this, I created a Table Valued Function that returns the descendants for a node in the tree. It does this via a CTE.
Now, I'm using Code First, so I had to use
https://www.nuget.org/packages/EntityFramework.Functions
in order to add the function to my DbContext
with a complex return type (couldn't reuse Node, looking into that)
Putting it all together allowed me, when the user expands a node in the tree, to get the filtered list of child nodes.
It's important to note that the function is executed on the server, not in the application. Here's the query that gets executed
Note the function call within the query above.