"Where" clause in linq to sql query using DateTime? - c#

I'm trying to run a query of a table with the columns Domain, LastUsed, and FreqInHours In c#.
I just want to return all the Domains that I need to crawl.I find this out by checking the datetime that they were last Crawled (LastUsed) and how frequently they should be crawled (ex. every 6 hours). If the current date/time - the time it was last crawled is greater than the frequency I add want to return that domain.
Here is the current query I've written:
var query = (from c in context.SitemapFreqs
where (DateTime.Now - c.LastUsed).TotalHours > c.Freq
select c.domain);
Here is the exception I'm being given:
LINQ to Entities does not recognize the method 'System.DateTime ?
ToDateTime(System.Object)' method, and this method cannot be translated into a store expression.
Any help would be really appreciated.

You can use DbFunctions class and method DiffHours.
Here is an example:
var query = (from c in context.SitemapFreqs
where DbFunctions.DiffHours(DateTime.Now,c.LastUsed) > c.Freq
select c.domain);
Here is the documentation. Hope it helps.

Complex DateTime stuff is a bit much for Linq2SQL to handle.
If it's a relatively small amount of data, load it all into memory first:
var query = (from c in context.SitemapFreqs.ToList()
where (DateTime.Now - c.LastUsed).TotalHours > c.Freq
select c.domain);
If it's a larger amount of data, you can use DbFunctions, or provide the query yourself.
context.SitemapFreqs.SqlQuery("SELECT * from SitemapFreqs WHERE DATEDIFF('hour', GETDATE(), LastUsed) > Freq")
If you make sure the query returns the columns the SitemapFreqs object expects, it will map the objects just like it would anything else.

Looks like your c.LastUsed property is nullable. You can subtract nullable DateTimes using the c.LastUsed.Value property, but you should know that if it is null, this will throw an exception as you can't subtract a DateTime - null. I believe you have two options:
Change the property LastUsed in your class to a non-nullable DateTime by removing the ?.
Create a method inside of your class that determines if the DateTime? LastUsed is equal to null. If it is, return something where your LINQ query will ignore that value. (I.E: Set the value of LastUsed = DateTime.Now so that your LINQ query comes back as 0).
Hope this helps.

Related

How to efficiently search a list of objects with a DateTime property in my scenario

