Entity framework "Select date time from string" - c#

I need to select the records from a table for date interval.
But the date to select from is kept as nvarchar in this pattern
20160511_155015 (yyyymmdd_hhmmss)
I cannot use ToList() to make it as DateTime.ParseExact(entry.StartDate, "yyyyMMdd_HHmmss", CultureInfo.InvariantCulture)
The table keeps several millions records.
So I need to make something like this:
var preQuery = context.Table
.AsNoTracking()
.Select(x => new
{
StartDate = ConvertFrom()),
Mode = x.Mode,
SessionStart = x.AStart,
SessionEnd = x.AEnd,
x.SensorNumber
})
.Where(x => x.StartDate != null
&& x.StartDate >= startDate
&& x.StartDate <= endDate)
.ToList();
Is it possible to convert the string representation to Datetime and then proceed with Where clause ?

Please try this DateTime.ParseExact(dateTime ,format,CultureInfo.InvariantCulture);
var preQuery = context.Table.AsNoTracking().Select(x => new
{
StartDate = ConvertFrom(),
Mode = x.Mode,
SessionStart = DateTime.ParseExact(x.AStart,"yyyyMMdd_HHmmss", CultureInfo.InvariantCulture),
SessionEnd = DateTime.ParseExact(x.AEnd,"yyyyMMdd_HHmmss", CultureInfo.InvariantCulture),
x.SensorNumber
})
.Where(x => x.StartDate != null && x.StartDate >= startDate && x.StartDate <= endDate)
.ToList();

You can try:
StartDate.StartsWith("20160511") or so..
or
Convert.ToInt32(StartDate.Substring(0,8)) > 20160511
by the way, i think you might want to run a script, as long as it takes, and create a new column which will generate a DateTime based on that column

Related

How can I group datetime using Linq method syntax but ignoring time section

I'm working on an ASP.NET MVC app written in C#, and I need to display sum of how many times people have viewed videos captured in a SQL database. I'm able to do it in SQL:
SELECT
CAST([Date] AS DATE),
SUM([Views])
FROM
db
WHERE
[DATE] >= '2022-01-28'
AND [DATE] <= '2022-07-28'
GROUP BY
CAST([Date] AS DATE)
But, now the problem is to translate it to Linq in my controller, this is what I have so far (where start and end date are variables):
var data = (Context.Where(w => w.Date >= startDate && w.Date <= endDate)
.GroupBy(g => new { date = g.Date })
.Select(s => new { date = s.Key, sum = s.Sum(c => c.Views) })).ToList();
This will work but will group by date and time which has more rows.
For example, what my code returns:
date = 3/3/2022 3:00:00 AM, sum = 1
date = 3/3/2022 4:00:00 AM, sum = 2
date = 3/3/2022 5:00:00 AM, sum = 3
What I want:
date = 3/3/2022, sum = 6
You can simply use EntityFunctions.TruncateTime. I had in the past tried to put in a PR to allow using DateTime.Date, not sure if it ever landed.
var data = (Context
.Where(w => w.Date >= startDate && w.Date <= endDate)
.GroupBy(g => EntityFunctions.TruncateTime(g.Date))
.Select(s => new { date = s.Key, sum = s.Sum(c => c.Views) })
).ToList()

How to filter only when date is not null using optional condition

