EF Core 5 Sum difference between two date times - .net-5

We have upgraded to net 5 / ef core 5, and many of our queries have needed "fixing" due to the changes that have been made.
In reality, the queries were horribly inefficient, because the LINQ could not be translated to SQL and now this has been brought to our attention; one solution is to force this to be worked on the client instead of the SQL Server... however I would like this to make this more efficient.
Below is a query that I can't find a way to do efficiently:
DateTime toDate = DateTime.Now.AddDays(NumberofDays + 1).Date;
MyInsights.AvailableHours = DatabaseContext.WorkCenterSchedules
.Where(x => x.ForWorkCenter.WorkCenterId == WorkCenterID && x.dateTo > DateTime.Now && x.dateTo < toDate)
.Sum(y => (y.dateTo - (DateTime.Now > y.dateFrom ? DateTime.Now : y.dateFrom)).TotalMinutes) / 60;
The error is:
This could be changed to the following:
DateTime toDate = DateTime.Now.AddDays(NumberofDays + 1).Date;
List<WorkCenterSchedule> schedules = DatabaseContext.WorkCenterSchedules
.Where(x => x.ForWorkCenter.WorkCenterId == WorkCenterID && x.dateTo > DateTime.Now && x.dateTo < toDate).ToList();
MyInsights.AvailableHours = schedules.Sum(y => (y.dateTo - (DateTime.Now > y.dateFrom ? DateTime.Now : y.dateFrom)).TotalMinutes) / 60;
However, this is far from ideal. I know EF Core isn't ideal for complex queries, but I feel like I am missing something obvious here, is there something I can do to improve this?

You might be able to leverage SQL Server's DATEDIFF function:
EF.Functions.DateDiffMinute(y.dateTo, DateTime.Now > y.dateFrom ? DateTime.Now : y.dateFrom)

Related

How to use date condition in Symfony (doctrine + oracle)?

