DBContext object LINQ Sum(time) column to hours

三世轮回 提交于 2021-01-29 16:53:10

问题


This is an ASP.Net Core Web API Project, using EntityFrameWorkCore. I need to convert TotalLoggedInTime (format: 139:05:40) to Hours and Sum the hours and assign it to logonHours, it fails on that line. The first line for callsOffered works fine.

C# Code:

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 Table

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

Error

The LINQ expression 'TimeSpan.Parse((EntityShaperExpression: EntityType: MyObj ValueBufferExpression: (ProjectionBindingExpression: EmptyProjectionMember) IsNullable: False ).TotalLoggedInTime).TotalHours' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

Solution:

Using the solution provided by @Arcord. I had to add an extension method for converting the hours, but that's beyond the scope of this original issue.

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:


It's because it tries to convert your code to SQL.

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

Of course everything is not convertible to SQL so in that case it fail.

You can use this :

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

It should works but the ".ToList()" will force EF to fetch all data from the database and then (since everything is in memory) the code can be applied.

But only do this if you have a small amount of data! It's better not to put that kind of stuff in production if you don't know the expected load.

Edit

You could also also considers to store "ticks" instead of a string (like described here). In that case you will the able to do.

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

More work will be done on the SQL server and you will only gather the sum.

You should also considers to use "async" version of methods (ToListAsync, SumAsync, ...)



来源:https://stackoverflow.com/questions/62457480/dbcontext-object-linq-sumtime-column-to-hours

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!