entity framework核心在转换时是延迟加载的

在将实体模型转换为DTO时,我遇到了Entity Framework Core(v2.0.1)的问题。 基本上,它是由任何其他版本的短语,懒惰加载,当我不想要它。 这是一个简单的.NET Core Console应用程序(使用Microsoft.EntityFrameworkCore.SqlServer(2.0.1)包)。

using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; namespace EfCoreIssue { class Program { static void Main(string[] args) { var dbOptions = new DbContextOptionsBuilder() .UseSqlServer("Server=.;Database=EfCoreIssue;Trusted_Connection=True;") .Options; // Create and seed database if it doesn't already exist. using (var dbContext = new ReportDbContext(dbOptions)) { if (dbContext.Database.EnsureCreated()) { string alphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; foreach (char alpha in alphas) { var report = new Report { Title = $"Report { alpha }" }; for (int tagId = 0; tagId  new ReportDto { Id = r.Id, Title = r.Title, Tags = r.Tags.Select(rt => rt.TagId) }) .ToList(); } } } class ReportDbContext : DbContext { public DbSet Reports { get; set; } public ReportDbContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity().HasKey(rt => new { rt.ReportId, rt.TagId }); } } [Table("Report")] class Report { [Key] public int Id { get; set; } public string Title { get; set; } public virtual ICollection Tags { get; set; } public Report() { Tags = new HashSet(); } } [Table("ReportTag")] class ReportTag { public int ReportId { get; set; } public int TagId { get; set; } } class ReportDto { public int Id { get; set; } public string Title { get; set; } public IEnumerable Tags { get; set; } } } 

现在,当执行ToList()方法来检索数据时,它正在执行以下SQL

 SELECT [r].[Id], [r].[Title] FROM [Report] AS [r] 

正如您所看到的,它没有努力加入[ReportTag]表,如果您实际尝试读取ReportDtoTags属性的值,那么它会触发另一个SQL查询

 SELECT [rt].[TagId] FROM [ReportTag] AS [rt] WHERE @_outer_Id = [rt].[ReportId] 

现在我知道EF Core不支持延迟加载,但这看起来非常像我的延迟加载。 在这个例子中,我不希望它延迟加载。 我已经尝试将var reports = dbContext.Reportsvar reports = dbContext.Reports.Include(r => r.Tags) ,它们没有任何效果。

我甚至尝试将Tags = r.Tags.Select(rt => rt.TagId)更改为Tags = r.Tags.Select(rt => rt.TagId).ToList()但这只是触发了上面的辅助SQL再查询26次。

最后在绝望中我尝试将var reports = dbContext.Reportsvar reports = dbContext.Reports.Include(r => r.Tags).ThenInclude((ReportTag rt) => rt.TagId)但可以理解的是抛出了ReportTag.TagId的exceptionReportTag.TagId不是导航属性。

有没有人对我能做什么有任何想法,以便它急切加载到ReportDto.Tags属性?

正如您所注意到的,目前包含集合投影的EF Core投影查询存在两个问题 – (1)它们导致每个集合执行N个查询,(2)它们被懒惰地执行。

问题(2)很奇怪,因为具有讽刺意味的是EF Core不支持延迟加载相关的实体数据,而这种行为有效地实现了它的投影。 至少你可以使用ToList()或类似方法强制立即执行,就像你已经找到的那样。

问题(1)目前无法解决。 它由Query跟踪:优化查询投影相关集合,以便它们不会导致N + 1数据库查询#9282并根据路线图 ( Reduce n + 1查询项)最终将在下一个EF中修复(改进)核心2.1发布。

我能想到的唯一解决方法是(使用更高的数据传输和内存使用的成本)使用预先加载并在之后进行投影(在LINQ to Entities的上下文中):

 var reports = dbContext.Reports .Include(r => r.Tags) // <-- eager load .AsEnumerable() // <-- force the execution of the LINQ to Entities query .Select(r => new ReportDto { Id = r.Id, Title = r.Title, Tags = r.Tags.Select(rt => rt.TagId) }) .ToList();