How to use date condition in Symfony (doctrine + oracle) ?
I have trying
$qb = $this->createQueryBuilder('la')
->select('la.id')
->andWhere("la.dateCreated >= TO_DATE(':a', 'yyyy-mm-dd')")
->setParameter('a', '2020-09-01')
->getQuery();
However is error: the query defines 0 parameters and you bound 1
Any Ideas?
In your code, I don't think there should be quotes on ':a', I'd put :
TO_DATE(:a, 'yyyy-mm-dd')
Beside, how about :
$qb = $this->createQueryBuilder('la')
->select('la.id')
->andWhere("la.dateCreated >= :a)
->setParameter('a', '2020-09-01')
->getQuery();

DateTime logic in LINQ for SQL Server CE

My object 'Job' has LastTimeFinishedRunning which is DateTime and RunIntervalMinutes which is an int. I need to add them together and compare against current time.
Something like this:
Job jobToRun = ctx.Jobs.Where
(job => DateTime.Now > Job.LastTimeFinishedRunning.Add(TimeSpan.FromMinutes(job.RunIntervalMinutes))).FirstOrDefault();
Well, the .Add() doesn't work of course....
neither works this:
DateTime.Now > EntityFunctions.AddMinutes(job.LastTimeFinishedRunning, job.RunIntervalMinutes)
or this:
DateTime.Now > System.Data.Objects.SqlClient.SqlFunctions.DateAdd("minute", job.RunIntervalMinutes, job.LastTimeFinishedRunning)
...because it's SQL Server CE provider
Any suggestions? Thx.
Update:
I guess my only option here is to store ticks in LastTimeFinishedRunning by switching it from DateTime to long in DB. So the code would look something like this:
long ticks = DateTime.Now.Ticks;
Job jobToRun = ctx.Jobs.Where
(job => ticks > Job.LastTimeFinishedRunning + job.RunIntervalMinutes * 1000 * 10000).FirstOrDefault();

LINQ Performance Issue on only a few hundred records

in the following code i've commented on the line that SLOWS my page right down. I did some speed test to reveal the CONTAINS LINQ expression is the problem.
Does anyone know how to change this one line to be more efficient using something else instead. I'm also curious as to why its so slow.
Any ideas (thanks in advance):
var allWaste = _securityRepository.FindAllWaste(userId, SystemType.W);
var allWasteIndicatorItems = _securityRepository.FindAllWasteIndicatorItems();
// First get all WASTE RECORDS
var searchResults = (from s in allWaste
join x in allWasteIndicatorItems on s.WasteId equals x.WasteId
where (s.Description.Contains(searchText)
&& s.Site.SiteDescription.EndsWith(searchTextSite)
&& (s.CollectedDate >= startDate && s.CollectedDate <= endDate))
&& x.EWC.EndsWith(searchTextEWC)
select s).Distinct();
var results = searchResults.AsEnumerable();
if (hazardous != "-1")
{
// User has requested to filter on Hazardous or Non Hazardous only rather than Show All
var HazardousBoolFiltered = (from we in _db.WasteIndicatorItems
.Join(_db.WasteIndicators, wii => wii.WasteIndicatorId, wi => wi.WasteIndicatorId, (wii, wi) => new { wasteid = wii.WasteId, wasteindicatorid = wii.WasteIndicatorId, hazardtypeid = wi.HazardTypeId })
.Join(_db.HazardTypes, w => w.hazardtypeid, h => h.HazardTypeId, (w, h) => new { wasteid = w.wasteid, hazardous = h.Hazardous })
.GroupBy(g => new { g.wasteid, g.hazardous })
.Where(g => g.Key.hazardous == true && g.Count() >= 1)
select we).AsEnumerable(); // THIS IS FAST
// Now join the 2 object to eliminate all the keys that do not apply
if (bHazardous)
results = (from r in results join x in HazardousBoolFiltered on r.WasteId equals x.Key.wasteid select r).AsEnumerable(); //This is FAST
else
results = (from r in results.Where(x => !HazardousBoolFiltered
.Select(y => y.Key.wasteid).Contains(x.WasteId)) select r).AsEnumerable(); // This is DOG SLOW 10-15 seconds !--- THIS IS SLOWING EXECUTION by 10 times --!
}
return results.AsQueryable();
I suggest using a logging / tracing framework like smart inspect or log4net combined with a debug text writer.
http://www.codesprouts.com/post/View-LINQ-To-SQL-Statements-Using-A-Debug-TextWriter.aspx
another possibility is to use the sql server profiler and see what sql linq2sql produces.
also a very nice way is to use the mvc mini profiler in combination with the Profiled DB Connection and the SqlFormatters.SqlServerFormatter.
Try Any (MSDN)
Try this:
results = (from r in results
.Where(x => !HazardousBoolFiltered
.Any(y => y.Key.wasteid == r.WasteId)))
.AsEnumerable()
Or Count:
results = (from r in results
.Where(x => HazardousBoolFiltered
.Count(y => y.Key.wasteid == r.WasteId) == 0))
.AsEnumerable()

Consecutive (conditional) Where clauses in Linq

I'm trying to construct a Linq statement to be used from a client with the Sharepoint (2010) object model.
This is the problematic piece of code:
var result = news.Where(n => (bool)n["Online"]
&& ((DateTime)n["StartDate"] <= DateTime.Now && (DateTime)n["StopDate"] >= DateTime.Now));
if (currentUser.IsAgUser())
result = result.Where(n => (string)n["Role"] != "AG-ADMIN");
var filteredNews = sharepointContext.LoadQuery(result);
When the if parte is executed and so another Where is added to result, I get the followin error when LoadQuerying it.
The query expression 'value(Microsoft.SharePoint.Client.ListItemCollection).Where(n => (Convert(n.get_Item("Online")) AndAlso ((Convert(n.get_Item("StartDate")) <= DateTime.Now) AndAlso (Convert(n.get_Item("StopDate")) >= DateTime.Now)))).Where(n => (Convert(n.get_Item("Role")) != "AG-ADMIN"))' is not supported.
Where is the error coming from? Thanks
It seems like SharePoint doesn't support several wheres.
Cheap solution:
var result = currentUser.IsAgUser()
? news.Where(n => (bool)n["Online"]
&& ((DateTime)n["StartDate"] <= DateTime.Now && (DateTime)n["StopDate"] >= DateTime.Now) && (string)n["Role"] != "AG-ADMIN")
: news.Where(n => (bool)n["Online"]
&& ((DateTime)n["StartDate"] <= DateTime.Now && (DateTime)n["StopDate"] >= DateTime.Now));
var filteredNews = sharepointContext.LoadQuery(result);

ArgumentError invalid date in DateTime#change

When doing
startTime = DateTime.now
startTime = startTime.change(:min => (startTime.min / 5.to_f).ceil * 5)
our production server occasionally produces the following exception
A ArgumentError occurred in controller#action:
invalid date
/opt/ruby-enterprise-1.8.7-2009.10/lib/ruby/1.8/date.rb:1519:in `civil'
vendor/bundle/ruby/1.8/gems/activesupport-3.0.9/lib/active_support/core_ext/date_time/calculations.rb:37:in `change'
And I just can't figure out what causes the problem nor reproduce it in my development environment. Am I doing something wrong, or what is happening here? What I want to do is create a DateTime instance which is rounded up to the closest 5 min from now.
If DateTime.now.min > 55, then you are setting the min value to 60.
If you only need the time part:
startTime = DateTime.now
new_minute = startTime.min / 5.to_f).ceil * 5
new_hour = DateTime.now.hour
if new_minute == 60
new_minute = 0
new_hour = new_hour + 1
end
new_time = startTime.change(:min => new_minute, :hour => new_hour)
if you need more than this, I'd suggest using activesupport. Then you could do
new_time = startTime + (new_minute - min).minutes

Resources