问题
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