GetWeekOfYear with Entity Framework - c#

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

Related

How to add year to LINQ to entities query?

I am attempting to query a table in SQL using LINQ to entities and add a year to a Date column.
I have tried the following query below:
data = data.Where(x => x.DueDate.Value.AddYears(1) >= DateTime.Now);
When I do this, I get the following error message:
LINQ to Entities does not recognize the method 'System.DateTime AddYears(Int32)' method, and this method cannot be translated into a store expression.
I have done some research and it seems as if most people fix this issue by separating the query out and using a variable they then input into the expression.
I can't do this because I need to add the years to my data lambda expression and can't separate them out.
Is there is a simple way to fix this or is there a way to create a pseudo column that adds a year without actually creating an actual table column? I am trying to avoid having to create an entire new SQL column just for the purpose of adding a year to the date displayed.
Thanks for any help.
You can do the inverse and subtract 1 year from DateTime.Now and use that as the comparison value.
var yearAgo = DateTime.Now.AddYears(-1);
data = data.Where(x => x.DueDate >= yearAgo);
Side notes
The comparison value has a time component but based on the naming of the persisted value DueDate might not have a time component. You can remove the time component by using .Date (DateTime.Now.Date.AddYears(-1);) or .Today instead of .Now (DateTime.Today.AddYears(-1);).
If working in different time zones you will need a more robust solution. I would recommend reading Storing UTC Is Not a Silver Bullet by Jon Skeet.

LINQ to Entities formatting date exception [duplicate]

This question already has answers here:
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression
(13 answers)
Closed 2 years ago.
I got this error:
System.NotSupportedException: 'LINQ to Entities does not recognize the
method 'System.String ToString(System.String)'
method, and this method cannot be translated into a store
expression.'
The code is as below:
var search = filter.getFilterValueFor("StartDate", "eq");
query = query.Where(it => it.it.it.ne.newWo.wo.STRT_DT.GetValueOrDefault().ToString("dd/MM/yyyy").Contains(search));
var total = query.Count();
How to solve this error? Thank you.
An IQueryable<...> does not represent a sequence of similar items, it represents the potential to get a sequence of similar items.
To do this, the IQueryable has an Expression and a Provider. The Expression holds the query that must be executed in some generic format. The Provider knows who must execute the query (usually a database management system) and what language is used to communicate with the DBMS (usually SQL).
When you start enumerating, either explicitly, using IQueryable.GetEnumerator(), or at higher level using foreach / ToList() / FirstOrDefault() / Any(), etc, the Expression is sent to the Provider, who will try to translate it into SQL and execute the query. The fetched data is returned as an IEnumerator<...>.
Back to your problem
Your provider does not know method ToString(string). In fact there are several LINQ methods that are not supported by your Provider. See List of Supported and Unsupported LINQ methods (LINQ to entities).
You also use the method String.Contains, which the Provider also will not know. Even if you would solve the ToString(string) problem you would stumble into this similar problem.
What you could do, is transfer the value in its original DateTime format to your local process, and convert it there to a String, and use the Contain. Transferring the data to your local process is done using AsEnumerable. This is preferred above ToList, because AsEnumerable will not fetch much more data than you will use, ToList will fetch everything.
The problem is that you use this construction to filter the fetched data (Where). So if you would use that solution, you will transfer way more data than you would need.
Alas you forgot to mention what you want to query. I think that you want to get all items where the string version of a date "dd/MM/yyyy" contains "2020", or "11": you want all items of a certain year, or all items from November, or maybe the first day of each month.
If that is the case, consider to translate the value of search from string to the year (or month, or date) that you want to fetch, and use DateTime.Year (or month, or Day), to compare.
One final solution: use SQL Like. See class DbFunctions
Next time: don't give us code and say: "this code doesn't do what I want", give us (also) a description of what you do want.

Group by using the Entity Framework

