Time of DateTime C# MVC - c#

I am trying to compare 24 hour time format stored as a string to the current time as follows
[AllowAnonymous]
public ActionResult _ProgOn()
{
int i = (int)DateTime.Now.DayOfWeek;
DateTime dt = DateTime.Now;
var onNow = db.Programs
.Where(u => u.PrOk == true)
.Where(u => u.DaId == i)
.Where(u => u.TimeOn == dt.ToString("HH:mm"));
return PartialView(onNow);
But that seems not to be acceptable to Linq query. I will be grateful if I am pointed to the right direction.

Pass time string to Linq query:
int i = (int)DateTime.Now.DayOfWeek;
string time = DateTime.Now.ToString("HH:mm");
var onNow = from u in db.Programs
where u.PrOk &&
u.DaId == i &&
u.TimeOn == time
select u;
return PartialView(onNow);
Also you don't need to compare boolean value with true/false. And I think query syntax looks better here.

Linq will only be able to use what it can translate. Try doing the conversion of the DateTime to a string and then do the comparison:
[AllowAnonymous]
public ActionResult _ProgOn()
{
string t = dt.ToString("HH:mm");
int i = (int)DateTime.Now.DayOfWeek;
DateTime dt = DateTime.Now;
var onNow = db.Programs
.Where(u => u.PrOk == true)
.Where(u => u.DaId == i)
.Where(u => u.TimeOn == t);
return PartialView(onNow);

Related

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))

DateTime ParseExact gives System.FormatException:

I am getting the following error:
System.FormatException: 'String '{ ActualInstallDate = 17/04/2020 09:06:23 }' was not recognised as a valid DateTime.
I have to add that in the database ActualInstallDate is nullable, that's why I'm converting it to string, to 'trick' it. The error comes at parsing actualInstallDates. Not sure what's wrong here.
Any suggestion is highly appreciated.
var actualInstallDates = _context.Units.Where(u => u.ID == unitID)
.Select(u => new {
u.ActualInstallDate
}).Single().ToString();
if (actualInstallDates != null)
{
DateTime actualInstallDate = DateTime.ParseExact(actualInstallDates, "dd/MM/yyyy", CultureInfo.InvariantCulture);
var nowString = DateTime.Now.ToString("dd/MM/yyyy");
DateTime now = DateTime.ParseExact(nowString, "dd/MM/yyyy", CultureInfo.InvariantCulture);
var averageWorkOver = (Math.Ceiling(Math.Ceiling(Convert.ToDecimal(now - actualInstallDate)) / 1000 * 60 * 60 * 24) / workOverList.Count).ToString();
ViewBag.AverageWorkOver = averageWorkOver;
}
[Column(TypeName = "datetime2")]
[Display(Name = "ActualInstallDate", Description = "ActualInstallDate_Description", ResourceType = typeof(Resource.Models.Unit))]
public DateTime? ActualInstallDate { get; set; }
You did'nt specify time in the format string. You can either truncate the time part from the date string or add time to the format string.
something like this:
DateTime actualInstallDate = DateTime.ParseExact(actualInstallDates, "dd/MM/yyyy HH:mm:ss", CultureInfo.InvariantCulture);
First of all you don't need to select it into anonymous object (if ActualInstallDate is string in database):
var actualInstallDates = _context.Units
.Where(u => u.ID == unitID)
.Select(u => u.ActualInstallDate)
.SingleOrDefault();
if (actualInstallDates != null)
{...}
If you still want to, the don't call ToString on it:
var actualInstallDates = _context.Units.Where(u => u.ID == unitID)
.Select(u => new { u.ActualInstallDate })
.Single();
if (actualInstallDates.ActualInstallDate != null)
{
DateTime.ParseExact(actualInstallDates.ActualInstallDate ...)
...
}
Secondary your date format does not match with the string, try using
DateTime.ParseExact(actualInstallDates, "dd/MM/yyyy hh:mm:ss", CultureInfo.InvariantCulture)
UPD
Since your field is DateTime? it seems that you've been missing Value property on Nullable<> struct so you just need to fetch your date from db and use actualInstallDates.Value:
var actualInstallDates = _context.Units
.Where(u => u.ID == unitID)
.Select(u => u.ActualInstallDate)
.Single()
if (actualInstallDates != null)
{
var averageWorkOver = (Math.Ceiling(Math.Ceiling(Convert.ToDecimal(DateTime.Now - actualInstallDates.Value)) / 1000 * 60 * 60 * 24) / workOverList.Count).ToString();
ViewBag.AverageWorkOver = averageWorkOver;
}

