【问题标题】:Is there a better and optimized way to create a nested list with C#?有没有更好和优化的方法来使用 C# 创建嵌套列表?
【发布时间】:2019-01-18 15:38:45
【问题描述】:

我想创建一个基于三个 Enumerable 列表的平面列表。这些列表的数据存储在我的数据库中的三个表中:大陆、国家、城市。 为此,我为每个实体获取 3 个循环,因此如果我添加另一个名为 District 的实体,我需要第四个循环,依此类推。那么有一种方法可以优化我的代码以使用 LooKup 或更简洁的 Linq 语法并提高性能吗?请参阅下面的代码:

大陆

Id  Name            
1   NorthAmerica    
2   Europe

国家

Id  Name              ContinentId        
1   USA               1
2   CANADA            1
3   FRANCE            2
4   ENGLAND           2

国家

Id  Name              CountryId        
1   PARIS             3
2   MONTREAL          2
3   NEW YORK          1
4   LYON              3
5   LONDRES           4

在 C# 中,我从存储库中获取每个表的数据

var lists = new List<Select> { };

var continents = _unitOfWork.ContinentRepository.Get().
                 Select(x => new { id = x.Id, name = x.name });

var countries = _unitOfWork.CountryRepository.Get().
                Select(x => new { id = x.Id, continentId = x.ContinentId, name = x.name });

var cities = _unitOfWork.CityRepository.Get().
             Select(x => new { id = x.Id, countryId = x.CountryId, name = x.name });


if (continents.Count() > 0)
{
    foreach (var continent in continents)
    {
        lists.Add(new Select { Id = continent.id, Value = continent.name, Level = "first-level" });
        foreach (var country in countries.Where(x => x.continentId == continent.id))
        {
            lists.Add(new Select { Id =  country.id , Value = country.name, Level = "second-level" });
            foreach (var city in cities.Where(x => x.countryId == country.id))
            {
                lists.Add(new Select { Id = city.id , Value = city.name, Level = "third-level" });
            }
        }
    }
}
NORTHAMERICA
   USA 
     NEW YORK
   CANADA
     MONTREAL
EUROPE
  FRANCE
    LYON
    PARIS
  ...
The class attribute will add the spaces (indentation), so i need it.
[
    {
        "id": 1
        "value": "NorthAmerica",
        "class": "first-level"
    },
    {
        "id": 1
        "value": "USA",
        "class": "second-level"
    },
    {
        "id": 3
        "value": "NEW YORK",
        "class": "third-level"
    },
    {
        "id": 2
        "value": "CANADA",
        "class": "second-level"
    },
    {
        "id": 3
        "value": "MONTREAL",
        "class": "third-level"
    },
    {
        "id": 2
        "value": "Europe",
        "class": "first-level"
    },
    {
        "id": 1
        "value": "FRANCE",
        "class": "second-level"
    },
    {
        "id": 3
        "value": "PARIS",
        "class": "third-level"
    },
    {
        "id": 4
        "value": "LYON",
        "class": "third-level"
    },
    {
        "id": 4
        "value": "ENGLAND",
        "class": "second-level"
    },
        "id": 5
        "value": "LONDRES",
        "class": "third-level"
    },
]

【问题讨论】:

  • 您是否使用 Entityframework 作为存储库?
  • 为什么要这样的平面列表?如果不是对象,则适当的对象图或 3 个列表可能会更有用,因为平面列表的用户需要过滤和/或分组。
  • 这是一个非常奇怪的数据表示。你打算如何消费它?我会将其表示为层次结构:Continent-&gt;Country-&gt;City。类似:[{NorthAmerica, [{Canada, [{Montreal}]}, USA, [{NewYork}]}}, 等。(这是在这个小框中编辑的 JSON,不一定是正确的 JSON)
  • 列表顺序重要吗?
  • 返回正确的对象图是 repository 的 工作。我怀疑你在 EF 上使用了“通用存储库”anti模式,阻止了 ORM 加载相关实体,所以现在你必须再次将实体连接在一起。通过适当的设计,您无需编写除 dbContetxt.Cities.Where(city=&gt;city.Country.Continent.Name=="Europe").ToList() 之外的任何内容即可获取欧洲的所有城市及其国家和大陆实体

标签: c# .net-core


【解决方案1】:

我不知道你为什么在那些有图书馆可以更好地处理这个问题的日子里做这样的事情 EntityFramework 和 EntityWorker.Core

在 Entityworker.Core 中,只需一次选择即可轻松完成 这是您的模块示例

public class Continent {

  [PrimaryKey]
  public int Id { get; set; }

  public string Name { get; set; }

  public List<Country> Countries { get; set; }

}

public class Country {

  [PrimaryKey]
  public int Id { get; set; }

  public string Name { get; set; }

  [ForeignKey(typeof(Continent))]
  public int ContinentId { get; set; }

  public List<City > Cities { get; set; }

}

   public class City {

      [PrimaryKey]
      public int Id { get; set; }

      public string Name { get; set; }


      [ForeignKey(typeof(Country))]
      public int CountryId { get; set; }
  }

现在您所要做的就是获取数据并加载孩子

using (var rep = new Repository())
   {
     // this will load all Continent and Countries and Cities all togather with a single call
    List<Continent> Continents = rep.Get<Continent>().Where(x=> x.Id == 54).LoadChildren().Execute(); 
   }

这里有更多关于图书馆的信息 https://www.codeproject.com/Tips/1222424/EntityWorker-Core-An-Alternative-to-Entity-Framewo

Entityframework 可以做同样的事情,除了 LoadChildren 你做 Include() http://www.entityframeworktutorial.net/basics/context-class-in-entity-framework.aspx

【讨论】:

  • 没有理由使用IncludedbContetxt.Cities.Where(city=&gt;city.Country.Continent.Name=="Europe").ToList() 将加载欧洲所有城市
  • @Alen.Toma,将其他库链接到他们的项目有什么好处。
  • @PanagiotisKanavos 同时加载所有数据将导致笛卡尔生产,根据性能限制,他们可能只想在单独调用中加载城市国家和大洲
  • @johnny5 为什么?如果关系配置正确,EF 应该生成一个查询,通过 ID 将三个表连接在一起。这与在 SQL 中所做的相同。
  • @Panagiotis 是的,但是您为包含其国家和大陆的每个城市发送重复项,如果您的服务器托管在您发送大量额外数据的其他地方,那么发出 3 个单独的请求可能会更有效避免join造成的重复
猜你喜欢
  • 2010-09-09
  • 1970-01-01
  • 1970-01-01
  • 2014-07-06
  • 1970-01-01
  • 2013-08-04
  • 1970-01-01
  • 2010-09-25
  • 1970-01-01
相关资源
最近更新 更多