I have an already sorted list of object (sorted by date)
public class HistoryValue{
public DateTime Date {get;set;}
public decimal Value {get;set;
}
Then I have a list of days, for example,
1MonthAgo, 2MonthAgo,3MonthAgo,120MonthAgo
What I need is to find the Value on date
1MonthAgo, 2MonthAgo,3MonthAgo,120MonthAgo
If the date can not be found in the list, I should return the one just before that date. It is easiest to explain in a SQL statement although I am doing the real work in c#:
select top 1 Value
from HistoryValueList
where Date between #d12m-#lookbackdaymax and #d12m order by Date desc
I was thinking of using binary search, but don't think binary search will do exactly what I want. Maybe it is best do a looping and remember the closet object of each?
What you want is a "lower bound" kind of algorithm (please check this question), that is, a binary search (or bisect) algorithm that finds the left most element less than or equal to your search element.
You're in luck, there is such a thing called Language Integrated Query (LINQ) which lets you run queries on objects in C# which implement IEnumerable. That includes the List<HistoryValue> you're using.
You're looking for some code like:
HistoryValue val = historyValues.FirstOrDefault(v => v.Date > dateMin && v.Date <= dateMax);
Where historyValues is your list object.
This is pretty easy in C#. I am supposing that you get all records in list of HistoryValue class from DB!
Then you will write code like this:
List<HistoryValue> list = list; //your list get from db here!
HistoryValue historyValue = list.Where(m=>m.Date >= #12m && m.Date <= #lookbackdaymax ).First();

How to convert string to datetime and then get the lowest date

Records are coming from database and date is in the string format. I am using LINQ Min() query to select the record with lowest date. LINQ is not allowing me to use Convert.ToDateTime().
How can I get lowest date record?
You could do something like
.Min(ob => System.Convert.ToDateTime(ob.DateProperty));
This way the value gets converted before checking for the lowest value.
What do you mean by "linq is not allowing me Convert.ToDateTime()" ?
Can you not do:
DateTime minDate = listOfStrings.Select(x => Convert.ToDateTime(x)).Min();
..?
If the strings are not in a date/time format that is handled by Convert.ToDateTime(), you may need to look into DateTime.ParseExact() with an appropriate format string.
Edit: sorry, just realized another possibility. Do you mean that you cannot do the Convert.ToDateTime() because you are using LINQ against SQL and it is not usable within the expression? If so, try:
DateTime minDate = listOfStrings.ToList().Select(x => Convert.ToDateTime(x)).Min();
.. with a ToList() to force it to perform the query and then to the conversion.
there's a way of doing it by using the Orderby method in your LINQ request,
using a Func() on the (datetime)field you wish to order by, then selecting the first element.
It's not very clean as i would use a datetime in the database, but it should work.
Looking into some code samples would be useful. But this might help.
var minDateTime = strings.Min(s => DateTime.Parse(s));

date difference in EF4

i need to get a difference of two dates ,one date from a table and one the current date, and the difference should be less than 9 0days. i need to use this as filter in where clause of the linq
i tried doing this
var list = from p in context.persons
where ((p.CreateDT).Subtract(DateTime.Now).Days < 90)
select p;
i get this excpetion :
LINQ to Entities does not recognize the method 'System.TimeSpan Subtract(System.DateTime)' method, and this method cannot be translated into a store expression.
I did research other articles but nothing helped..Any ideas
Trick here is that it can't translate all your fancy oo hoo-ha to plain old sql. Trick is to flip it on it's head:
var before = DateTime.Now.AddDays(-90);
var persons = context.Persons.Where(x => x.CreateDT > before);
EXPLANATION
Remember that everything in the WHERE bit of your LINQ statement must be translated from C# to SQL by the EF. It is very, very capable out of the box and handles most basic tasks, but it has no idea how to understand the most rudimentary method calls, such as DateTime.Subtract(). So the idea here is to let it do what it does best by precalculating a value and then passing that to the data tier.
The first line subtracts 90 days from the current time by adding negative 90 days. The second line passes it off to the database server.
The second line should translate to the SQL WHERE CreateDT > #BEFORETHIS
Update
It seems that EF doesn't support subtracting dates and returning a TimeSpan. Here's one way to solve the problem:
DateTime oldestDate = DateTime.Now.AddDays(-90);
var list = from p in context.persons
where p.CreateDT >= oldestDate
select p;
See this thread on Stackoverflow.
Try doing simply (p.CreateDate - DateTime.Now).Days < 90. Instead of calling DateTime.Subtract(). In some cases the operator overloads are implemented for Entity Framework even when the corresponding named methods are not.
If that doesn't work you could instead use ESQL or a stored procedure. As a final, dirty solution, you could call context.persons.ToList() and then call the DateTime.Subtract().

LINQ to SQL Equivalent of ISDATE() in T-SQL and casting?

Does anyone know the equivalent of ISDATE() in LINQ to SQL query syntax? I've got a varchar field in SQL that contains dates and I need to filter out the non-date rows.
was hoping for something like this:
var query = SomeDataContext;
query = from p in query
where p.ISDATE(field1) == true;
select p;
also, how would one cast something for SQL in Linq syntax?
CAST(SomeDate AS SMALLDATETIME)
The trick to doing this is to create a user function in the database which merely calls ISDATE as returns the value
CREATE FUNCTION My_ISDATE(#maybeDate varchar(max))
returns bit
as return ISDATE(#maybeDate);
Then add My_IsDate to you database context, and use it in you query:
var db = SomeDataContext;
var query = from p in db.MyTable
where db.My_ISDATE(p.field1)
select p;
I don't think there is an extension method that maps to ISDATE when using LINQ to SQL.
If you are ok with loading all the data and then doing the filtering in the client space, then use TryParse on the field and compare to the date.
Otherwise, I would create a stored procedure which would return the data you want (so that you can use ISDATE), and then execute that through the context.
Also, your syntax for the use of ISDATE is incorrect. ISDATE just tells you if the expression is a valid date format. Your query would look like this:
var query = SomeDataContext;
query = from p in query
where ISDATE(field1) != 0 && CONVERT(datetime, field1) > some date
select p;
The syntax isn't valid, but it gives you an idea of how to form the query.
A problem with SO is that an increasing number of threads on answers of problems contain outdated answers, mostly because they are not touched in years. However in combination with a search engine (e.g. Google) who then returns as #1 a link (for instance this post) when you query "How should i use IsDate in Linq" it needs someone to then add one answer (at the bottom with 0 votes) of the latest correct answer.
:: https://github.com/dotnet/efcore/issues/8488 >
It now has been added to EF.Functions so you can simply do:
Where(x=> EF.Functions.IsDate(x.somefield))
Looks like you can do it. Links:
http://msdn.microsoft.com/en-us/library/bb386973.aspx
this might be of help but I haven't watched it:
http://mtaulty.com/videos/nuggets/l2s/15_mt_l2s_callingsqlfunctions.wmv
Full list here:
http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2007/05/10/9322.aspx

How to Use Dates in Where Clause in EF Core?

I need to filter my queries by dates but I don't care in this case about time portion of it that is stored in SQL Database.
I first tried to something like
var now = DateTime.Now.Date;
Where(x => x.CreatedDate.Date.Compare(now) == 0)
but this seems to all get locally checked making the query slow. How can I do this without making it do the check locally?
I am pretty much trying to just find all results that would say have happened today(2020-01-06).
There are a limited number of methods you can use on translatable types when constructing your Lambda / Linq expressions. This is because each method would need additional code so that it could be translated into a sql store expression. It means that you must check that any methods you want to use and expect to be translated into a sql store expression are supported.
In this case the DateTime.Compare is not supported.
The easiest thing to do here is a simple range comparison because the time is included in your persisted value.
var start = DateTime.Now.Date;
var end = start.AddDays(1);
Where(x => x.CreatedDate >= start && x.CreatedDate < end)
This will result in a sargable query.
Use
var now = DateTime.Now.Date
...WHERE(CreatedDate.Date == now)
I just checked that above translates to the following SQL query:
WHERE ((CONVERT(date, [x].[CreatedDate]) = '2019-01-07T00:00:00.000')
I used this (link) method to see what LINQ translates to

Categories