Click here to Skip to main content
15,886,963 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hello!

I recently migrated to .net 6 and to a new EF version.
The tree model in my code is not working as intended.
In my data, I have several stores, several cities and several countries.

They should be displayed as the following:

HTML
---> Country 1
   ---> City 1
      --->Store 1
      --->Store 2
      --->Store 3
      ---Etc....
   ---> City 2
      --->Store 1
      --->Store 2
      --->Store 3
      ---Etc....
---> Country 2
   ---> City 1
      --->Store 1
      --->Store 2
      --->Store 3
      ---Etc....
   ---> City 2
      --->Store 1
      --->Store 2
      --->Store 3
      ---Etc....
---> Country 3
   ---> City 1
      --->Store 1
      --->Store 2
      --->Store 3
      ---Etc....
   ---> City 2
      --->Store 1
      --->Store 2
      --->Store 3
      ---Etc....



But the data is currently being displayed as follows:

HTML
---> Country 1
   ---> City 1
      --->Store 1
---> Country 1
   ---> City 1
      --->Store 2
---> Country 1
   ---> City 2
      --->Store 1
---> Country 1
   ---> City 2
      --->Store 2
---> Country 2
   ---> City 1
      --->Store 1
---> Country 2
   ---> City 1
      --->Store 2


So instead of having countries grouped together with cities also grouped under the country, it is showing each row by itself on a new seperate node of the tree.

How can I fix this issue? I suspect it might be caused by EF but what I tried didn't fix it.

Below is the code used:

What I have tried:

C#
var foundedTradeCenters = _tradeCenterService
                .FindTradeCenters(request.FilterByName, request.FilterByManager)
                .Include(tc => tc.City.Cluster.Region.Country)
                .ToList();

            var tcGroup = foundedTradeCenters
                .GroupBy(g => g.City)
                .GroupBy(g => g.Key.Cluster.Region.Country)
                .OrderBy(g => g.Key.Name)
                .AsEnumerable();
            
            var result = tcGroup
                .Select(s => new TreeModel()
                {
                    Text = s.Key.Name,
                    Type = TreeModel.NodeTypeName,
                    Children = s.Select(c => new TreeModel()
                    {
                        Text = c.Key.Name,
                        Type = TreeModel.NodeTypeName,
                        Children = c.Select(t => new TreeModel()
                        {
                            Id = t.Id,
                            Type = TreeModel.LeafTypeName,
                            Text = t.Name
                        }).OrderBy(n => n.Text)
                    }).OrderBy(n => n.Text)
                });

            return Json(result);


tcGroup variable results is in the following 2 images: tcGroup Image[^]

The result variable: results [^]

In the result, the country is being repeated with each new leaf, I only have 3 countries, but 1500 stores to display. so Instead of having just 3 countries at the beginning to expand, I am getting these 3 countries being repeated for 1500 times to expand.
Posted
Updated 28-Jun-23 13:15pm
Comments
[no name] 28-Jun-23 12:04pm    
EF is for querying; not "reporting" per se. You have a classic "master-detail" report suitable for just about any "report writer"; but not a query language - it's not intuitive enough. You have a recursive structure of "simple" location data; and using "duplicate keys" (in your example) doesn't help the mental model.

I am not sure what your relation database table hierarchical setup is, so I will focus on a typical setup.

The data is stored in a flat structure, usually with a pointer to a parent node. So something like this:
C#
class Data
{
    public int Id { get; set; }
    public int ParentId { get; set; }
    public DataType DataType { get; set; }
    public string Title { get; set; }
}

enum DataType
{
    Country,
    City,
    Store
}

To map to a hierarchical dataset, the parent usually contains the children for that node. Something like this:
C#
class Node
{
    public Data Data { get; set; }
    public int Level { get; set; }
    public List<Node>? Children { get; set; }
}

I am not going to create an EF DB + table, so I will emulate a dataset:
C#
List<Data> FlatData = GetData().ToList();

