簡體   English   中英

在 Entity Framework Core/LINQ 中對 1:M:M:1 相關數據進行分組和扁平化

[英]grouping and flatening 1:M:M:1 related data in Entity Framework Core/LINQ

在一個使用 Entity Framework 和 LINQ 的小型 ASP.NET Core Web API 中,我想實現一種方法來查詢一個人需要訪問的房間。 一個人想要的結果是這樣的


{
  "id": 1,
  "firstname": "First1",
  "lastname": "Last1",
  "rooms": [
      {
        "id": 1,
        "name": "test room 1"
      },
      {
        "id": 2,
        "name": "test room 2"
      },
      {
        "id": 3,
        "name": "test room 3"
      }
    ]
}

現在房間不是直接分配給一個人而是一個人有多個任務(每個任務只能有一個人),每個任務都需要訪問多個房間(所以任務和房間之間有一個M:N)。 房間表存儲房間 id 和名稱。

所以我有4張桌子:

  1. 人(身份證,名字,姓氏)
  2. 任務(id、personId、姓名)
  3. 任務室(id、roomId、taskId)
  4. Room (id, name) -> 應該顯示的數據

表之間的連接是:

  • 人 1:M 任務
  • 任務 1:M 任務室
  • 任務室 N:1 間

我目前的方法看起來像這樣


[HttpGet("RoomAccess/{personId:int}")]
public IActionResult RoomAccess(int personId)
{
    _logger.LogDebug(string.Format("{0}.RoomAccess(person id: {1})", GetType().Name, personId));

    var result = _dbContext.Person
        .Include(p => p.Task).ThenInclude(t => t.TaskRoom).ThenInclude(tr => tr.Room)
        .Where(p => p.Id == personId)
        .Select(person => new
        {
            person.Id,
            person.Firstname,
            person.Lastname,

            // how to do that line?
            rooms = person.Task.Select(ta => ta.TaskRoom.Select(tr => new {id = tr.Room.Id, name = tr.Room.Name} ) )
        }
    )
    .FirstOrDefault();

    if (result == null) 
    {
        return NotFound(string.Format("person id: {0}", personId));
    }

    return Ok(result);
}

到目前為止的結果:


{
  "id": 1,
  "firstname": "First1",
  "lastname": "Last1",
  "rooms": [
    [
      {
        "id": 1,
        "name": "test room 1"
      }
    ],
    [
      {
        "id": 2,
        "name": "test room 2"
      },
      {
        "id": 3,
        "name": "test room 3"
      }
    ],
    [
      {
        "id": 3,
        "name": "test room 3"
      }
    ],
    []
  ]
}

對於尚未分配空間的任務(到目前為止),這里還有末尾的空括號[] 並且測試室 3 出現多次,因為兩個不同的任務需要訪問這個房間。

如何獲得僅包含房間 id 和名稱的相關房間的分組列表(如所需結果的示例)

歡迎任何幫助。


現在EF Core Github上有一個未解決的問題


回答

那么工作代碼是:


[HttpGet("RoomAccess/{personId:int}")]
        public IActionResult RoomAccess(int personId)
        {
            _logger.LogDebug(string.Format("{0}.RoomAccess(person id: {1})", GetType().Name, personId));

            var result = _dbContext.Person
                .Include(p => p.Task).ThenInclude(t => t.TaskRoom).ThenInclude(tr => tr.Room)
                .Where(p => p.Id == personId)
                .ToList()
                .Select(person => new
                    {
                        person.Id,
                        person.Firstname,
                        person.Lastname,

                        rooms = person.Task.SelectMany(ta => ta.TaskRoom.Select(
                            tr => new { id = tr.Room.Id, name = tr.Room.Name })
                        )
                        .Distinct()
                        .OrderBy(adt => adt.name)
                    }
                )
                .FirstOrDefault();


            if (result == null) 
            {
                return NotFound(string.Format("person id: {0}", personId));
            }

            return Ok(result);
        }

除了 Nkosi 的答案之外,在 Where() 之后使用 Include() 和 ThenInclude() 以及 .ToList() 也很重要。

使用SelectMany擴展來展平列表。

//...
rooms = person.Task
    .SelectMany(ta => ta.TaskRoom.Select(tr => new {id = tr.Room.Id, name = tr.Room.Name}))
    .Distinct()
//...

不確定它將如何在 EF 中轉置以及任何性能影響。

請注意使用Distinct刪除任何重復項。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM