实体框架 - 如何使用多对多关系改进查询

时间:2013-12-24 05:36:16

标签: c# performance linq entity-framework query-optimization

我想选择所有餐馆,并为每家餐馆加载所附类别的列表。

RestaurantCategory之间存在多对多关系:

enter image description here

public class Restaurant
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual List<Category> Categories { get; set; }
}

public class Category
{
    public int Id { get; set; }
    public string Value { get; set; }
}

在我目前的实施中,我选择了包含所有餐馆和类别的原始数据,并在客户端处理它们:按餐厅分组并选择类别。在这种情况下,生成的SQL看起来非常简单并且执行速度很快:

var plainData = (
    from restaurant in RestaurantsRepository.GetAll()
    from category in restaurant.Categories
    select new
        {
            Id = restaurant.Id,
            Name = restaurant.Name,
            Category = category.Value
        }).ToList();

var restaurants = (
    from restaurant in plainData
    group restaurant by new
        {
            restaurant.Id,
            restaurant.Name
        }
    into grp
    select new RestaurantModel
        {
            Id = grp.Key.Id,
            Name = grp.Key.Name,
            Categories = grp.Select(c => c.Category).ToList()
        });

另一种变体是使用实体框架以及餐馆和类别之间的关系。在这种情况下,生成的SQL非常复杂,执行速度慢四倍:

var restaurants =(
    from restaurant in RestaurantsRepository.GetAll()
    select new RestaurantModel
        {
            Id = restaurant.Id,
            Name = restaurant.Name,
            Categories = restaurant.Categories
        }).ToList();

问题是:有更有效的方法(然后是1或2)来选择我的数据吗?

1 个答案:

答案 0 :(得分:1)

您的收藏集是virtual,因此,我想,您正在使用延迟加载 第二个变体执行N + 1个查询,其中N是项目计数,从RestaurantsRepository.GetAll()返回:一个查询获取所有餐馆,N个查询获取特定餐厅的所有类别。

尝试使用急切的集合加载:

RestaurantsRepository
    .GetAll()
    .Include(r => r.Categories)

这应该对数据库执行JOIN的单一查询,就像这样(真正的SQL会有所不同):

SELECT
    *
FROM
    [Restaurants] JOIN [Categories] ON [Restaurants].Id = [Categories].[RestaurantId]

另外,考虑延迟加载 - 如果您将查询结果映射到其他类型(样本中为RestaurantModel),您真的需要它吗?