I am a little confused about how I can use the date as an optional condition.
if there is a date then <= of date, if the date is null then don't filter based on date.
My code is like this
DateTime date= DateTime.UtcNow.AddDays(-10);
foreach (var camEvent in dbContext
.CAM_EVENTS
.Where(c => c.USER_ID == userID &&
c.CAM_ID == cam.CAM_ID &&
c.EVENT_DATE >= date) // I want to change this like
.OrderByDescending(c => c.DATE))
{...}
I want that line to look something like this
(date && c.EVENT_DATE >= date)
so it only filter when date is not null, but this is not working.
I'd do the following logic:
(date==null || (c.EVENT_DATE >= date))
You can do something like this:
DateTime date = DateTime.UtcNow.AddDays(-10);
var filteredContext = dbContext
.CAM_EVENTS
.Where(c => c.USER_ID == userID &&
c.CAM_ID == cam.CAM_ID)
.OrderByDescending(c => c.DATE);
if (date != null) {
filteredContext = filteredContext.Where(c.EVENT_DATE >= date);
}
foreach (var camEvent in filteredContext) {
...
}
You can use a ternary operator, also known as a conditional operator.
foreach (var camEvent in dbContext
.CAM_EVENTS
.Where(c => c.USER_ID == userID &&
c.CAM_ID == cam.CAM_ID &&
// if date is not null, it will return bool c.EVENT_DATE >= date, otherwise just true
(date != null ? c.EVENT_DATE >= date : true))
.OrderByDescending(c => c.DATE))

Changing my stored procedure to Linq

