Почему контекст БД используется после уничтожения в этом запросе LINQ?

#c# #async-await #linq-to-sql #entity-framework-core #ef-core-2.2

#c# #асинхронное ожидание #linq-to-sql #entity-framework-core #ef-core-2.2

Вопрос:

У меня есть следующий запрос и ядро EF 2.2.6

 public async Task<List<SamplePointContamination>> GetSamplePointsContaminationAsync(int siteId, int buildingId)
        {
            var siteZones = await administrationApiClient.GetAsync<List<BuildingZone>>($"sites/{siteId}/buildingZones",
                new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("IncludeBuildings", "True") });
            var buildingZoneIds = siteZones.Where(x => x.Building.Id == buildingId).Select(x => x.Id);
            if (!buildingZoneIds.Any())
                throw new SecurityException("Building does not belong to the site.");

            var timeFrom = DateTimeOffset.UtcNow.AddDays(-9);
            using (var dbContext = CreateDbContext())
            {             
                //TODO optimize query. Generated sql does not promise
                return await dbContext.Analyses
                    .Where(x => buildingZoneIds.Contains(x.Sample.SamplePoint.BuildingZoneId) amp;amp; x.CreatedAt >= timeFrom
                    amp;amp; x.Sample.SamplePoint.Top.HasValue amp;amp; x.Sample.SamplePoint.Left.HasValue)
                    .Include(x => x.Sample.SamplePoint)
                    .Include(x => x.AnalysisRisks)
                        .ThenInclude(x => x.Risk)
                    .GroupBy(x => new
                    {
                        x.Sample.SamplePoint.Id,
                        x.Sample.SamplePoint.Name,
                        x.Sample.SamplePoint.Top,
                        x.Sample.SamplePoint.Left
                    })
                    .Select(x => new SamplePointContamination()
                    {
                        Id = x.Key.Id,
                        Name = x.Key.Name,
                        Top = x.Key.Top.Value,
                        Left = x.Key.Left.Value,
                        WaitingResults = x.Count(y => !y.ResultPositive.HasValue),
                        RiskTypes = x.SelectMany(y => y.AnalysisRisks)
                        .GroupBy(y => new { Id = (int)y.Risk.RiskType, Name = y.Risk.RiskType.ToString() })
                        .Select(y => new RiskTypeContamination() 
                            {
                                Id = y.Key.Id, 
                                Name = y.Key.Name,  
                                Positives = y.Count(z => z.Analysis.ResultPositive == true),
                                Negatives = y.Count(z => z.Analysis.ResultPositive == false),
                                IsPositive = y.Any(z => z.Analysis.ResultPositive == true)
                        }).ToList() //Crashes if ToList() is omitted. If using statement is removed, ToList() is not needed
                    })
                    .ToListAsync();
            }                       
        }
  

Первая проблема заключается в том, что базовый SQL-запрос очень неэффективен, потому что GROUP BY почти никогда не применяется к базе данных (но выполняется в памяти), за исключением самых простых случаев, но это не тема.

Тема заключается в том, что по какой-то причине приложение выходит из строя, если ToList() опущен (5-я строка от конца кода, который комментируется). Кроме того, нет ошибки, если я удаляю оператор using . Все мои запросы используют этот шаблон с использованием, и до сих пор у меня не было ни одной проблемы. Я знаю, что мне не нужно использовать using , но, по моему мнению, при использовании таким образом это не должно создавать проблем, поскольку я никогда не пытаюсь получить доступ к экземпляру DbContext за пределами этой области. Отображается эта ошибка:

Майкрософт.EntityFrameworkCore: не удается получить доступ к удаленному объекту. Распространенной причиной этой ошибки является удаление контекста, который был разрешен при внедрении зависимостей, а затем последующая попытка использовать тот же экземпляр контекста в другом месте вашего приложения. Это может произойти, если вы вызываете Dispose() для контекста или переносите контекст в оператор using . Если вы используете внедрение зависимостей, вы должны позволить контейнеру внедрения зависимостей позаботиться об удалении экземпляров контекста. Имя объекта: ‘Context’.

Итак, очевидно, что DbContext снова используется после уничтожения, но я не делаю этого в своем коде явно. Очевидно, что это имеет какое-то отношение к async / await, и по некоторым причинам добавление упомянутого ToList() решает проблему, но я понятия не имею, что происходит и почему ошибка существует без нее.

Комментарии:

1. Контекст удаляется в конце using , но запрос начинается с ToListAsync continue в асинхронной задаче.

2. Можете ли вы опубликовать список исключений?

3.без ToList() SamplePointContamination.RiskTypes is IQueryable , который не «материализуется», поэтому доступ к нему вызовет это исключение (поскольку он будет запрашивать базу данных)

4. я бы предложил переписать ether с использованием правильных объединений или ToListAsync данных, а затем поработать с in mem над возвращаемыми данными var data = await dbContext.Analyses .Where(x => buildingZoneIds.Contains(x.Sample.SamplePoint.BuildingZoneId) amp;amp; x.CreatedAt >= timeFrom amp;amp; x.Sample.SamplePoint.Top.HasValue amp;amp; x.Sample.SamplePoint.Left.HasValue) .Include(x => x.Sample.SamplePoint) .Include(x => x.AnalysisRisks) .ThenInclude(x => x.Risk).ToListAsync() , и @Selvin, вероятно, прав.

5. Насколько я помню, в версиях ядра EF до 3 — EF может решить оценить любую часть запроса на клиенте, если он не может преобразовать ее в правильный запрос к базе данных. Я бы предложил начать с полного отключения этой функции (например, как описано здесь: compiledexperience.com/blog/posts/ef-core-client-side-eval ), потому что если EF не может оценить ваш запрос — он должен выдать исключение. В этом случае, скорее всего, последний выбор не может быть преобразован в запрос и поэтому оценивается на клиенте, что потенциально очень дорого, а также приводит к вашей проблеме.