我使用的是EF Core 2.1预览本应该减少N + 1查询的问题。 我试图进行查询,选择论坛帖子与帖子的作者:
dbContext.ForumThreads
.Include(t => t.Posts)
.Take(n)
.Select(t => new
{
t.Id,
t.Title,
PostAuhtors = t.Posts.Select(p => p.Author).Take(5)
}).ToArray();
这会产生n + 1个查询:对于每个ForumThread,它会选择帖子作者
架构很简单:
public class ForumThread
{
public Guid Id {get;set;}
public string Title {get;set;}
public ICollection<ForumPost> Posts {get;set;}
}
public class ForumPost
{
public Guid Id {get;set;}
public string Author {get;set;}
public string Content {get;set;}
}
答案 0 :(得分:1)
我认为你可以用更少的查询(只有2个)实现这一点,在内存中做出一些行为。这段代码是否符合您的要求?
class Program
{
static void Main(string[] args)
{
using (var db = new SampleContext())
{
Console.ReadLine();
var result = db.Threads
.Include(t => t.Posts)
.Take(10)
.Select(t => new
{
t.Id,
t.Title,
t.Posts
// Do this in memory
//PostAuhtors = t.Posts.Select(p => p.Author).Take(5)
}).ToArray();
Console.WriteLine($"» {result.Count()} Threads.");
foreach (var thread in result)
{
// HERE !!
var PostAuhtors = thread.Posts.Select(p => p.Author).Take(5);
Console.WriteLine($"» {thread.Title}: {string.Join("; ", PostAuhtors)} authors");
}
Console.ReadLine();
}
}
}
public class SampleContext : DbContext
{
public static readonly LoggerFactory MyLoggerFactory = new LoggerFactory(new[] {
new ConsoleLoggerProvider((category, level)
=> category == DbLoggerCategory.Database.Command.Name
&& level == LogLevel.Debug, true)
});
public DbSet<ForumThread> Threads { get; set; }
public DbSet<ForumPost> Posts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.EnableSensitiveDataLogging()
.UseLoggerFactory(MyLoggerFactory)
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFStart;Trusted_Connection=True;");
}
}
public class ForumThread
{
public Guid Id { get; set; }
public string Title { get; set; }
public ICollection<ForumPost> Posts { get; set; }
}
public class ForumPost
{
public Guid Id { get; set; }
public string Author { get; set; }
public string Content { get; set; }
}
答案 1 :(得分:0)
您的方法各有利弊。让我解释一下,
缺点=>查询n + 1次。
专业人士=>您只要求5个帖子的作者。 (n次)。
SELECT TOP(@__p_0) [t].[Id], [t].[Title] FROM [ForumThreads] AS [t] => 1 time
SELECT TOP(5) [p].[Author] FROM [ForumPosts] AS [p] => n time (n => number of ForumThread)
var source = dbContext.ForumThreads.Include(t => t.Posts).Take(5).ToList();
var result = source.Select(t => new { t.Id, t.Title, PostAuhtors = t.Posts.Select(p => p.Author).Take(5).ToList() }).ToArray();
优点=>查询1次。
缺点=>您可以从数据库中获取帖子的所有作者,然后对其进行过滤。 (根据您的数据大小,这可能会花费太多。)
SELECT [t.Posts].[Id], [t.Posts].[Author], [t.Posts].[Content], [t.Posts].[ForumThreadId] FROM [ForumPosts] AS [t.Posts]
INNER JOIN (SELECT TOP(@__p_0) [t0].[Id]
FROM [ForumThreads] AS [t0] ORDER BY [t0].[Id]
) AS [t1] ON [t.Posts].[ForumThreadId] = [t1].[Id] ORDER BY [t1].[Id]