I have a question regarding how to convert the following to linq. I've been trying to get my head around ASP.NET MVC, and one of the biggest hurdles for me is Linq.
CREATE PROCEDURE [dbo].[SelectAvailableCoops]
#startDate DATETIME, #endDate DATETIME
AS
BEGIN
SET NOCOUNT ON;
SELECT ID, coopName, coopCPN
FROM tbl_Coops
WHERE ID NOT IN (SELECT coopID
FROM tbl_bookings
WHERE (startDate <= #startDate AND endDate >= #startDate)
OR (startDate < #endDate AND endDate >= #endDate)
OR (#startDate <= startDate AND #endDate >= endDate)
)
END
GO
If it helps, at the moment I have the following in my action result.
BookingsListVM bookinglist = new BookingsListVM();
//bookinglist.Customers = db.Customers.ToList();
bookinglist.Customers = (from c in db.Customers select c).ToList();
bookinglist.CustomerBookings = (from cb in db.CustomerBookings select cb).ToList();
bookinglist.Coops = (from co in db.Coops select co).ToList();
Well here is one
db.Customers.Where(x=> db.CustomerBookings.Any(a=> a.coopID != x.Id &&
((a.StartDate <= startDate && a.EndDate >= enddate)
||
(a.StartDate < endDate && a.EndDate >= enddate)
||
(a.StartDate <= startDate && a.EndDate >= endDate)
)));
The other answer looks better but here is a slightly longer solution that is hopefully a bit easier to understand.
//Returns IDs
var bookingIds = tbl_bookings
.Where(b => b.ReleaseDate <= startDate && b.ReleaseDate >= endDate
|| (b.ReleaseDate < endDate && b.ReleaseDate >= endDate)
|| (b.ReleaseDate <= startDate && b.ReleaseDate >= endDate))
.Select(b => b.Id);
//Returns anonymous object list containing all records
//with an ID not found in previous result set
var bookings = tbl_bookings.Where(b => !bookingIds.Contains(b.Id))
.Select(b => new { ID = b.Id, CoopName = b.Id, coopCPN = b.Id })
.ToList();

LINQ DateTime Query that ignores milliseconds

x.CreateDate DateTime is stored in our database down to milliseconds. My dateTimePicker values startdate and enddate only allows for querying down to seconds.
How can change my query to ignore the milliseconds of x.CreateDate? I thought the code I wrote below would work but it is not.
if (stardDateIsValid && endDateIsValid && startdate == enddate)
query = _context.Logs
.Where(x => x.ApplicationID == applicationId &&
x.CreateDate.AddMilliseconds(-x.CreateDate.Millisecond) == startdate)
.OrderByDescending(x => x.ID)
.Take(count);
var query = from l in _context.Logs
where l.ApplicationID == applicationId
&& SqlMethods.DateDiffSecond(l.CreateDate,startdate) == 0
orderby l.ID descending
select l).Take(count);
This avoids converting every date in you table into a string and the subsequent string comparison, by comparing the two dates as dates.
Getting CreateDate and startdate in the same format will help you compare apples to apples. This should accomplish that.
if (stardDateIsValid && endDateIsValid && startdate == enddate)
query = _context.Logs
.Where(x => x.ApplicationID == applicationId &&
x.CreateDate.ToString(#"MM/DD/YYYY h:mm:ss") == startdate.ToString(#"MM/DD/YYYY h:mm:ss")
.OrderByDescending(x => x.ID)
.Take(count);
I have no idea why I could not get any results from the queries posted above as I tried several variations of their themes. However I did get it working correctly by adding milliseconds to the startdate and enddate variables and it s working.
if (stardDateIsValid && endDateIsValid)
startdate = startdate.AddMilliseconds(000);
enddate = enddate.AddMilliseconds(999);
query = _context.Logs.Where(x => x.ApplicationID == applicationId && x.CreateDate >= startdate && x.CreateDate <= enddate).OrderByDescending(x => x.ID).Take(count);
You can create extension method.
public const long TicksPerMillisecond = 10000;
public const long TicksPerSecond = TicksPerMillisecond * 1000;
public static bool IsEqualIgnoreMilliseconds(this DateTime date, DateTime compareDate)
{
long tickDiff = date.Ticks - compareDate.Ticks;
return tickDiff > 0 ? tickDiff < TicksPerSecond : tickDiff < -TicksPerSecond;
}
Then you can use this:
if (stardDateIsValid && endDateIsValid && startdate == enddate)
query = _context.Logs
.Where(x => x.ApplicationID == applicationId &&
x.CreateDate.IsEqualIgnoreMilliseconds(startdate)
.OrderByDescending(x => x.ID)
.Take(count);

How to write the following query in LINQ

I have a sql query and would like to convert it into linq
SELECT CAST([Date] AS DATE),
COUNT([ID]) AS 'Amount of Systems'
FROM [DemoDB].[dbo].[Servers]
WHERE [ServerID] IN ('ServerX') AND [Type] = 'Complete'
GROUP BY CAST([Date] AS DATE)
ORDER BY CAST([Date] AS DATE)
this will return the result as follows
What I have tried
//fromDP and toDP are the names of the Datepicker's
var query = (this.db.Servers
.Where(x => x.Date >= fromDP.SelectedDate.Value &&
x.Date <= toDP.SelectedDate.Value));
var query_Success = query.Count(p => p.Type == "Complete"
&& (p.ServerID == "ServerX"));
and I have the result as Count on the whole ( for example, if I select from from April 1st to April 15th , the result is the sum of all "complete"), but I need count for each day in this selected range. the result I will bind to the column chart.
how to proceed ?
If I understood correctly the author wants to use only the date without the time. To do this with EF we can use the method EntityFunctions.TruncateTime for trimming the time portion. I will build on #steaks answer:
db.Servers.Where(s => s.ServerId == "ServerX" && s.Type == "Complete")
.GroupBy(s => EntityFunctions.TruncateTime(s.Date))
.OrderBy(s => s.Key)
.Select(g => new {Date = g.Key, AmountOfSystems = g.Count()});
this.db.Servers.Where(s => s.ServerId == "ServerX" && s.Type == "Complete")
.GroupBy(s => s.Date)
.OrderBy(s => s.Key)
.Select(g => new { Date = g.Key, AmountOfSystems = g.Count() });
Change the Where clause to read
Where(s => s.ServerId == "ServerX" && s.Type == "Complete" && s.Date >= fromDP.SelectedDate.Value && s.Date <= toDP.SelectedDate.Value)
to filter to a limited date range.
EDIT
As #vvs0205 suggested. Use EntityFunctions class to manipulate the date column as you please: http://msdn.microsoft.com/en-us/library/system.data.objects.entityfunctions.aspx
Something like this
var fromDate = fromDP.SelectedDate.Value;
var toDate= toDP.SelectedDate.Value;
var q = from server in this.db.Servers
where (server.Date >= fromDate && server.Date<=toDate && server.ServerID="ServerX" && server.Type=="Complete")
group server by server.Date
into g
orderby g.Key
select new
{
Date = g.Key,
Count = g.Count()
};
var results = q.ToList();

Categories