Entity SQL compare datetime without milliseconds - c#

I'm trying to fetch some records from MSSQL DB using EntityObject with EntitySQL query.
The field i'm using to filter is type of datetime.
The generated query projected without millisecond, to the SQL Server returns nothing.
When i'm adding the milliseconds, i get results.
How can i use the EntitySQL to get result without the milliseconds?
thanks.

It's not elegant, but this worked for me:
foos.SingleOrDefault(f => f.EntryDate.Year == bar.EntryDate.Year &&
f.EntryDate.Month == bar.EntryDate.Month &&
f.EntryDate.Day == bar.EntryDate.Day &&
f.EntryDate.Hour == bar.EntryDate.Hour &&
f.EntryDate.Minute == bar.EntryDate.Minute &&
f.EntryDate.Second == bar.EntryDate.Second);

One way to do it is to create a view of your data, where your datetime column is converted to smalldatetime (which does not have milliseconds).
Then add the view to your entity framework model, and read the data through the view.
Hope this helps
Shiraz

Workaround I found is on initial fetch store date as .ToBinary() then when you filter just do new DateTime(binaryValue) and compare with that.

Related

LINQ query not statisfying the datetime validation

This is my LINQ for returning the matching record
var staffawards = await _context.StaffAwards.FirstOrDefaultAsync(c => c.StaffID == StaffAwards.StaffID && c.EmpID == StaffAwards.EmpID && c.AwardDate == StaffAwards.AwardDate);
The StaffAwards.AwardDate will be in this format "09/12/2020 12:00:00 AM"
whereas the AwardDate in my table will be like this "2020-12-09 17:16:00.000"
How can i convert the StaffAwards.AwardDate in Sql Server datetime?
AnyHelp would be appreciated.
If your code and your database use Date/DateTime types as they should be, and not strings then you need to understand a few things:
date datatypes don't have a format, only strings created from dates have a format. Whatever format you see in your code/sql query tool is the formatting it has applied when it showed you the date (it had to turn it to a string to display it)
a datetime with a time of midnight is a completely different datetime to one where the time is 17:16, just like a number 1.0 is a completely different number to 1.75352; you will never get a database to return you a record with a time of midnight if you use equals and pass a time of anything other than midnight, just like you will never succeed in getting a record where the age of the person is 1.0 by asking "where age = 1.75352"
Either fix up your parameter so it is midnight, like the db is, or use a parameter range (if the dates in the db will have times other than also)
//if the db date is always midnight
.Where(x => x.DateColumnInDb == datetimeParameter.Date);
//if the db might have times too
.Where(x => x.DateColumnInDb >= datetimeParameter.Date && x.DateColumnInDb < datetimeParameter.Date.AddDays(1));
By using a range, we do not risk asking the database to convert every datetime in the table, every time we want to query. Converting data in a where clause is typically a bad idea because it usually leads to significant performance loss because indexes cannot be used
Also, make sure your .net side datetime and your db time use the same timezone or they will actually be referring to different times
To use a Date with the Database-Format you can use the DbFunctions.
Like this:
var staffawards = await _context.StaffAwards.FirstOrDefaultAsync(c => c.StaffID == StaffAwards.StaffID && c.EmpID == StaffAwards.EmpID && DbFunctions.TruncateTime(DateTime.Parse(c.AwardDate)) == StaffAwards.AwardDate);
Important: For TruncateTime, you have to use a DateTime. You have to convert c.AwardDate to DateTime. DateTime.Parse(c.AwardDate)
Most likely, you have the SQL server installed on a separate machine, which may be due to a different date format.
But there is no need for this conversion, the entity framework will do it automatically for you.
If you just compare date, you can use this code :
var staffawards = await _context.StaffAwards.FirstOrDefaultAsync(c => c.StaffID == StaffAwards.StaffID && c.EmpID == StaffAwards.EmpID && c.AwardDate.Date == StaffAwards.AwardDate.Date);