IEnumerable<Data> GetData()
{
    for (int i = 0; i < 10; i++)
        yield return new Data
        {
            DataType = DataType.Country,
            Id = i,
            ParentId = -1,
            Title = $"Country {i}"
        };

    for (int i = 1000; i < 1020; i++)
        yield return new Data
        {
            DataType = DataType.City,
            Id = i,
            ParentId = random.Next(10),
            Title = $"City {i}"
        };

    for (int i = 2000; i < 2100; i++)
        yield return new Data
        {
            DataType = DataType.Store,
            Id = i,
            ParentId = random.Next(1000, 1020),
            Title = $"Store {i}"
        };
}

Now that we have the data, we can build the hierarchical dataset:
C#
List<Node> NodeData = BuildHierarchy(FlatData).ToList();

IEnumerable<Node>? BuildHierarchy(List<Data> flatData, int parentId = -1, int level = 1)
{
    foreach (Data data in flatData.Where(item => item.ParentId == parentId))
    {
        yield return new Node
        {
            Data = data,
            Level = level,
            Children = BuildHierarchy(flatData, data.Id, level + 1)?.ToList()
        };
    }
}

Lastly, we can now look at the hierarchical dataset:
C#
OutputHierarchy(NodeData);

void OutputHierarchy(List<Node> nodeData)
{
    foreach (Node node in nodeData)
    {
        Console.WriteLine(node.Data.Title.PadLeft((node.Level - 1) * 2 +
                                                  node.Data.Title.Length));
        if (node.Children?.Any() == true)
            OutputHierarchy(node.Children);
    }
}

and the output:
Country 0
  City 1017
    Store 2040
    Store 2063
    Store 2070
Country 1
  City 1003
    Store 2000
    Store 2006
    Store 2010
    Store 2055
    Store 2081
    Store 2088
    Store 2097
  City 1008
    Store 2046
    Store 2074
    Store 2075
    Store 2092
    Store 2098
Country 2
  City 1002
    Store 2002
    Store 2008
    Store 2024
    Store 2094
  City 1007
    Store 2005
    Store 2029
    Store 2041
    Store 2065
    Store 2089
  City 1011
    Store 2011
    Store 2028
    Store 2038
    Store 2042
    Store 2044
    Store 2072
    Store 2077
  City 1012
    Store 2026
    Store 2061
    Store 2068
    Store 2086
    Store 2099
Country 3
  City 1010
    Store 2020
    Store 2027
    Store 2033
    Store 2034
    Store 2052
    Store 2093
Country 4
  City 1001
    Store 2039
    Store 2073
    Store 2087
  City 1015
    Store 2051
    Store 2064
    Store 2071
    Store 2095
  City 1016
    Store 2025
    Store 2030
  City 1018
    Store 2015
    Store 2021
    Store 2036
    Store 2085
    Store 2091
Country 5
  City 1000
    Store 2003
    Store 2031
    Store 2058
Country 6
  City 1006
    Store 2032
    Store 2035
    Store 2037
    Store 2048
    Store 2059
    Store 2067
  City 1014
    Store 2001
    Store 2023
    Store 2043
Country 7
  City 1005
    Store 2012
    Store 2016
    Store 2018
    Store 2022
    Store 2054
    Store 2056
    Store 2057
    Store 2078
Country 8
  City 1019
    Store 2007
    Store 2009
    Store 2047
    Store 2060
Country 9
  City 1004
    Store 2049
    Store 2050
    Store 2062
    Store 2066
    Store 2080
    Store 2082
    Store 2083
    Store 2084
    Store 2090
    Store 2096
  City 1009
    Store 2004
    Store 2014
    Store 2017
    Store 2019
    Store 2076
    Store 2079
  City 1013
    Store 2013
    Store 2045
    Store 2053
    Store 2069

Here is the complete test Dot Net 7.0 console app:
C#
Random random = new Random();

List<Data> FlatData = GetData().ToList();
List<Node> NodeData = BuildHierarchy(FlatData).ToList();

