Как правильно использовать вложенные запросы в EntityFramework? - PullRequest
1 голос
/ 20 февраля 2020

Я играю EntityFrameworkCore с WebAPI при создании приложения для голосования.

Я хочу сделать код в asyn c путь, где это возможно.

Так должен ли я каким-то образом использовать вложенный запрос в асин c (// Проблема 1, // Проблема 2)?

/* The target of the question - the query*/
var pollResults =
                await _context.Polls
                        .Select(poll => new PollDto
                        {
                            Id = poll.Id,
                            Question = poll.Question,
                            CreatedAt = poll.CreatedAt,
                            Options = poll.Options
                            .Select(option => new OptionDto
                            {
                                Id = option.Id,
                                Value = option.Value,
                                VotesCount = option.Votes.Count() // Problem 1
                            })
                            .ToList(), // Problem 2
                            LastVotedAt = _context.PollVotes.Where(vote=>vote.PollId == poll.Id).Select(vote => vote.VoteDate).SingleOrDefault()
                        })
                        .ToListAsync(); 

/* Domain classes */

public class Poll
    {
        public int Id { get; set; }
        public ICollection<PollOption> Options { get; set; } = new List<PollOption>();
        public ICollection<PollVote> Votes { get; set; } = new List<PollVote>();
    }

public class PollOption
    {
        public int Id { get; set; }
        public string Value { get; set; }
        public int PollId { get; set; }
        public Poll Poll { get; set; }
        public ICollection<PollVote> Votes { get; set; } = new List<PollVote>();
    }

 public class PollVote
    {
        public int Id { get; set; }
        public int PollId { get; set; }
        public Poll Poll { get; set; }
        public int OptionId { get; set; }
        public PollOption Option { get; set; }
        public DateTime VoteDate { get; set; }
    }

/* Dto classes */

public class PollDto
    {
        public int Id { get; set; }
        public string Question { get; set; }
        public ICollection<OptionDto> Options { get; set; } = new List<OptionDto>();
        public DateTime LastVotedAt { get; set; }
    }

public class OptionDto
    {
        public int Id { get; set; }
        public string Value { get; set; }
        public int VotesCount { get; set; }
    }

Таким образом, в не вложенных запросах Count и SingleOrDefault сделали бы запрос к базе данных, и он должен быть выполнен асин c способом. Но в моем случае весь запрос - это один запрос.

Должен ли я что-то изменить, чтобы сделать методы Count и SingleOrDefault asyn c способом? Или достаточно вызвать ToListAsyn c в конце?

Я полагаю, что ответ состоит в том, что 1 запрос к базе данных выполняется за 1 асин c вызов. Но я не нашел никакого решения в inte rnet.

1 Ответ

1 голос
/ 20 февраля 2020

ToListAsync() в конце достаточно. Выражения внутри запроса используются EF для составления запроса. Они не «выполняются» как SQL, как если бы они были как отдельные операторы для DbSets.

Например, когда я запускаю нечто подобное:

var parents = await context.Parents
    .Select(x => new
    {
        x.ParentId,
        x.Name,
        Children = x.Children.Select(c => new { c.ChildId, c.Name }).ToList(),
        ChildCount = x.Children.Count()
    }).ToListAsync();

в тесте и установите точку останова при запущенном профилировщике. Оператор создает один оператор SQL:

SELECT 
    [Project2].[ParentId] AS [ParentId], 
    [Project2].[Name] AS [Name], 
    [Project2].[C2] AS [C1], 
    [Project2].[C1] AS [C2], 
    [Project2].[ChildId] AS [ChildId], 
    [Project2].[Name1] AS [Name1]
    FROM ( SELECT 
        [Project1].[ParentId] AS [ParentId], 
        [Project1].[Name] AS [Name], 
        [Extent3].[ChildId] AS [ChildId], 
        [Extent3].[Name] AS [Name1], 
        CASE WHEN ([Extent3].[ChildId] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1], 
        [Project1].[C1] AS [C2]
        FROM   (SELECT 
            [Extent1].[ParentId] AS [ParentId], 
            [Extent1].[Name] AS [Name], 
            (SELECT 
                COUNT(1) AS [A1]
                FROM [dbo].[Children] AS [Extent2]
                WHERE [Extent1].[ParentId] = [Extent2].[ParentId]) AS [C1]
            FROM [dbo].[Parents] AS [Extent1] ) AS [Project1]
        LEFT OUTER JOIN [dbo].[Children] AS [Extent3] ON [Project1].[ParentId] = [Extent3].[ParentId]
    )  AS [Project2]
    ORDER BY [Project2].[ParentId] ASC, [Project2].[C1] ASC
go

Не три запроса, которые могут вас заинтересовать, заблокируют. Это было при просмотре свойств навигации для связанных записей.

Больший вопрос, который я увидел, глядя на ваш пример для двойной проверки, был такой строкой:

LastVotedAt = _context.PollVotes.Where(vote=>vote.PollId == poll.Id).Select(vote => vote.VoteDate).SingleOrDefault()

Как это будет go вернуться непосредственно к контексту, а не получить доступ к голосам через коллекцию на опросе. Но я тоже это попробовал, и это все равно привело к одному запросу.

Children = x.Children.Select(c => new { c.ChildId, c.Name }).ToList(),
ChildCount = x.Children.Count(),
YoungestChild = context.Children.OrderBy(c=>c.BirthDate).Where(c=>c.ParentId == x.ParentId).FirstOrDefault()

В моем тестовом примере я go вернулся в контекст, чтобы получить младшего потомка для родительской записи, а не для дочерних элементов. свойство навигации. В этом случае он по-прежнему выполняется как 1 запрос.

Для подобных вопросов я определенно рекомендую создать проект песочницы для экспериментов EF с локальной базой данных, а затем использовать инструмент профилирования и SQL для наблюдения за операторами SQL. производится и когда они выполняются. Asyn c полезен для запросов, выполнение которых, как ожидается, займет некоторое время, но его следует использовать экономно, поскольку они могут снизить общую производительность запросов, выполняемых при каждом тривиальном запросе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...