分层数据并使用 LINQ 合并两个数据集

Hierarchical data and merging two data sets with LINQ

提问人:MattoMK 提问时间:3/1/2023 最后编辑:MattoMK 更新时间:3/2/2023 访问量:81

问:

我正在尝试合并来自两个不同服务调用的数据。数据在结构上与此类似:

API 层中的 DTO:

public static class GetData
{
    public record Country
    {
        public Guid Id { get; set; }
        public List<State> States { get; set; }
    }

    public record State
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public List<City> Cities { get; set; }
    }

    public record City
    {
        // this Id is stored in Service B's DB as ItemId
        public Guid Id { get; set; }
        public string Name { get; set; }
        public Location Location { get; set; }
    }

    // From service B
    public record Location
    {
        public Guid Id { get; set; }
        public Guid ItemId { get; set; }
        public string Name { get; set; }
        public string Latitude { get; set; }
        public string Longitude { get; set; }
    }
}

我需要将位置合并到正确的城市中,并一次性返回上面的整个结构。例如。

// this gets data including city (but no location) :
var country = await _serviceRequestA.GetCountryData();

// get list of Ids to make second call:
var ids = country.States.SelectMany(x => x.Cities).Select(x => x.Id).ToList();

// get list of location objects based on these ids:
var locations = await _serviceRequestB.GetLocations(ids); 

尝试:通过城市将每个城市与每个位置进行匹配。ID 和位置。ItemId,找到后,将位置的 Id、Name、Latitude、Longitude 作为 Location 对象附加到城市。

// do something like this on each city:
// ...but all previous data,
cities.ForEach(c =>
 {
  c.Location = locations.Where(x => x.ItemId == c.Id)
  .FirstOrDefault();
 });

但是,我如何按原样获取每个父对象和子对象,包括城市?

同样,我的数据是:.country.states[n].cities[n]

我对 C# 或 LINQ 没有经验,除了简单的 where 和 selects。我是否必须进行多个循环并获取每个关卡的道具,然后应用合并逻辑?在 LINQ 中是否有更简洁的方法?

PS-这是当前从第一个服务调用返回的内容的json表示,另一个是合并后我想要的结果。我需要将 Location 对象附加到每个匹配的城市:

{
  "country": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "name": "US",
    "states": [
      {
        "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "name": "California",
        "cities": [
          {
            "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "name": "LA"
          }
        ]
      },
      {
        "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "name": "Maine",
        "cities": [
          {
            "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "name": "Bangor"
          }
        ]
      }
    ]
  }
}

因此,当我将数据返回到前端时,城市将如下所示:

{
  "country": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "name": "US",
    "states": [
      {
        "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "name": "California",
        "cities": [
          {
            "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "name": "LA",
            "location": {
               "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
               "name": "Location X",
               "latitude": "xxx",
               "longitude": "xx-xx"
              }
          }
        ]
      },
      {
        "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "name": "Maine",
        "cities": [
          {
            "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "name": "Bangor",
            "location": {
               "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
               "name": "Location Y",
               "latitude": "yy",
               "longitude": "yy-yy"
              }
          }
        ]
      }
    ]
  }
}
C# Linq ASP.Net-Core ASP.NET-Web-API 嵌套列表

评论

0赞 Xinran Shen 3/1/2023
我不确定我是否明白你的意思。 在外键是要对吗?当您用于获取位置时,是否要将它们添加到相应城市中的属性中?ItemIdLocationIdCityvar locations = await _serviceRequestB.GetLocations(ids); Location
0赞 MattoMK 3/1/2023
是的,这听起来没错。现在,Cities数组从State.Cities开始。第二次调用返回位置数组。我需要根据这些 ID 将位置链接到每个城市。它们不是数据库中的 FK,它们是单独的服务和数据库。但是所有城市 ID 都会在 location.itemId 中匹配。基本上,我想将来自呼叫二的额外数据作为 DTO 附加到列表中每个城市的正确城市。Vs 在城市上空循环并为每个城市进行网络呼叫。id, name

答:

1赞 Xinran Shen 3/1/2023 #1

从你的问题和评论来看,我认为这就是你想要的。

cities.ForEach(c =>
            {
                c.Location = locations.Where(x => x.ItemId == c.Id).FirstOrDefault();
            });

或者,如果不想包含 的值,也可以使用以下代码:Itemid

cities.ForEach(c =>
            {
                c.Location = locations.Where(x => x.ItemId == c.Id).Select(x => new LocationA(){Id= x.Id,Name= x.Name,Latitude=x.Latitude,Longitude=x.Longitude}).FirstOrDefault();
            });

更新

如果要从国家/地区访问城市,只需使用两次即可。请注意,如果要输出不带 itemid 属性的 json,而不是 itemid 的值为 null,则需要更改代码,例如:Foreach(xxx)

public class City
    {
        // this Id is stored in Service B's DB as ItemId
        public Guid Id { get; set; }
        public string Name { get; set; }
        public Object Location { get; set; }
    }

然后:

country.States.ForEach(x =>
            {
                x.Cities.ForEach(y =>
                {
                    y.Location = locationAs.Where(z => z.ItemId == y.Id).Select(d=> new {Id=d.Id,Name = d.Name, Latitude = d.Latitude, Longitude = d.Longitude }).FirstOrDefault();
                });
            });

输出:

enter image description here

您甚至可以看到属性没有映射数据,它只会显示 null。

评论

0赞 MattoMK 3/1/2023
是的,就是这个想法。但这就是我卡住的地方:我如何进入城市?以及如何在我下树时保持每个级别的现有属性?我有 where states and cities 是多个对象的列表。每个级别都是一个对象,还需要保留其他属性。一旦我到达城市级别并且树内没有损失,那么你的例子应该有效。这有意义吗?我可能没有很好地解释它。我应该发布示例json。country.states[n].cities[n]
0赞 MattoMK 3/2/2023
我试着在上面澄清。json 应该会有所帮助。
0赞 Xinran Shen 3/2/2023
请检查更新,希望它能解决您的问题。
0赞 MattoMK 3/2/2023
伟大。我想也许有一种 LINQ 模式以不同的方式处理这个问题。我会在几个小时内尝试。感谢您指出 null 的问题,但我们有处理所有这些和 json 的拦截器,如果它为 null,它甚至不会显示属性。
1赞 MattoMK 3/2/2023
当然可以!