OutputHierarchy(NodeData);

Console.WriteLine("-- press any key to quit --");
Console.ReadKey();

void OutputHierarchy(List<Node> nodeData)
{
    foreach (Node node in nodeData)
    {
        Console.WriteLine(node.Data.Title.PadLeft((node.Level - 1) * 2 + node.Data.Title.Length));
        if (node.Children?.Any() == true)
            OutputHierarchy(node.Children);
    }
}

IEnumerable<Node>? BuildHierarchy(List<Data> flatData, int parentId = -1, int level = 1)
{
    foreach (Data data in flatData.Where(item => item.ParentId == parentId))
    {
        yield return new Node
        {
            Data = data,
            Level = level,
            Children = BuildHierarchy(flatData, data.Id, level + 1)?.ToList()
        };
    }
}

IEnumerable<Data> GetData()
{
    for (int i = 0; i < 10; i++)
        yield return new Data
        {
            DataType = DataType.Country,
            Id = i,
            ParentId = -1,
            Title = $"Country {i}"
        };

    for (int i = 1000; i < 1020; i++)
        yield return new Data
        {
            DataType = DataType.City,
            Id = i,
            ParentId = random.Next(10),
            Title = $"City {i}"
        };

    for (int i = 2000; i < 2100; i++)
        yield return new Data
        {
            DataType = DataType.Store,
            Id = i,
            ParentId = random.Next(1000, 1020),
            Title = $"Store {i}"
        };
}

class Node
{
    public Data Data { get; set; }
    public int Level { get; set; }
    public List<Node>? Children { get; set; }
}

class Data
{
    public int Id { get; set; }
    public int ParentId { get; set; }
    public DataType DataType { get; set; }
    public string Title { get; set; }
}

enum DataType
{
    Country,
    City,
    Store
}


UPDATE

One more tip. If you need to get the path for a node, you can use the following:
C#
void OutputHierarchy(List<Node> nodeData)
{
    foreach (Node node in nodeData)
    {
        StringBuilder sb = new();
        sb.Append(node.Data.Title.PadLeft((node.Level - 1) * 2 + node.Data.Title.Length))
            .Append("  [")
            .Append(GetDataPath(node.Data))
            .Append("]");

        Console.WriteLine(sb);
        if (node.Children?.Any() == true)
            OutputHierarchy(node.Children);
    }
}

string GetDataPath(Data data)
{
    // root
    if (data.ParentId == -1)
        return data.Title;

    // child
    return $"{GetDataPath(FlatData.First(parent => parent.Id == data.ParentId))} > {data.Title}";
}

And the output will be:
Country 0  [Country 0]
  City 1000  [Country 0 > City 1000]
    Store 2015  [Country 0 > City 1000 > Store 2015]
    Store 2030  [Country 0 > City 1000 > Store 2030]
    Store 2068  [Country 0 > City 1000 > Store 2068]
    Store 2086  [Country 0 > City 1000 > Store 2086]
    Store 2089  [Country 0 > City 1000 > Store 2089]
  City 1013  [Country 0 > City 1013]
    Store 2007  [Country 0 > City 1013 > Store 2007]
    Store 2043  [Country 0 > City 1013 > Store 2043]
Country 1  [Country 1]
  City 1006  [Country 1 > City 1006]
    Store 2016  [Country 1 > City 1006 > Store 2016]
    Store 2019  [Country 1 > City 1006 > Store 2019]
    Store 2044  [Country 1 > City 1006 > Store 2044]
    Store 2078  [Country 1 > City 1006 > Store 2078]
    Store 2093  [Country 1 > City 1006 > Store 2093]
  City 1007  [Country 1 > City 1007]
    Store 2003  [Country 1 > City 1007 > Store 2003]
    Store 2020  [Country 1 > City 1007 > Store 2020]
    Store 2051  [Country 1 > City 1007 > Store 2051]
    Store 2052  [Country 1 > City 1007 > Store 2052]
    Store 2092  [Country 1 > City 1007 > Store 2092]
