Столбец LINQ Sum (time) объекта DBContext в часы - PullRequest
0 голосов
/ 18 июня 2020

Это ASP. Net Core Web API Project, использующий EntityFrameWorkCore. Мне нужно преобразовать TotalLoggedInTime (формат: 139: 05: 40) в Hours и Суммировать часы и назначить его logonHours, в этой строке он не работает. Первая строка для callsOffered работает нормально.

C# Код:

internal void Hydrate(DbSet<MyObj> myObj)
{
    string callsOffered = myObj.Sum(a => a.CallsPresented).ToString();
    string logonHours = myObj.Sum(a => TimeSpan.Parse(a.TotalLoggedInTime).TotalHours).ToString();
}

SQL Таблица

SELECT total_logged_in_time FROM mytable

total_logged_in_time
139:05:40
157:22:46
157:26:51
148:17:42
157:23:14
138:04:28
175:27:41
162:35:44
163:13:13
144:36:12
9:01:56

Ошибка

Выражение LINQ TimeSpan.Parse ((EntityShaperExpression: EntityType: MyObj ValueBufferExpression: (ProjectionBindingExpression: EmptyProjectionMember) IsNullable: False) .TotalLoggedInTime) .TotalHours не может быть переведено. Либо перепишите запрос в форме, которая может быть переведена, либо явно переключитесь на оценку клиента, вставив вызов либо AsEnumerable (), AsAsyncEnumerable (), ToList (), либо ToListAsyn c (). См. https://go.microsoft.com/fwlink/?linkid=2101038 для получения дополнительной информации.

Решение:

Использование решения, предоставленного @Arcord. Мне пришлось добавить метод расширения для преобразования часов, но это выходит за рамки этой исходной проблемы.

string logonHours = myObj.ToList().Sum(a => a.TotalLoggedInTime.ToHours()).ToString();

public static double ToHours(this string valueInTimeFormat)
{
    double hours = 0;

    //hours = TimeSpan.Parse(valueInTimeFormat).TotalHours;

    int[] ssmmhh = { 0, 0, 0 };
    var hhmmss = valueInTimeFormat.Split(':');
    var reversed = hhmmss.Reverse();
    int i = 0;
    reversed.ToList().ForEach(x => ssmmhh[i++] = int.Parse(x));
    hours = (int)(new TimeSpan(ssmmhh[2], ssmmhh[1], ssmmhh[0])).TotalHours;

    return hours;
}

1 Ответ

3 голосов
/ 18 июня 2020

Это потому, что он пытается преобразовать ваш код в SQL.

string logonHours = myObj.Sum(a => TimeSpan.Parse(a.TotalLoggedInTime).TotalHours).ToString();

Конечно, все не может быть преобразовано в SQL, поэтому в этом случае он потерпит неудачу.

Вы можете используйте это:

string logonHours = myObj.ToList().Sum(a => TimeSpan.Parse(a.TotalLoggedInTime).TotalHours).ToString();

Он должен работать но «.ToList ()» заставит EF извлечь все данные из базы данных, а затем (поскольку все находится в памяти) код может применяться.

Но делайте это только при небольшом количестве данных! Лучше не запускать такие вещи в производство, если вы не знаете ожидаемую нагрузку.

Edit

Вы также можете рассмотреть возможность хранения «тиков» вместо строки (как описано здесь ). В этом случае вы сможете это сделать.

string logonHours = TimeSpan.FromTicks(myObj.Sum(a => a.TotalLoggedInTime)

На сервере SQL будет проделана дополнительная работа, и вы соберете только сумму.

Вам также следует рассмотреть возможность использования "asyn c" версия методов (ToListAsyn c, SumAsyn c, ...)

...