【问题标题】:Entity Framework - Nested Select Query on Selected Entry实体框架 - 选定条目上的嵌套选择查询
【发布时间】:2020-09-24 15:14:45
【问题描述】:

我正在使用Entity Framework Core 3.1.4。我的模型就像-

public class Review
{
    [Key, Required]
    public Guid Id { get; set; }

    public int Rating { get; set; }
    public DateTime WatchDate { get; set; }

    public virtual Guid UserId { get; set; }
    public virtual User User { get; set; }
}

我的控制器是这样的-

public async Task<IActionResult> Index()
{
    var data = await _context.Reviews
                        .Include(r=>r.User)
                        .Select(r => new Review {
                            Id = r.Id,
                            WatchDate = r.WatchDate,
                            User = r.User,
                            Rating = r.Rating
                        })
                        .OrderByDescending(r => r.Rating)
                        .ToListAsync();
    return View(data);
}

它做得很好,但是它正在像这样查询User表的所有数据(红色标记区域)-

但我只需要email,所以我喜欢从那里只选择电子邮件(黄色标记的一个)。我在上层做Select,但不能在内部元素做同样的事情。据我所知,Entity Framework 中有一种方法,例如 this。但随着版本的变化,代码无法正常工作。

谁能帮忙,我该如何完成?

如何在列表中仅包含用户电子邮件,而不包含所有数据?那么如何选择嵌套条目呢?

【问题讨论】:

  • 你可以这样做。 .Select(p => new { P.User.Email }) .AsEnumerable()
  • 如果 Select 不使用 Include,则 User 为空

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


【解决方案1】:

尝试下一个:

var data = await _context.Reviews
                    .OrderByDescending(r => r.Rating)
                    .Select(r => new Review {
                        Id = r.Id,
                        WatchDate = r.WatchDate,
                        User = new User { Email = r.User.Email},
                        Rating = r.Rating
                    })                       
                    .ToListAsync();

但我想说更好的设计是创建特定的DTO,它只包含需要的属性并将其填充到Select 子句中。

也不需要.Include(r=&gt;r.User) 调用,因为您有Select 子句。

【讨论】:

    【解决方案2】:

    您需要将您的实体映射到具有完全相同字段的 dto(数据传输对象)中,少用户,在其中您仅将 UserEmail 添加为参数

    public async Task<IActionResult> Index()
    {
        var data = await _context.Reviews
                            .Include(r=>r.User)
                            .Select(r => new Dtos.Review {
                                Id = r.Id,
                                WatchDate = r.WatchDate,
                                UserEmail = r.User.Email,
                                Rating = r.Rating
                            })
                            .OrderByDescending(r => r.Rating)
                            .ToListAsync();
        return View(data);
    }
    

    因此,来自 ContextReviews 的类型为 Review Entity,但来自您查询的 new Review 的类型为 Dto(参数相同,User少)

    无论如何,最佳做法是不要将实体直接发送给客户端并将其映射到 dto。

    实体:

    public class Review {
      public int Id {get; set;}
      public DateTime WatchDate {get; set;}
      public int UserId {get; set;}
      public User User {get; set;}
      public decimal Rating {get; set;}
    }
    

    Dto:

    public class Review {
      public int Id {get; set;}
      public DateTime WatchDate {get; set;}
      public string UserEmail {get; set;}
      public decimal Rating {get; set;}
    }
    

    【讨论】:

    • 你不需要写r.User != null ? r.User.Email : null,。 EF Core 会在生成 SQL 之前做一些必要的事情。
    • 谢谢。答案已更新。对我来说是防止空值错误的好方法。我太悲观了:))
    【解决方案3】:

    作为best practice,我建议create a DTO,它只包含必要的数据,不包含数据库对象:

    public class UserReviewDTO
    {
        public Guid Id { get; set; }
        public int Rating { get; set; }
        public DateTime WatchDate { get; set; }
        public string UserEmail { get; set; }
    }
    

    您可以这样进行查询:

    var data = await _context.Reviews
        .Select(r => new UserReviewDTO {
            Id = r.Id,
            WatchDate = r.WatchDate,
            UserEmail = r.User.Email,
            Rating = r.Rating
        })
        .OrderByDescending(r => r.Rating)
        .ToListAsync();
    

    【讨论】:

      【解决方案4】:

      在视图中使用ViewModel 总是更好。所以首先,创建一个ViewModel 类如下:

      public class ReviewViewModel
      {
          public Guid Id { get; set; }
      
          public int Rating { get; set; }
          public DateTime WatchDate { get; set; }
      
          public Guid UserId { get; set; }
          public string UserEmail { get; set; }
      }
      

      然后将您的查询投射到ViewModel 类型,如下所示:

      public async Task<IActionResult> Index()
      {
          var data = await _context.Reviews
                              .Select(r => new ReviewViewModel {
                                  Id = r.Id,
                                  WatchDate = r.WatchDate,
                                  Rating = r.Rating,
                                  UserId = r.User.Id,
                                  UserEmail = r.User.Email,
                              })
                              .OrderByDescending(r => r.Rating)
                              .ToListAsync();
          return View(data);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-06
        • 2014-05-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多