Country 2  [Country 2]
  City 1004  [Country 2 > City 1004]
    Store 2006  [Country 2 > City 1004 > Store 2006]
    Store 2027  [Country 2 > City 1004 > Store 2027]
    Store 2034  [Country 2 > City 1004 > Store 2034]
    Store 2047  [Country 2 > City 1004 > Store 2047]
    Store 2058  [Country 2 > City 1004 > Store 2058]
    Store 2095  [Country 2 > City 1004 > Store 2095]
  City 1010  [Country 2 > City 1010]
    Store 2032  [Country 2 > City 1010 > Store 2032]
    Store 2039  [Country 2 > City 1010 > Store 2039]
    Store 2041  [Country 2 > City 1010 > Store 2041]
    Store 2077  [Country 2 > City 1010 > Store 2077]
  City 1015  [Country 2 > City 1015]
    Store 2001  [Country 2 > City 1015 > Store 2001]
    Store 2037  [Country 2 > City 1015 > Store 2037]
    Store 2063  [Country 2 > City 1015 > Store 2063]
    Store 2070  [Country 2 > City 1015 > Store 2070]
    Store 2082  [Country 2 > City 1015 > Store 2082]
  City 1019  [Country 2 > City 1019]
    Store 2012  [Country 2 > City 1019 > Store 2012]
    Store 2029  [Country 2 > City 1019 > Store 2029]
    Store 2080  [Country 2 > City 1019 > Store 2080]
    Store 2099  [Country 2 > City 1019 > Store 2099]
Country 3  [Country 3]
  City 1003  [Country 3 > City 1003]
    Store 2000  [Country 3 > City 1003 > Store 2000]
    Store 2040  [Country 3 > City 1003 > Store 2040]
    Store 2045  [Country 3 > City 1003 > Store 2045]
    Store 2055  [Country 3 > City 1003 > Store 2055]
    Store 2061  [Country 3 > City 1003 > Store 2061]
    Store 2084  [Country 3 > City 1003 > Store 2084]
  City 1012  [Country 3 > City 1012]
    Store 2026  [Country 3 > City 1012 > Store 2026]
    Store 2031  [Country 3 > City 1012 > Store 2031]
    Store 2087  [Country 3 > City 1012 > Store 2087]
    Store 2088  [Country 3 > City 1012 > Store 2088]
    Store 2090  [Country 3 > City 1012 > Store 2090]
Country 4  [Country 4]
  City 1005  [Country 4 > City 1005]
    Store 2017  [Country 4 > City 1005 > Store 2017]
    Store 2018  [Country 4 > City 1005 > Store 2018]
    Store 2028  [Country 4 > City 1005 > Store 2028]
    Store 2050  [Country 4 > City 1005 > Store 2050]
    Store 2059  [Country 4 > City 1005 > Store 2059]
    Store 2064  [Country 4 > City 1005 > Store 2064]
    Store 2073  [Country 4 > City 1005 > Store 2073]
  City 1017  [Country 4 > City 1017]
    Store 2011  [Country 4 > City 1017 > Store 2011]
    Store 2014  [Country 4 > City 1017 > Store 2014]
    Store 2069  [Country 4 > City 1017 > Store 2069]
    Store 2083  [Country 4 > City 1017 > Store 2083]
  City 1018  [Country 4 > City 1018]
    Store 2004  [Country 4 > City 1018 > Store 2004]
    Store 2035  [Country 4 > City 1018 > Store 2035]
    Store 2053  [Country 4 > City 1018 > Store 2053]
    Store 2056  [Country 4 > City 1018 > Store 2056]
    Store 2057  [Country 4 > City 1018 > Store 2057]
    Store 2060  [Country 4 > City 1018 > Store 2060]
    Store 2062  [Country 4 > City 1018 > Store 2062]
    Store 2081  [Country 4 > City 1018 > Store 2081]
    Store 2097  [Country 4 > City 1018 > Store 2097]
