【问题标题】:Entity Framework - grouping by one column and selecting multiple columns实体框架 - 按一列分组并选择多列
【发布时间】:2021-09-24 19:23:25
【问题描述】:

我的 API 有这种方法,我试图过滤掉用户 (userId) 并按 EventId 分组。然后我想从表中选择三列:nameeventIdUserId

这是我的代码,但它不起作用:

[HttpGet("[action]/{userId}")]
public IActionResult EventsMatchFiltered(int userId)
{
    var events = _dbContext.Events.Where(event1 => event1.UserId != userId)
       .GroupBy(g => new {g.EventId })
       .SelectMany(g => g.Select(h => new 
       {
           Name = h.Name, 
           h.EventId, 
           h.UserId 
       }));
    return Ok(events);
}

【问题讨论】:

  • 没有以什么方式工作?
  • EventId 分组有意义吗?我认为这是Event 的独特PK?也就是说,GroupBy 后跟SelectMany 几乎可以肯定没有多大意义,因为这些组立即被展平,使得查询几乎与直接Select 相同。再说一次,“它不起作用”并没有给我们太多的依据。
  • 感谢您的反馈,但要澄清 EventId 不是主键,它更像是一个交叉引用。用外行的话来说,我所拥有的是一个包含以下列的事件表:Id、Name、UserId、EventId。我试图用我的方法做的是,如果我输入一个 userId 作为参数,那么它将过滤掉 userId 并按 eventID 分组。然后我希望它显示名称、UserId 和 EventId。根据一些反馈,我也尝试过此代码,但它返回 500 内部服务器错误。
  • 这是最新的错误代码 public IActionResult EventsMatchFiltered(int userId) { var events = _dbContext.Events .Where(event1 => event1.UserId != userId) .GroupBy(g => g.EventId) .Select(g => new Event { EventId = g.Key, Events = g.Select(e => new Event { Name = e.Name, UserId = e.UserId }) });返回确定(事件); }
  • 有一天您将不得不透露您收到的异常消息。不是“500”等。查询本身引发的异常。

标签: c# api entity-framework


【解决方案1】:

首先,事件将是一个IQueryable,而不是一个物化集合。其次,它将是一个匿名类型,而不是一个适当的 DTO/ViewModel 可序列化类。第三,您按 EventId 进行分组,但不会基于该分组“选择”任何内容。 EventId 听起来像是您应该分组的事件的主键。您确定您实际上不是要按用户分组吗??

通常对于像分组结果这样的东西,你会有一个类似的视图模型:

[Serializable]
public class UserEventSummaryViewModel
{
    public int UserId { get; set; }
    public ICollection<EventSummaryViewModel> Events { get; set; } = new List<EventSummaryViewModel>();
}

[Serializable]
public class EventSummaryViewModel
{
    public int EventId { get; set; }
    public string Name { get; set; }
}

然后查询并填写:

var otherUserEvents = _dbContext.Events
   .Where(e => e.UserId != userId)
   .GroupBy(g => g.UserId )
   .Select(g => new UserEventSummaryViewModel
   { 
       UserId == g.Key,
       Events = g.Select(e => new EventSummaryViewModel
       {
           EventId = e.EventId,
           Name = e.Name
       }).ToList()
   }).ToList();

这将提供按其他每个用户分组的事件结构。如果您想要 UserId 和 UserName,则将 UserName 属性添加到 UserEventSummary 视图模型和 GroupBy 子句中:

.GroupBy(g => new { g.UserId, g.Name } )

并在视图模型中填充这些:

UserId == g.Key.UserId,
UserName = g.Key.Name,

【讨论】:

  • 没有必要有具体的类型(查看模型,因为你在这里称它们)匿名类型在大多数情况下都可以正常工作。您希望避免使用内部.ToList(),而外部尤其是它是您想要推迟的更大查询的一部分。就像在 [EnableQuery] 的场景中一样
  • 感谢您的反馈,但要澄清 EventId 不是主键,它更像是一个交叉引用。用外行的话来说,我所拥有的是一个包含以下列的事件表:Id、Name、UserId、EventId。我试图用我的方法做的是,如果我输入一个 userId 作为参数,那么它将过滤掉 userId 并按 eventID 分组。然后我希望它显示名称、UserId 和 EventId。根据一些反馈,我也尝试过此代码,但它返回 500 内部服务器错误。
  • 这是最新的错误代码 public IActionResult EventsMatchFiltered(int userId) { var events = _dbContext.Events .Where(event1 => event1.UserId != userId) .GroupBy(g => g.EventId) .Select(g => new Event { EventId = g.Key, Events = g.Select(e => new Event { Name = e.Name, UserId = e.UserId }) });返回确定(事件); }
  • 好的,这更有意义,尽管在不是 PK 的表/实体事件中包含 EventId 令人困惑。 :) 500 错误是来自服务器的通用“包罗万象”响应,以避免暴露有关内部工作的详细信息。您需要使用调试器运行您的代码,然后捕获异常以进行检查并单步执行以查看失败的位置。如果它甚至没有进入您的方法(没有断点命中),那么参数/签名就有问题。你怎么称呼这个方法?通过 Ajax?
猜你喜欢
  • 1970-01-01
  • 2013-11-01
  • 2012-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-14
  • 2021-10-16
相关资源
最近更新 更多