How to efficiently grab results from Linq with or without user input params

I have a linq query that I would like to return results with user input data. However, if this function gets called and there is zero data from user, OR user just wants to search via data, OR just one of the other parameters, how can I efficiently write the linq to accommodate for this? Here is the Linq and function:
public static List<Objects.Logs.GenericLog> GetLogs(int entityId, int logLevelId,
DateTime startDate, DateTime endDate)
{
var logsList = new List<Objects.Logs.GenericLog>();
using(var db = CORAContext.GetCORAContext())
{
logsList = (from i in db.GenericLog select new Objects.Logs.GenericLog()
{
EntityId = i.FkEntityId,
LogSourceCode = i.FkLogSourceCode,
LogLevelId = i.FkLogLevelId,
LogDateTime = i.LogDateTime,
LogId = i.PkLogId,
Message = i.Message
})
.Where(i => i.LogDateTime >= startDate && i.LogDateTime <= endDate)
.Where(i => i.EntityId == entityId || i.EntityId == null)
.Where(i => i.LogLevelId == logLevelId || i.EntityId == null)
.ToList();
}
return logsList;
}
For example, in the second and third Where(), I have || i.EntityId == null... thinking this would accomodate for is user input for Entity is null?
Will this work?
Also, how can I do this for date ranges? Can I also do the same?
Finally, is there a BETTER way to do this?
Split creating a query and generating a final result by .ToList()
When you generate a query, you can add where statements on demand, like this:
public static List<Objects.Logs.GenericLog> GetLogs(int entityId, int logLevelId, DateTime startDate, DateTime endDate)
{
var logsList = new List<Objects.Logs.GenericLog>();
using(var db = CORAContext.GetCORAContext())
{
var query = (from i in db.GenericLog select new Objects.Logs.GenericLog()
{
EntityId = i.FkEntityId,
LogSourceCode = i.FkLogSourceCode,
LogLevelId = i.FkLogLevelId,
LogDateTime = i.LogDateTime,
LogId = i.PkLogId,
Message = i.Message
});
if(someCondition) {
query = query.Where(i => i.LogDateTime >= startDate && i.LogDateTime <= endDate)
}
query = query.Where(i => i.EntityId == entityId || i.EntityId == null)
query = query.Where(i => i.LogLevelId == logLevelId || i.EntityId == null)
logsList = query.ToList();
}
return logsList;
}
If I understand you correctly, you have a method that gets a filtered set of data based on the values of the parameters passed in. But you want to make the parameters optional, so if the user wants data for all entities, they wouldn't pass in an entityId.
If that's the case, then you can make the arguments optional by providing a default value for them in the method signature. We can then check if the argument has the default value, and if it does, don't apply that filter; otherwise apply it.
We can do this by doing .Where(x => argHasDefaultValue || someFilter). This works because if the argument has the default value, then the second part of the || is ignored.
For example:
public static List<Objects.Logs.GenericLog> GetLogs(int entityId = int.MinValue,
int logLevelId = int.MinValue, DateTime startDate = default(DateTime),
DateTime endDate = default(DateTime))
{
using(var db = CORAContext.GetCORAContext())
{
return db.GenericLog
.Where(i => startDate == default(DateTime) || i.LogDateTime >= startDate)
.Where(i => endDate == default(DateTime) || i.LogDateTime <= endDate)
.Where(i => entityId == int.MinValue || i.EntityId == entityId)
.Where(i => logLevelId == int.MinValue || i.LogLevelId == logLevelId)
.Select(i => new Objects.Logs.GenericLog
{
EntityId = i.FkEntityId,
LogSourceCode = i.FkLogSourceCode,
LogLevelId = i.FkLogLevelId,
LogDateTime = i.LogDateTime,
LogId = i.PkLogId,
Message = i.Message
}).ToList();
}
}

