Question:
I'm running two code snippets and they're giving different results. Does anyone know why?
This code only shows coupons that have the validity of the current date and time + 3h (I don't know why):
var cupons = Listar().Where(c => c.Validade > Datetime.Now);
While this code shows the coupons correctly:
Datetime agora = Datetime.Now;
var cupons = Listar().Where(c => c.Validade > agora);
NOTE: The List query function in the database.
Answer:
At first the question wasn't clear, now I'm highlighting this part after editing because that's the problem. The second part is as a complement for people to better understand LINQ.
Database search
If the data is in a database (the question doesn't say anything about this) the expression will be translated to be executed in the database and depending on the provider used the DateTime.Now
will be translated to an expression that it recognizes and it will be executed there in the database that may be in a different time zone than the application. None of this is guaranteed, but it often happens. When it takes a variable from the application, this translation is not possible and then it compares it with the fixed data of the moment that is sent to the database.
I even think this is another mistake because the times should be all UTC and never have a time zone problem, but it's not the focus of the question. Probably the error is from modeling that adopted time zone in storage.
Another common mistake with LINQ
The first is using an expression that compares data from the data collection being evaluated to the current time. As time passes, this moment changes. In each check of an element of the collection the value of Datetime.Now
changes, so in this example it tends to filter a little more because it has a moving value, it narrows.
The second takes the current moment of code execution and stores it in a variable. This value is fixed, it does not change anymore. Then, when it goes through the data collection, the comparison is always being made with the same value, the value doesn't narrow, it doesn't change during execution.
But LINQ still has a feature that further exacerbates the problem. This technology uses a technique called lazy evaluation . When you run this line of code nothing is actually running. Only when you need the data or materialize the data collection will the execution be done. This can be done seconds, minutes or hours later in some cases. Even the time between checking one element and another can take a long time. It's not that common, but it can happen.
Can you see how the results can be very different? One of them will always use the time of the moment of the effective execution of an element, because you used a code that tells it to get a new time each time it goes through it. The other takes a time and saves it, then every time it evaluates an element, even if a long time later, the time to be used is always the same, it's the one that took it right at the beginning of the execution.
I like LINQ, but I see a problem with it because people don't understand what it is, they think there is magic inside it and that they don't need to understand any of this "magic". Without deeply understanding LINQ I think people shouldn't use it.
If you wrote these codes by hand, maybe you could understand better why this happens:
foreach (var c in Lista()) {
if (c.Validade > DateTime.Now) {
//faz alguma coisa
}
//talvez chama algo aqui demorado
}
E
var agora = DateTIme.Now;
foreach (var c in Lista()) {
if (c.Validade > agora) {
//faz alguma coisa, poderia montar uma outra lista, mas seria a materialização
}
//talvez chama algo aqui demorado
}
I put it on GitHub for future reference .
LINQ is not simple
And one of the problems I see most people having with LINQ is using a ToList()
to materialize the data collection. When a person does this, he probably shouldn't use LINQ, the advantage of this technique is that it doesn't materialize unnecessarily and if he always needs it, then LINQ doesn't help as much as it could. In this case you are probably accessing the data collection without materializing for nothing and this is good, but it is also the cause of the difference.
Conclusion
Which gives the expected result? Go with him. I think the second is better in most cases, but I see scenario where the first is more useful. Or fix the modeling error.