如何使用单个SQL生成的查询在EFCore中选择模拟多对多的属性

时间:2018-04-02 18:21:26

标签: c# entity-framework-core

如果不创建链接实体,EFCore不支持多对多关系。我需要从一对多关系的“另一端”有效地选择属性的子集。

我发誓这已经有了答案,但还没有找到答案。

使用这些模型:

public class Book
{
    public int BookId { get; set; }
    public string Title { get; set; }
    public Author Author { get; set; }
    public ICollection<BookCategory> BookCategories { get; set; }
} 

public class Category
{
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public string ExtraProperties {get; set; }
    public ICollection<BookCategory> BookCategories { get; set; }
}

public class BookCategory
{
    public int BookId { get; set; }
    public Book Book { get; set; }
    public int CategoryId { get; set; }
    public Category Category { get; set; }
}

这个问题是一个类似但不同的问题的延伸,标题为“Select specific properties from include ones in entity framework core

我正在寻找一个返回本书类别List<string> categoryNames的查询。

这个嵌套选择,使用“投影”会产生多个SQL查询:

var result= await _ctx.Books
    .Where(x => x.BookId == id)
    .Select(x => x.BookCategorys
        .Select(y => y.Category.CategoryName )
        .ToList())                
    .FirstOrDefaultAsync();

使用.Include(x => x.BookCategory).ThenInclude(x => Category)的任何解决方案都会在应用选择之前从服务器加载所有数据。

是否有符合以下条件的查询?:

  • 仅生成1个SQL查询
  • 不加载整个链接实体和/或整个导航属性2'跳'。
  • 仅返回CategoryNames的List<string>

我从这Entity Framework Core generates two select queries for one-to-many relationship推断,这是不可能的。

1 个答案:

答案 0 :(得分:4)

通常,您无法控制生成的SQL以及ORM执行的SQL查询数。在编写本文时,已知EF Core(版本2.0.2)在查询包含集合投影时会生成N + 1个查询。这个is fixed in the next 2.1 release,但仍会生成并执行至少2个查询。

但每条规则都有例外。由于您只想返回 单个相关集合投影,因此您只需使用SelectMany代替原始Select + FirstOrDefault构造。这些相当于这种情况,而且EF Core不够聪明,不能像以前那样对待后者。可以理解的是,还需要考虑其他案例。好处是以这种方式重写LINQ查询会产生所需的单个SQL查询转换:

var result = await _ctx.Books
    .Where(x => x.BookId == id)
    .SelectMany(x => x.BookCategorys
        .Select(y => y.Category.CategoryName))
    .ToListAsync();