简体   繁体   中英

Entity Framework - How to improve query with many-to-many relationship

I want select the all restaurants, and for the each restaurant load the list of the attached categories.

There is a many-to-many relationship between the Restaurant and Category :

在此处输入图片说明

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; }
}

In my current implementation I'm selecting a raw data with all restaurants and categories, and process them on the client side: group by restaurant and select categories. In this case, the generated SQL looks very simple and executed fast:

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()
        });

The another variant is to use Entity Framework and the relation between restaurants and categories. In this case, the generated SQL is very complicated and executed four times slower:

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

The question is: There is there a more efficient way (then 1 or 2) to select my data?

Your collection is virtual , thus, I suppose, you're using lazy loading.
The second variant executes N + 1 queries, where N is a count of items, returned from RestaurantsRepository.GetAll() : one query to get all restaurants, and N queries to get all categories for the particular restaurant.

Try to use eager loading of collection:

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

This should execute single query with JOIN against database, like this (real SQL will differ):

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

Also, think about lazy loading - do you really need it, if you're mapping query result into another types ( RestaurantModel in your sample).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM