If you need to go through the whole tree, you must use a stored procedure. Entity Framework is especially bad for recursive relationships. You need to either issue N + 1 queries for each level, or load a specific set of levels. For example, .Include("Childs.Childs.Childs") load three levels. However, this will create a monstrous request, and you still have to issue N + 1 requests for any additional level that you do not include at the beginning.
In SQL, you can use WITH to recursively traverse a table, and it will be much faster than anything the Entity Framework can do. However, your result will be flattened, not the graphic you get from the Entity Framework. For instance:
DECLARE @Pad INT = ( SELECT MAX([Length]) FROM ( SELECT LEN([Order]) AS [Length] FROM [dbo].[Menus] ) x ); WITH Tree ([Id], [ParentId], [Name], [Hierarchy]) AS ( SELECT [ID], [ParentID], [MenuName], REPLICATE('0', @Pad - LEN([Order])) + CAST([Order] AS NVARCHAR(MAX)) FROM [dbo].[Menus] WHERE [ParentID] = 0
It looks a lot harder than it is. To ensure that the parents arrange the menu items correctly and their position inside this parent tree, we need to create a hierarchical order view for the order. I do this here by creating a line in the form 1.1.1 , where essentially every order of the elements is added to the end of the line of the parent hierarchy. I also use REPLICATE to align the order for each level, so you have no problems with string ordering of numbers where something like 10 precedes 2 because it starts with 1 . The @Pad ad just gets the maximum length I need to fill out based on the highest order number in the table. For example, if the maximum order was something like 123 , then the @Pad value would be 3, so orders less than 123 would still have three characters (i.e. 001 ).
Once you go through all this, the rest of SQL is pretty straight forward. You simply select all the root elements, and then combine them with your whole child, walking through the tree. This is the connection of each new level. Finally, you select from this tree the information you need, sorted by the hierarchy ordering line that we created.
At least for my trees, this query is reasonably fast, but it may be slightly slower than you might like if there is a scale of complexity or there are a ton of menu items you need to deal with. It’s a good idea to do some kind of tree caching, even using this request. Personally, for something like a site navigator, I would recommend using a child action in conjunction with OutputCache . You call a child action in your layout, where the navigator should appear, and it will either launch the action to get the menu, or get the HTML code already created from the cache, if it exists. If the menu is intended for individual users, just make sure that you change differently as you wish, and take into account the user ID or something in your user line. You could also just cache memory as a result of the request itself, but you could also reduce the cost of creating HTML, also when you are on it. However, you should avoid storing it in a session.