I receive 48 files per day on a half hourly basis from market. These files have a start time as a property. I am using Entity Framework to view these files in a web application and as the files have a UK time but I am working with a european market the trading day begins the day before at 11pm and so I want to group these together based on the trading day.
In SQL I can accomplish this by:
select cast(DATEADD(hour, 1, START_TIME) as date), count(cast(START_TIME as date))
from imbalancecost
group by cast(DATEADD(hour, 1, START_TIME) as date)
I am trying to achieve a similar result in C# using the following attempt:
IEnumerable<IGrouping<DateTime, ImbalanceCost> imbalanceCost = db.ImbalanceCost.GroupBy(ic => ic.START_TIME).ToArray();
Is there any means of first adding the hour onto my grouping and then using only the date part of this new calculated value?
Is there any means of first adding the hour onto my grouping and then using only the date part of this new calculated value?
In LINQ to Entities (EF6) it's a matter of using respectively the canonical functions DbFunctions.AddHours and DbFunctions.TruncateTime:
db.ImbalanceCost.GroupBy(ic =>
DbFunctions.TruncateTime(DbFunctions.AddHours(ic.START_TIME, 1)).Value)
Note that .Value (or cast to DateTime) is to make the result DateTime rather than DateTime? returned by the canonical method in case ic.START_TIME is not nullable, hence you know the result is not nullable as well.
If I understand you correctly, you want to add an hour to the start time and group by the date part. That can be done as follows:
var imbalanceCost = db.ImbalanceCost
.Select(x => EntityFunctions.AddHours(x.START_TIME, 1))
.GroupBy(ic => ic.Value.Date)
.ToArray();

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.

Workaround to use a method within a LINQ expression

In my ASP.NET MVC3 application I have the following method that use EF entities:
public IQueryable<Products> GetCreatedProducts(int year)
{
return GetAllProducts().Where(m => Equals(DateUtilities.ExtractYear(m.ProductCreationDate), year));
}
ProductCreationDate is a field stored in the database as string "YYYYMMddhhmm".
int DateUtilities.ExtractYear(string date) is a method that I created in order to get the year from the string. Similarly I use ExtractMonth and ExtractDay.
However when I execute my application it launches an NotSupported exception:
"LINQ to Entities does not recognize the method 'Int32 ExtractYear(System.String)' method, and this method cannot be translated into a store expression."
By googling the problem I found out that it is a bug in LINQ to Entities Anybody knows a workaround?
My first question would be why are you storing a date in the database as a string? Store it as a date and use data comparison against it through Linq.
The issue that you're coming up against is Linq doesn't understand what the method ExtractYear is when it tries to compile your Linq statement to raw SQL
EDIT
Another alternative would be to create a view in the database and query against the view with a computed column that represents the string value as a datetime.
In this case you could go the other way:
GetAllProducts().Where(m => m.ProductCreationDate.StartsWith(year.ToString()));
Rather than extract the year from the string, find all strings beginning with the year you're after.
I'm not sure if these will work for the Month and Day searches:
var monthString = String.Format("{0:00}", month);
GetAllProducts().Where(m => m.ProductCreationDate.Substring(4,2) == monthString);
var dayString = String.Format("{0:00}", day);
GetAllProducts().Where(m => m.ProductCreationDate.Substring(6,2) == dayString);
I don't think this is really a bug so much as a limitation -- there's no SQL version of ExtractYear, so it's not possible to execute this query.
You'll have to rewrite the query to either use only SQL-compatible commands (like some other answers have shown) or pull lots of data out of the database and do the filtering with LINQ-to-Objects (this could potentially be very expensive, depending on what you're querying and how many of the records match).
You get the exception because you work with IQueryable. Entity Framework will try to translate the predicate in the where clause into SQL but EF doesn't know how to translate your method.
If you don't want to (or can't) change the database you still have a few options:
Pull all rows to the client side and do the filtering on the client. You do this by changing from IQueryable to IEnumerable:
return GetAllProducts()
.AsEnumerable()
.Where(m => Equals(DateUtilities.ExtractYear(m.ProductCreationDate), year))
.AsQueryable();
To be more efficient you can use your own SQL (this requires EF 4.1 and only avoid pulling all products to the client, not the scan on the server):
return context
.Products
.SqlQuery("select * from Products where substring(ProductCreationDate, 1, 4) = '2012'")
.AsQueryable();
Note that I was lazy and hardcoded the SQL. You should probably parametrize it and make sure you avoid any SQL injection attacks.
You could do the extraction inline, something like this:
public IQueryable<Products> GetCreatedProducts(int year)
{
string sYear = year.ToString();
return GetAllProducts().Where(m => m.ProductCreationDate.Substring(0,4) == sYear);
}

Categories

Resources