Linq Statement when you need to do a Convert.To?

Is there anyways I can stop from having to stay Convert twice?
allObjects.Where(x =>
Convert.ToDateTime(x.MyDate).DayOfWeek != DayOfWeek.Saturday &&
Convert.ToDateTime(x.MyDate).DayOfWeek != DayOfWeek.Sunday).ToList()
Assume I can't change the type of "MyDate" to datetime to stop from having to do the convert in the first place
For these sorts of cases it is often easier to use the query comprehension form.
var q = from obj in allObjects
let day = Convert.ToDateTime(obj.MyDate).DayOfWeek
where day != DayOfWeek.Saturday
where day != DayOfWeek.Sunday
select obj;
var result = q.ToList();
Note that the answer given by Tim Schmelter is the fluent form of this query; I find the comprehension form much easier to read and reason about.
Assume I can't change the type of "MyDate" to datetime to stop from
having to do the convert in the first place
Why you assume that? That would be the best option
If you can't do that you could store the result in an anonymous type:
var objectList = allObjects
.Select(x => new { Object = x, Date = Convert.ToDateTime(x.MyDate) })
.Where(x => x.Date.DayOfWeek != DayOfWeek.Saturday && x.Date.DayOfWeek != DayOfWeek.Sunday)
.Select(x => x.Object)
.ToList();
Making a helper extension method will make your query more readable:
static class DayOfWeekExtensions {
public static bool IsWeekEnd(DayOfWeek dow) {
return dow == DayOfWeek.Saturday || dow == DayOfWeek.Sunday;
}
}
Now your query could be rewritten as follows:
var notOnWeekEnds = allObjects
.Where(x => !Convert.ToDateTime(x.MyDate).DayOfWeek.IsWeekEnd())
.ToList()
You can do this:
allObject.Where(x => { var dt = Convert.ToDateTime(x.MyDate);
return dt.DayOfWeek != DayOfWeek.Saturday && dt.DayOfWeek != DayOfWeek.Sunday;
}).ToList();
Yes -
allObjects.Where(x => {
var dateConvered = Convert.ToDateTime(x.MyDate);
return dateConvered .DayOfWeek != DayOfWeek.Saturday && dateConvered.DayOfWeek != DayOfWeek.Sunday; }).ToList();
I would probably go this route. I hate multiple OR logic.
var allObjects = new List<Foo>
{
new Foo { MyDate = "3/10/18" },
new Foo { MyDate = "3/11/18" },
new Foo { MyDate = "3/12/18" },
new Foo { MyDate = "3/13/18" },
new Foo { MyDate = "3/14/18" }
};
var valuesToRemove = new DayOfWeek[]
{
DayOfWeek.Saturday, DayOfWeek.Sunday
};
var list = allObjects.Where //<-- This is the part that will interest you
(
x => Array.IndexOf
(
valuesToRemove,
Convert.ToDateTime(x.MyDate).DayOfWeek
) == -1
);
Console.WriteLine(string.Join("\r\n", list));
Output:
3/12/18
3/13/18
3/14/18
Code on DotNetFiddle
This might not help with regard to reusing calculated values in LINQ expressions, but in your specific case, you can rewrite it as:
var weekendDays = new [] { DayOfWeek.Saturday, DayOfWeek.Sunday };
var weekdayObjects = allObjects.Where(x =>
!weekendDays.Contains(Convert.ToDateTime(x.MyDate).DayOfWeek)
).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);

Categories