Search code examples
asp.net-coreasync-awaitmediatr

Why is my ASP.NET Core controller performance slow when using MediatR and async/await?


I'm using MiniProfiler to try to figure out why the performance of my ASP.NET Core 5.0 web application is slower than anticipated.

I've narrowed down the issue to the MediatR call in the controller (1201 ms to execute), but it's odd because when I dig down to the handler that MediatR executes, the entire body of the handler runs in less than 50 ms (including the Entity Framework Core database call to MS SQL Server).

Here is my relevant code:

Controller

        public async Task<IActionResult> Index(GetAnnouncementsRequest query)
        {
            using (MiniProfiler.Current.Step("[AnnouncementsController] Index"))
            {
                IEnumerable<GetAnnouncementsDto> model;

                using (MiniProfiler.Current.Step("[AnnouncementsController] Index: MediatR"))
                {
                    model = await _mediator.Send(query)
                       .ConfigureAwait(false);
                }

                return View(model);
            }
        }

Handler

        public async Task<IEnumerable<GetAnnouncementsDto>> Handle(GetAnnouncementsRequest request, CancellationToken cancellationToken)
        {
            using (MiniProfiler.Current.Step("[GetAnnouncementsRequest] Handle"))
            {
                List<Announcement> announcements;
                IReadOnlyCollection<GetAnnouncementsDto> announcementsDto;

                using (MiniProfiler.Current.Step("[GetAnnouncementsRequest] Handle: GetAllActiveAsync"))
                {
                    announcements = await _announcementRepository.GetAllActiveAsync()
                        .ConfigureAwait(false);
                }

                using (MiniProfiler.Current.Step("[GetAnnouncementsRequest] Handle: Mapper"))
                {
                    announcementsDto = _mapper.Map<List<Announcement>, List<GetAnnouncementsDto>>(announcements)
                        .OrderByDescending(announcement => announcement.EditedDate)
                        .ThenByDescending(announcement => announcement.PostedDate)
                        .ToList()
                        .AsReadOnly();
                }

                return announcementsDto;
            }
        }

Repository

        public async Task<List<Announcement>> GetAllActiveAsync()
        {
            var announcements = await GetAll()
                .Where(announcement => announcement.IsActive)
                .ToListAsync()
                .ConfigureAwait(false);

            return announcements;
        }

This is the data the profiler is returning:

MiniProfiler Results

Is there an issue with my usage of nested async/await? Am I using the Mediator pattern incorrectly? Or am I not using MiniProfiler the right way? I can't figure out why the MediatR call is so slow.

For reference, I'm using a traditional WISA stack (Windows/IIS/SQL Server/ASP.NET Core) on an on-prem server.


Solution

  • It turns out that I had a service injected that did some busy authentication work in the middle of the request that I had set to Scoped. I changed it to Singleton and it completely fixed the issue. Now my pages are loading in 10-20 ms.