EF Core Group By работает на sqlite, но не на сервере SQL - PullRequest
2 голосов
/ 04 августа 2020

Если у меня есть следующий оператор EF Core

var totalCallsOnHealthRelatedIssue = await _context.CallReasons
    .Where(cr => cr.Call.IsDeleted != true 
            && (cr.Call.CallStartDateTime >= filterStartDate 
                && cr.Call.CallStartDateTime <= filterEndDate) 
                && cr.Reason.ReasonTypeId == 2)
    .GroupBy(cr => cr.CallId)
    .CountAsync(); // Reason: Any type of health issue is counted ReasonType: Health Issue == 2

Он работает нормально на SQLite, но когда я меняю сервер базы данных на SQL Sever 2019, он не работает.

Ошибка: столбец CallReasons.ReasonId недопустим в списке выбора, потому что он не содержится ни в агрегатной функции, ни в предложении GROUP BY.

Подробная ошибка

fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
      Failed executing DbCommand (152ms) [Parameters=[@__filterStartDate_0='2020-08-01T00:00:00', @__filterEndDate_1='2020-08-25T23:59:59'], CommandType='Text', CommandTimeout='30']
      SELECT COUNT(*)
      FROM (
          SELECT [c].[CallId], [c].[ReasonId]
          FROM [CallReasons] AS [c]
          INNER JOIN [Calls] AS [c0] ON [c].[CallId] = [c0].[Id]
          INNER JOIN [Reasons] AS [r] ON [c].[ReasonId] = [r].[Id]
          WHERE (([c0].[IsDeleted] <> CAST(1 AS bit)) AND (([c0].[CallStartDateTime] >= @__filterStartDate_0) AND ([c0].[CallStartDateTime] <= @__filterEndDate_1))) AND ([r].[ReasonTypeId] = 2)
          GROUP BY [c].[CallId]
      ) AS [t]
fail: Microsoft.EntityFrameworkCore.Query[10100]
      An exception occurred while iterating over the results of a query for context type 'Infrastructure.Data.ApplicationDbContext'.
      Microsoft.Data.SqlClient.SqlException (0x80131904): Column 'CallReasons.ReasonId' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
         at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__164_0(Task`1 result)
         at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
         at System.Threading.Tasks.Task.<>c.<.cctor>b__274_0(Object obj)
         at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
      --- End of stack trace from previous location where exception was thrown ---
         at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
         at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
      --- End of stack trace from previous location where exception was thrown ---
         at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
      ClientConnectionId:294c90c0-ce11-439c-811b-858be9623680
      Error Number:8120,State:1,Class:16
Microsoft.Data.SqlClient.SqlException (0x80131904): Column 'CallReasons.ReasonId' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
   at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__164_0(Task`1 result)
   at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
   at System.Threading.Tasks.Task.<>c.<.cctor>b__274_0(Object obj)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
ClientConnectionId:294c90c0-ce11-439c-811b-858be9623680
Error Number:8120,State:1,Class:16
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2]
      Executed action API.Controllers.ReportsController.GetMTHotlineReport (API) in 1260.4704ms
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint 'API.Controllers.ReportsController.GetMTHotlineReport (API)'
fail: System.Exception[0]
      Column 'CallReasons.ReasonId' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 1774.7343ms 500 application/json

1 Ответ

2 голосов
/ 05 августа 2020

Это одна из многих проблем с GroupBy в EF-core 3. аналогичная проблема опубликована на github.

Обычный совет - группируйте на стороне клиента, т.е. после выбора необходимых данных и AsEnumerable(). Но в этом случае вам нужно только подсчитать группы. Было бы излишним загружать все данные в клиент, а затем производить подсчет.

Значит, вам нужен обходной путь. Один из них, который я могу найти, - это помочь EF и явно выбрать столбец группирования:

var totalCallsOnHealthRelatedIssue = await _context.CallReasons
    .Where(cr => !cr.Call.IsDeleted 
              && (cr.Call.CallStartDateTime >= filterStartDate 
              && cr.Call.CallStartDateTime <= filterEndDate) 
              && cr.Reason.ReasonTypeId == 2)
    .Select(cr => cr.CallId)
    .GroupBy(id => id)
    .CountAsync();

Совсем не приятно.

Но это типичный пример туннельного зрения, когда по работе с проблемами GroupBy. В вашем случае вы можете просто подсчитать разные идентификаторы:

var totalCallsOnHealthRelatedIssue = await _context.CallReasons
    .Where(cr => !cr.Call.IsDeleted 
              && (cr.Call.CallStartDateTime >= filterStartDate 
              && cr.Call.CallStartDateTime <= filterEndDate) 
              && cr.Reason.ReasonTypeId == 2)
    .Select(cr => cr.CallId)
    .Distinct()
    .CountAsync();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...