【问题标题】:Query all records including a count of related records查询所有记录,包括相关记录的计数
【发布时间】:2019-04-10 22:45:29
【问题描述】:

在带有 Entity Framework Core 的 ASP.NET Core 应用程序中,会显示记录列表。因此,将获取集合的所有实体并将它们的一些属性显示在表格中。

此外,还有通过外键连接的相关实体。对于其中一些,还应显示对象的总数。

例如有以下表格(简体):

  • 位置(ID、姓名、地址)
  • 设备(ID、名称、评论、LocationId)

这里只显示位置(主要实体)和设备(相关实体),但除了设备之外还有更多类型。数据库中的所有设备都分配到一个位置。实体类中存在外键和导航属性,以及非映射计数属性(参见最后一个示例)。

想要的结果是这样的:

  • 位置 ID 1,名称 1,此位置有 15 个设备
  • 位置 ID 2,名称 2,此位置有 6 个设备
  • 位置 ID 3,名称 3,0 个设备在此位置

我可以先获取按名称排序的位置,然后在单独的数据库查询中获取每个位置的设备数量。这可能会导致大量短查询。

var locations = dbContext.Locations
    .OrderBy(l => l.Name)
    .ToList();
foreach (var location in locations)
{
    int count = dbContext.Devices.Where(d => d.LocationId == location.Id).Count();
}

我还可以使用Include(location => location.Devices) 获取所有位置,然后只使用location.Devices.Count 获取它们的计数,但这涉及获取所有设备的所有详细信息,最终只使用它们的总计数。

var locations = dbContext.Locations
    .Include(l => l.Devices)
    .OrderBy(l => l.Name)
    .ToList();
foreach (var location in locations)
{
    int count = location.Devices.Count;
}

我可以像往常一样首先获取所有位置,然后构建一个查询来收集所有位置的设备计数。然后我必须通过遍历一个列表并在另一个列表中找到相应的条目来手动合并这些结果集,这可能会导致大量内存搜索。

var locations = dbContext.Locations
    .OrderBy(l => l.Name)
    .ToList();
var deviceCounts = dbContext.Locations
    .Select(l => new
    {
        l.Id,
        l.Devices.Count
    });
foreach (var c in deviceCounts)
{
    locations.First(l => l.Id == c.Id).DeviceCount = c.Count;
}

我正在寻找一个查询来获取按名称排序的位置以及每个位置的设备计数。结果必须是我的实体类型的列表,例如List<Location>。是否有一种简单或有效的方法可以通过 EF Core 实现这一目标?

【问题讨论】:

  • context.Query<LocationsWithDevicesCount>().FromSql("SELECT L.Id, L.Name, L.Address, Count(1) DevicesCount FROM Locations L LEFT OUTER JOIN Devices D ON L.Id = D.LocationId ORDER BY L.Name").AsNoTracking().ToList().

标签: c# .net entity-framework-core


【解决方案1】:

您需要选择所需的值。

var locations = dbContext.Locations
.Include(l => l.Devices)
.OrderBy(l => l.Name)
.Select( l => new {
             LocationId = l.Id,
             LocationName = l.Name,
             DeviceCount = l.Devices.Count()
        })
.ToList();

【讨论】:

  • 我已经编辑了这个问题,以澄清我需要一个类型化的列表,因此没有匿名类。
【解决方案2】:

首先,创建一个ViewModel 类并定义要在列表中显示的属性。 ViewModel 将只提供您需要的数据,而不是整个实体。示例:

public class DeviceViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int DeviceCount { get; set; }
}

然后,执行您想要返回的ViewModel 列表的查询,其中包含来自实体的数据,在Select 方法之前定义您想要的顺序。

var devices = dbContext.Locations
                       .Include(l => l.Devices)                           
                       .OrderBy(x => x.Name)          
                       .Select(l => new DeviceViewModel()
                       {
                          Id = l.Id,
                          Name = l.Name,
                          DevicesCount = l.Devices.Count()
                       })
                       .ToList();

记得调用ToList()来强制ORM对数据库执行查询。

【讨论】:

  • 我只是想知道 EF 会为此生成多糟糕的 SQL ......或者它将使用多少个查询
  • 我没有测试过它,我对 EF 没有很好的体验,但是......真的吗? NHibernate 只需对其进行一次简单的查询。
  • 嗯,这可能是查询中重复的大量属性令人不安。我不能只说“所有实体属性以及这个计数”吗?有可能会丢失一个属性,然后视图将没有它。
  • 明确命名使用的属性时,Include 不是必需的。将生成警告并忽略 Include 调用。所以我删除了它。我试图验证生成的 SQL 但无法验证。由于您只能为整个应用程序启用 SQL 日志记录,或者根本不启用(EF 日志记录的严重限制),这里发生的事情太多了。
猜你喜欢
  • 2016-02-19
  • 1970-01-01
  • 2013-11-11
  • 2014-06-10
  • 2017-03-10
  • 1970-01-01
  • 1970-01-01
  • 2022-07-01
  • 1970-01-01
相关资源
最近更新 更多