Linq if DateTime field is older than X hours - c#

I have tried the following (obviously without the //), but I can't get any to work, can anybody help please?
public void CleanBasket()
{
//double validHours = 3;
// var expired = (from a in db.Baskets where (DateTime.Now - a.DateCreated).TotalHours > validHours select a);
//var expired = (from a in db.Baskets where (DateTime.Now.Subtract(a.DateCreated).Hours > 3) select a);
//var expired = (from a in db.Baskets where(a => a.DateCreated > DateTime.Now.AddHours(-1));
//foreach (Basket basket in expired) db.DeleteObject(expired);
db.SaveChanges();
}

In this case surely you can simply do your date time calculation before you invoke LINQ:
double validHours = 3;
var latest = DateTime.UtcNow.AddHours(-validHours);
var expired = (from a in db.Baskets where a.DateCreated < latest select a);
For any more complex DateTime operations that you need to do in the database and cannot do this way you can use SqlFunctions.
BTW you should store your times in Utc not local time. Calculations using DateTime.Now will be wrong during daylight savings time changes.

My guess is that linq-to-entities doesn't know how formulate the query with the DateTime.Now operation. To do this, I would get the values in a list and then with just linq, filter them out.
public void CleanBasket()
{
var cutoff = DateTime.Now.Subtract(new TimeSpan(3, 0, 0));
var baskets = db.Baskets.Where(a=>a.DateCreated<cutoff);
db.DeleteObjects(baskets); // You can combine this with the last line
db.SaveChanges();
}

It is very likely that a ORM wont be able to translate TimeSpan operations, check this question it could be helpful: Comparing dates in query using LINQ
basically you can consider that if the method you are using does not have a literal translation to SQL it is very likely that it will not be supported.

Related

How to apply query operation only to first entity in Azure table?

I am trying my best to keep the query operation simple by just querying the first record of a table to check if my query is valid and throws no exception.
I have seen many answers on SO about retrieving top 'n' entities. My requirement here is, to apply query operation to only the first entity, no matter if that matches the filters specified in the query and abort.
I tried:
TableQuery query = new TableQuery().Where("MyKey eq 'RawMaterial'").Take(1);
But this query performs a complete table scan if there is no matching entity. Any comments on this?
Here is sample code :
List<int> numbers = Enumerable.Range(0, 100000000).Select(x => x).ToList();
DateTime startAll = DateTime.Now;
List<int> none = numbers.Where(x => x == -1).Take(1).ToList();
DateTime endAll = DateTime.Now;
DateTime startOne = DateTime.Now;
List<int> one = numbers.Where(x => x == 1).Take(1).ToList();
DateTime endOne = DateTime.Now;
Console.WriteLine("Time One : {0}, Time All {1}", endOne.Subtract(startOne).ToString(), endAll.Subtract(startAll).ToString());
Console.ReadLine();

Calculate elapsed time in a linq query

Using System.Linq in a .Net Core application I'm to trying place the date difference between AssignedDate and DateTime.Now into ElapsedTime.
var query = from r in _context.Request
join st in _context.ServiceType on r.ServiceTypeId equals st.ServiceTypeId
join u in _context.Users on r.AssignedUserId equals u.UserId into ju
from u in ju.DefaultIfEmpty()
select new RequestDto
{
RequestId = r.RequestId,
UserId = r.UserId,
//..
AssignedUserId = r.AssignedUserId.Value,
AssignedUser = u.Name,
ElapsedTime = DateDiff(r.AssignedDate, DateTime.Now)
};
The DateDiff is not a valid function. How would I replace that?
EDIT: Using MicrosoftEntityFrameworkCore 2.0. Trying EntityFunction is not valid.
Edit again: I could use this on the view side with razor with something like:
#if (item.AssignedDate != null){#DateTime.Compare(item.AssignedDate.Value, DateTime.Now)}
But that doesn't work??
Unfortunately TimeSpan and DateTime operations support in EF Core is still far form perfect even in the lastest at this time EF Core 2.0.1.
For instance, the natural
ElapsedTime = DateTime.Now - r.AssignedDate
for SqlServer throws SqlException - "The data types datetime and datetime2 are incompatible in the subtract operator.".
And attempt to trick it with using variable
var baseDate = DateTime.Now;
and then
ElapsedTime = baseDate - r.AssignedDate
leads to another SqlException - "Operand data type datetime2 is invalid for subtract operator.".
The only way I was able to use w/o error is the DateTime.Subtract method:
ElapsedTime = DateTime.Now.Subtract(r.AssignedDate)
but keep in mind that this leads to client evaluation.
Assuming you can change the type of ElapsedTime, how about:
...
ElapsedTime = (DateTime.Now - r.AssignedDate).Ticks
ElapsedTime will have to be of type long which will hold the difference as ticks, when you read it back, you can use TimeSpan.FromTicks(ticksValue), then you can choose what members you want (hours, minutes, seconds, fractions, etc...)

LINQ to entites does not recognize Convert.ToDatetime method

EDIT All the other answers that link to a previous question's wont work for me because they are either using two tables or they know what startdate they are looking for.
I have the following LINQ query where i am trying to get the data from a table for the last 14 days. LINQ does not recognize Convert.ToDatetime method. The Query_Date column is a type string and i can't change it. How do i get the data i need?
var logs = (from bwl in db.UserActivities
where Convert.ToDateTime(bwl.Query_Date) >= DateTime.Now.AddDays(-14)
select new
{
Id = bwl.Id,
UserEmail = bwl.UserEmail
}).ToList();
So i couldn't get what i wanted because i would have to make too many changes so the workaround i am doing is: I am using this code
var logs = db.UserActivities.ToList().Take(100);
This will get me the last 100 entries. I will give options for more or less entries then they can filter it on date in the search bar on the datatable.
It's not great but time is against me and this will suffice.
Do you have data being entered at least every day? If so, what about something like this:
var oldestDate = DateTime.Now.AddDays(-14);
var dateString = oldestDate.ToString(*/ your date format */);
var oldestID = db.UserActivities.First(b => b.Query_Date == dateString).Id;
var logs = (from bwl in db.UserActivities
where bwl.Id > oldestID
select new {
Id = bwl.Id,
UserEmail = bwl.UserEmail
}).ToList();
Basically you find a record on that date and use its ID as a proxy for the date. This only works in certain circumstances (i.e. if the IDs are in date order).
This also isn't guaranteed to be the oldest entry on that date, but that could be achieved either by using ID order:
var oldestID = db.UserActivities.Where(b => b.Query_Date == dateString)
.OrderBy(b => b.Id)
.First().Id;
Or more lazily by using -15 instead of -14 when you add days, if you don't mind grabbing an unknown percentage of that 15th day.
Convert.ToDateTime works if your Query_Date has proper format. If not, check your string expression is convertable format.
I tested below code and it works fine when I assume Query_Date is a form of DateTime.toString().
var logs = (from bwl in db.UserActivities
where DateTime.Now - Convert.ToDateTime(bwl.Query_Date) <= TimeSpan.FromDays(14)
select new
{
Id = bwl.Id,
UserEmail = bwl.UserEmail
}).ToList();
I also tested with your where expression Convert.ToDateTime(bwl.Query_Date) >= DateTime.Now.AddDays(-14) and confirmed that it gives same result.

Date Difference Logic in LINQ

I'm attempting to access data from two different database tables and then join them together on two fields using LINQ in C#. I believe that I have a logically sound overall working approach. Part of the problem I'm running into is that I'm filtering the data from both tables prior to joining them, because the tables have far too much data and it would cause a crash.
The main problem is that for one of the tables I need to pull only data that has a timestamp (column) value of today. The timestamp value is of type System.DateTime?.
I've tried a few different ways:
DateTime? currentDate = System.DateTime.Now;
var second_data = (from b in this.database.table
where EntityFunctions.DiffDays(b.timeStamp.Value, currentDate) == 0
select b);
I'm under the impression this doesn't work because there's no function in the database to handle it. Inner Exception: '{"FUNCTION database.DiffDays does not exist"}'
var second_data = (from b in this.database.table
where b => b.timeStamp.Value.Date == DateTime.Now.Date
select b);
This doesn't work because 'The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.'
var second_data =
this.database.table.Where(sd => sd.timeStamp.Value.Date == DateTime.Now.Date);
But this again fails because of the use of .Date.
Unfortunately, because I don't have the memory to hold all that data, the possibility of of pulling all the data first and then running date logic on it is out of the question. If anyone could give any insight on how I might be able to solve this problem it would be greatly appreciated, thank you.
To get rows from the table that are only for today (or a specific date range), you could simply do this. The nice thing about this approach is that it works for both cases of a specific date or a date range.
// specify date range (without time)
DateTime currentDate = System.DateTime.Now.Date;
DateTime nextDate = currentDate.AddDays(1);
var second_data = from b in this.database.table
where b.timeStamp.Value >= currentDate
and b.timeStamp.Value < nextDate
select b;
query = query.Where(c=> DbFunctions.DiffDays(c.ToDate, DateTime.Now) < 30);
Its not working in my scenario
I'm using Sql Server and I get your first method to work if I remove the call to timestamp.Value. I don't think your version compiles because DiffDays takes nullable DateTimes for both parameters.
DateTime? currentDate = System.DateTime.Now;
var second_data = (from b in this.database.table
where EntityFunctions.DiffDays(b.timeStamp, currentDate) == 0
select b);
The other thing I note is that I get a warning:
'System.Data.Entity.Core.Objects.EntityFunctions' is obsolete: 'This
class has been replaced by System.Data.Entity.DbFunctions.'
So if it still doesn't work in MySql you could try DbFunctions instead
this answer helped me for the exact day number, not in-between difference:
Found it on forums.asp.net
Here's a sample showing one way to get all employees with a DOB between now and 14 days from now...
var employeesWithBirthday =
from emp in dc.Employees
let BirthdayDiff = (new DateTime(DateTime.Now.Year, emp.BirthDate.Month, emp.BirthDate.Day) - DateTime.Now).TotalDays
where BirthdayDiff >= 0 && BirthdayDiff <= 14
select emp
;
...although, be aware that a query like that will do a table scan (can't use any indexes)...

.NET: LINQ's Last()

I'm new to LINQ and LINQ to SQL and don't understand what's wrong with this code. The Excetpion.Message I get is
"Query operator 'Last' is not supported."
What I'm trying to do is get the earliest LastActivityUtcout of the latest 100. The code follows.
var postTimes = from post in db.Post
where post.LastActivityUtc != null
orderby post.LastActivityUtc descending
select post.LastActivityUtc;
DateTime startDate = DateTime.MinValue;
if (postTimes.Count() >= 2)
{
startDate = postTimes.Take(100).Last().Value;
}
Brandon has posted a solution, but it requires copying the whole list in memory.
If you just want to "transition" from database queries to in-process, you can use AsEnumerable:
startDate = postTimes.Take(100).AsEnumerable().Last().Value;
Having said that, you possibly do want to call ToList(), but earlier - to avoid having to execute the query once for the count, and once for the last value:
var postTimes = (from post in db.Post
where post.LastActivityUtc != null
orderby post.LastActivityUtc descending
select post.LastActivityUtc).Take(100).ToList();
DateTime startDate = DateTime.MinValue;
if (postTimes.Count >= 2)
{
startDate = postTimes.Last().Value;
}
That will execute the database query once, but only fetch the first 100 records into memory. Of course, it falls down somewhat if you were going to use postTimes elsewhere...
Call .ToList() on postTimes and then try using .Last()
startDate = postTimes.Take(100).ToList().Last().Value;

Categories

Resources