Country 5  [Country 5]
  City 1011  [Country 5 > City 1011]
    Store 2025  [Country 5 > City 1011 > Store 2025]
    Store 2067  [Country 5 > City 1011 > Store 2067]
    Store 2074  [Country 5 > City 1011 > Store 2074]
Country 6  [Country 6]
Country 7  [Country 7]
  City 1008  [Country 7 > City 1008]
    Store 2013  [Country 7 > City 1008 > Store 2013]
    Store 2033  [Country 7 > City 1008 > Store 2033]
    Store 2046  [Country 7 > City 1008 > Store 2046]
    Store 2049  [Country 7 > City 1008 > Store 2049]
    Store 2072  [Country 7 > City 1008 > Store 2072]
    Store 2096  [Country 7 > City 1008 > Store 2096]
  City 1009  [Country 7 > City 1009]
    Store 2005  [Country 7 > City 1009 > Store 2005]
    Store 2009  [Country 7 > City 1009 > Store 2009]
    Store 2021  [Country 7 > City 1009 > Store 2021]
    Store 2022  [Country 7 > City 1009 > Store 2022]
    Store 2042  [Country 7 > City 1009 > Store 2042]
    Store 2048  [Country 7 > City 1009 > Store 2048]
    Store 2071  [Country 7 > City 1009 > Store 2071]
    Store 2076  [Country 7 > City 1009 > Store 2076]
    Store 2085  [Country 7 > City 1009 > Store 2085]
    Store 2098  [Country 7 > City 1009 > Store 2098]
Country 8  [Country 8]
  City 1001  [Country 8 > City 1001]
    Store 2010  [Country 8 > City 1001 > Store 2010]
    Store 2024  [Country 8 > City 1001 > Store 2024]
    Store 2036  [Country 8 > City 1001 > Store 2036]
    Store 2038  [Country 8 > City 1001 > Store 2038]
    Store 2054  [Country 8 > City 1001 > Store 2054]
    Store 2065  [Country 8 > City 1001 > Store 2065]
    Store 2066  [Country 8 > City 1001 > Store 2066]
    Store 2075  [Country 8 > City 1001 > Store 2075]
    Store 2094  [Country 8 > City 1001 > Store 2094]
  City 1002  [Country 8 > City 1002]
    Store 2008  [Country 8 > City 1002 > Store 2008]
    Store 2079  [Country 8 > City 1002 > Store 2079]
    Store 2091  [Country 8 > City 1002 > Store 2091]
  City 1014  [Country 8 > City 1014]
    Store 2002  [Country 8 > City 1014 > Store 2002]
Country 9  [Country 9]
  City 1016  [Country 9 > City 1016]
    Store 2023  [Country 9 > City 1016 > Store 2023]


Hopefully, this will point you in the right direction.
 
Share this answer
 
v2
Comments
xTMx9 29-Jun-23 7:16am    
Thanks for the answer!
I would also like to know if it is possible to group the results of tcGroup list in my code by the country name, it should have just 3 entries, right now, it is showing all countries in duplicates for each store there is. In the picture here it should show just 3 when debugging: https://imgur.com/a/xicFx1S
It should not show [0..99] and more rows like that, it should already group them and just show:
[0]
[1]
[2]
and inside each of these, the data of cities and stores would be in them.
Graeme_Grant 29-Jun-23 8:41am    
Not enough information posted, hence why I posted the answer that I did. Now it is up to you to learn and apply to your own code.
xTMx9 2-Jul-23 12:46pm    
Thanks for the detailed answer.
Graeme_Grant 2-Jul-23 20:30pm    
Glad that it helped.
BillWoodruff 29-Jun-23 16:33pm    
+5 great answer
I can't give you a complete code ... but a description how to do it ...
You must iterate through your entries and create at first the Root-Nodes (and only them) - and for every entry look if the root-node is allready existing.
After this you iterate again and for each entry you should look to which root-node it matches and create it there - but also here you have to look if that entry is allready existing under that root-node.
The same again which each other child-level ...
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900