LINQ query maps dates incorrectly

My query looks like so:
using (var ctx = new PCLvsCompContext())
{
var brokerId = broker?.Id;
var symbolId = company?.Id;
var result = (from t in ctx.TradeHistoryJoineds
where t.TradeDate >= fromDate
&& t.TradeDate <= toDate
&& (brokerId == null || t.BrokerId == brokerId)
&& (symbolId == null || t.SymbolId == symbolId)
select t).OrderBy(x => x.TradeDate).ThenBy(x => x.BrokerName).ToList();
return result;
}
As an example, I run this query with dates like fromDate March-01-2017 toDate March-31-2017. I then captured the generated sql in SQL profiler that this query produces and ran it in SQL management studio. The output was as expected where for each weekday, each company has some trades. The query is based off of a view which casts all dates to "datetime" so that excel can parse them as dates correctly. However, when I put a breakpoint at "return result" and inspect the dates, all but 2 of the dates are March-1-2017. This is incorrect, the query result in SQL manager shows trades for almost every weekday in March (which is correct).
What is going on here? Why is Linq losing its mind?
Although based on the results I cannot see exactly how you would end up with those results, it is very common that you could be dealing with a DateTime timezone issue. I suspect that perhaps you saved your dates to the database using a DateTime object from say DateTime.Now instead of DateTime.UtcNow. So at that point in time and based on the machine it was called on it would be based on the timezone and datelight savings of that machine.
A DateTime object should not be used as it can relate to the region of the SQL database, the region of the server making this LINQ call and so the two regions could be on different timezones.
Instead you should always use DateTimeOffset.
If you cannot do that for some reason, then double-check your dates toDate and fromDate and do:
var utcToDate = toDate.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'");
var utcFromDate = toDate.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'");
Which gives something like this if it was run on 3rd April 2018 at 22:56.
2018-04-03T22:56:57.740Z
You would then also need to make sure when you save any date to the SQL backing store that you do ToUniversalTime() firstly. You can check your SQL tables directly with a normal SQL query and they should be stored in the database as the same UTC string format as above, and then the comparison should be obvious to whether it is valid.
However I would strongly recommend changing all your DateTime calls and gets to DateTimeOffset. The only time to use DateTime in majority of cases is for the final display to a user.
Thank you all for your suggestions. For those who are familiar with linq, EF and views this may seem like a stupid oversight, but I will post my shame for others in case this happens to them since the cause and the resulting behavior are not immediately obviously linked (as can be seen by all the suggestions, none of which actually point to the answer).
When you are querying a view using linq and Entity Framework, you apparently must have a column called 'Id', otherwise Entity Framework can't distinguish any of the rows from one another and simply makes some sort of duplication that I can't quite decipher (all the rows were unique based on their data even without an Id column, so I can't quite figure out why this is necessary).
So by adding an the TradeId with an 'Id' alias to the view, then Entity Framework seemed to return to sanity and map the data as expected.

LINQ to SQL query succeeds or fails based on using foreign key versus using a built in navigation property

I have a query against a SQL server database that throws an Error converting data type nvarchar to numeric. exception. I was trying to use Convert.ToDecimal on a varchar field, however I'll eyeballed the data as best I could and couldn't find an invalid value.
The query is filtering the table by a 'Group' by using the foreign key of p.pgKey=#. However, if I use the navigation property and filter by the navigation property instead, p.Group.gName='ABC' the query works.
Here are the queries (note, originally, I didn't know if error was happening in Where translation or the Select processing, so that is why query looks weird, but as you can guess, when it works, it should just return a single distinct row of true):
Profiles
.Where(p =>
p.pgKey == 237
&& !p.pPlanProfile.Value
&& Convert.ToDecimal(p.pSearch08 ?? "0") > 0
).Select(p =>
Convert.ToDecimal(p.pSearch08 ?? "0") > 0
)
.Distinct()
.Dump();
The above query fails, while this query succeeds:
Profiles
.Where(p =>
p.Groups.gName == "ABC"
&& !p.pPlanProfile.Value
&& Convert.ToDecimal(p.pSearch08 ?? "0") > 0
).Select(p =>
Convert.ToDecimal(p.pSearch08 ?? "0") > 0
)
.Distinct()
.Dump();
Below is a full LINQPad screen dump showing:
Proves the gKey for ABC is 237.
Proves that the counts of simplying counting the Profile records is identical when using pgKey or Group.gName.
Shows the query working correctly when using the Group.gName processing.
Shows the query failing when using the pgKey processing.
Obviously I've used the Group.gName method to fix my problem, but I stumbled on that solution by accident. Anyone know why LINQ to SQL would behave this way?
Note: I get the same behavior using the generated DataContext from LINQPad or if I run against a compiled .dbml DataContext.
The two queries will be generating different TSQL and hence the query plan will be different.
I would suspect that the former query is attempting to convert some values of pSearch08 to decimal before it rejects them based on the other selection criteria, whereas the latter query is performing the other selection criteria first and hence is attempting to convert a smaller number of pSearch08 values to decimal and hence does not attempt to convert the invalid value.
If this is the case, then it could be dangerous to assume that the second query will always work and it would be best to fix the invalid data.
Rather than eyeballing the data you can try
SELECT * from Profile where ISNUMERIC(pSearch08) = 0

Linq not finding matching record that is there

I'm trying to find a record that I know exist in the database. When searching for it using this query nothing is found.
List<OrganizationALE> ales =
_ctx.OrganizationALEs.Where(c => c.OrganizationId.Equals(organizationId) && c.LastModified.Equals(modified) && c.StartDate.Equals(start)).ToList();
But when I search using this query it is clearly there in the result using watch.
And if I try pull it out on the very next line using
var found = ales.First(a => a.LastModified == modified);
I get an exception say Sequence contains no matching element
I bet it's because modified and/or start contains milliseconds that are not stored with the same precision in the database (datetime in SQL Server?) so that the comparison fails in the database.
Possible solutions are here (the second and third point apply to your situation). Basically supply less precise DateTime values (cut off the millisecond) or use datetime2(7) in SQL Server or avoid using equality comparisons with DateTime values and use >= and <= instead.

GetWeekOfYear with Entity Framework

I've a column in my table called Date, and I need to compare this date's WeekOfTheYear with DateTime.Now's WeekOfTheYear,
If I give like this,
var cal = CultureInfo.CurrentCulture.Calendar;
int week = cal.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
I am getting 26 here. The same way, in my Entity Framework, I need to compare this week's data, for this I tried like,
entities.WorkingDays.Where(a =>
cal.GetWeekOfYear(a.DATE,CalendarWeekRule.FirstDay,DayOfWeek.Sunday)
== cal.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstDay,
DayOfWeek.Sunday)
when I run the query like this, am getting error like,
"LINQ to Entities does not recognize the method 'Int32 GetWeekOfYear
(System.DateTime, System.Globalization.CalendarWeekRule, System.DayOfWeek)'
method, and this method cannot be translated into a store expression."
How can I fetch the data for weekly basis here, can any one help me out here....thanks in advance
Call .ToList() first. Like this:
entities.WorkingDays.ToList().Where(a =>
cal.GetWeekOfYear(a.DATE,CalendarWeekRule.FirstDay,DayOfWeek.Sunday)
== cal.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstDay,
DayOfWeek.Sunday)
See this post for duplicate issue. Basically, the data needs to be in memory before using your GetWeekOfYear functions.
As noted in the comments, this does bring the whole "WorkingDays" table into memory and therefore fetching more data than needed from the DB. Sometimes this is more preferable to using Stored Procedures and sometimes not, depending on the amount of data and other factors based on your application/database architecture.
You could probably use the day of year and divide it with 7 on both instances, and get a sufficient result?
Date.DayOfYear / 7

Categories

Resources