Entity Framework Core é carregamento lento ao transformar
Estou tendo um problema com o Entity Framework Core (v2.0.1) ao transformar um modelo de entidade em um DTO. Basicamente, em qualquer outra versão da frase, o carregamento é lento quando não quero. Aqui está um aplicativo simples do .NET Core Console (com o pacote 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<ReportDbContext>()
.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 < 10; tagId++)
report.Tags.Add(new ReportTag { TagId = tagId });
dbContext.Reports.Add(report);
dbContext.SaveChanges();
}
}
}
using (var dbContext = new ReportDbContext(dbOptions))
{
var reports = dbContext.Reports
.Select(r => new ReportDto
{
Id = r.Id,
Title = r.Title,
Tags = r.Tags.Select(rt => rt.TagId)
})
.ToList();
}
}
}
class ReportDbContext : DbContext
{
public DbSet<Report> Reports { get; set; }
public ReportDbContext(DbContextOptions<ReportDbContext> options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ReportTag>().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<ReportTag> Tags { get; set; }
public Report()
{
Tags = new HashSet<ReportTag>();
}
}
[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<int> Tags { get; set; }
}
}
Agora quando oToList()
método é executado para recuperar os dados, ele está executando o seguinte SQL
SELECT [r].[Id], [r].[Title]
FROM [Report] AS [r]
Como você pode ver, não fez nenhum esforço para ingressar no[ReportTag]
tabela e se você realmente tentar ler os valores doTags
propriedade em umReportDto
então ele dispara outra consulta SQL
SELECT [rt].[TagId]
FROM [ReportTag] AS [rt]
WHERE @_outer_Id = [rt].[ReportId]
Agora eu sei que o EF Core não suporta carregamento lento, mas isso me parece muito com carregamento lento. Neste caso, eu não quero que ele carregue preguiçosamente. Eu tentei mudarvar reports = dbContext.Reports
paravar reports = dbContext.Reports.Include(r => r.Tags)
que não tem efeito.
Eu até tentei mudarTags = r.Tags.Select(rt => rt.TagId)
paraTags = r.Tags.Select(rt => rt.TagId).ToList()
mas isso apenas dispara a consulta SQL secundária acima mais 26 vezes.
Finalmente, em desespero, tentei mudarvar reports = dbContext.Reports
paravar reports = dbContext.Reports.Include(r => r.Tags).ThenInclude((ReportTag rt) => rt.TagId)
mas que, compreensivelmente, gera uma exceção queReportTag.TagId
não é uma propriedade de navegação.
Alguém tem alguma idéia do que eu posso fazer para que ele carregue ansiosamente oReportDto.Tags
propriedade?