LINQ to Entities with AddMonth method - c#

This is my code:
return Newsletterctx.Subscribers.Count(o =>
o.Validated == false &&
o.ValidationEmailSent == true &&
o.SubscriptionDateTime.AddMonths(1) < DateTime.Now);
I get this error:
LINQ to Entities does not recognize
the method 'System.DateTime
AddMonths(Int32)' method, and this
method cannot be translated into a
store expression.

You can use SqlFunctions classvar;
today = DateTime.Now; return Newsletterctx.Subscribers.Count(o =>
o.Validated == false &&
o.ValidationEmailSent == true &&
SqlFunctions.DateAdd("month",1,o.SubscriptionDateTime) <today);

Perhaps you can shift the date to test against instead:
DateTime testDate = DateTime.Now.AddMonths(-1);
return Newsletterctx.Subscribers.Count
(o => o.Validated == false
&& o.ValidationEmailSent == true
&& o.SubscriptionDateTime < testDate);

You have to use the Datetime outside the request because you are in the LINQ TO ENTITIES that don't use System.Datetime Library.
If you wish to use a fix date the you can define it outside the request as
DateTime compareDate = DateTime.Now.AddMonths(x);

Now, EntityFramework, Version=6 above, you can jus use System.Data.Entity.DbFunctions
return Newsletterctx.Subscribers.Count(o =>
o.Validated == false &&
o.ValidationEmailSent == true &&
DbFunctions.AddMonths(o.SubscriptionDateTime,1) < DateTime.Now);
However, in this case, use the temp variable testDate that answered by Fredrik Mörk uses less resources.

Related

EF Core declaring multiple variables on complex Where Clause

I have this LINQ Where clause that is declaring 2 variables on the SQL query
var parkingLotPrice =
_context.ParkingLotPrice
.Where(x => currentDate >= x.EffectiveDate && (currentDate <= x.ExpiryDate || x.ExpiryDate == null))
.ToQueryString();
It generates this SQL Query:
DECLARE #__currentDate_0 datetime2 = '2021-07-21T17:48:29.1106534-06:00';
DECLARE #__currentDate_1 datetime2 = '2021-07-21T17:48:29.1106534-06:00';
SELECT [p].[ParkingLotId],
[p].[PriceScheduleId],
[p].[EffectiveDate],
[p].[ExpiryDate]
FROM [ParkingLotPrice] AS [p]
WHERE (#__currentDate_0 >= [p].[EffectiveDate]) AND ((#__currentDate_1 <= [p].[ExpiryDate]) OR [p].[ExpiryDate] IS NULL)
Note: the declarations contains the same value.
The problem is the (currentDate <= x.ExpiryDate || x.ExpiryDate == null).
If I remove the null evaluation, it only declares 1 variable.
DECLARE #__currentDate_0 datetime2 = '2021-07-21T17:32:31.3980763-06:00';
SELECT [p].[ParkingLotId],
[p].[PriceScheduleId],
[p].[EffectiveDate],
[p].[ExpiryDate]
FROM [ParkingLotPrice] AS [p]
WHERE (#__currentDate_0 >= [p].[EffectiveDate]) AND (#__currentDate_0 <= [p].[ExpiryDate])
Is there a way of keeping the Where evaluation, but only declare 1 variable?
And what is wrong with it? Does it return a wrong result? I created hundreds queries with null and without and they always returned the right result.
I can see the real problem in your query. The way you compare dates. It will compare times too. In some cases it will return a wrong result. I highly recommend you to compare only dates
var parkingLotPrice = _context.ParkingLotPrice
.Where(x => EF.Functions.DateDiffDay(x.EffectiveDate,currentDate) >=0
&& (x.ExpiryDate == null || EF.Functions.DateDiffDay(currentDate,x.ExpiryDate)>=0 )).ToList();
or if you need some time try this
var parkingLotPrice = _context.ParkingLotPrice
.Where(x => EF.Functions.DateDiffMinute(x.EffectiveDate,currentDate) >=0
&& (x.ExpiryDate == null || EF.Functions.DateDiffMinute(currentDate,x.ExpiryDate)>=0 )).ToList();

how to convert string to datetime in Linq where clause

DateTime newDate = DateTime.Now.AddDays(-30);
var queryResultPage = (from r in CustomPlanning
where (r.Assignment == "In Use") &&
(r.CUST_SHRT_NM != null) &&
System.Convert.ToDateTime(r.lastlogontimestamp) < newDate &&
!(from uA in SurveyActivity
where uA.CustomDemandID == r.ID
select uA.CustomDemandID).Contains(r.ID)
select r)
.OrderBy(t => t.ID);
Above is my code. lastlogontimestamp is a string field in my table. i need to do this check to show the query result in a grid. Can someone please help?
ok i tried this and it worked
DateTime newDate = DateTime.Now.AddDays(-30);
var queryResultPage = (from r in CustomPlanning
where (r.Assignment == "In Use") && (r.lastlogontimestamp != null && r.lastlogontimestamp != string.Empty)
&& (r.CUST_SHRT_NM != null)
&& !(from uA in SurveyActivity where uA.CustomDemandID == r.ID select uA.CustomDemandID).Contains(r.ID)
select r).OrderBy(t => t.ID).ToList();
queryResultPage = queryResultPage.Where(r => System.Convert.ToDateTime(r.lastlogontimestamp) < newDate).ToList();
You cannot in this query directly. You need to materialize the results from the query in a list, and run another query to filter the returned CustomPlanning with Linq-To-Objects (it supports conversion).
You can convert newDate to string
string newDate = DateTime.Now.AddDays(-30).ToString();
and try to compare strings instead, but you need to cover lots and lots of cases, so it is not recommended. You can consider changing the type of lastlogontimestamp to DateTime, when you have dates, it is natural to store them as dates.

Whats wrong in this Linq query

var MyCours = Db.COURS.Where(C => C.CLASSE_ID == ClassID
&& DateTime.Now>= C.START_DATE
&& DateTime.Now <= C.END_DATE)
.ToList();
Some change still dont work !
A likely problem is that the provider can't project DateTime.Compare into a SQL statement. There is potentially also a logical error in the direction of comparison (unless you really want enddate < now < startdate), and I would also suggest using .ToList() to materialize into a list:
var theTimeNow = DateTime.Now;
var MyCours = Db.COURS.Where(C => C.CLASSE_ID == ClassID
&& theTimeNow >= C.START_DATE
&& theTimeNow <= C.END_DATE)
.ToList();
Projecting DateTime.Now into a variable isolates the non-determinism of it, i.e. to ensure that both comparisons are against the same time.

Conditional Operator without evaluating twice?

Say I have the following:
MyDate =
(db.MyTables.FirstOrDefault(x => x.MyID == idToFind).DateValue == DateTime.MinValue)
? DateTime.Now
: db.MyTables.FirstOrDefault(x => x.MyID == idToFind).DateValue
Is there any way to do this without running that LINQ query twice?
I cannot run it first into a temp variable because this query is itself part of a bigger LINQ query.
I cannot run it first into a temp variable because this query is
itself part of a bigger LINQ query.
You can use a let assignment within your query (or alternatively a projection that includes a helper field if you are using lambda syntax - that's what it gets compiled down to anyway):
var query = from foo in db.Bar
let bar = db.MyTables.FirstOrDefault(x => x.MyID == idToFind).DateValue
select new
{
MyDate = bar == DateTime.MinValue ? DateTime.Now : bar
}
Yes.
var dateValue = db.MyTables.FirstOrDefault(x => x.MyID == idToFind).DateValue;
return dateValue == DateTime.MinValue ? DateTime.Now : dateValue;
Now, when you're saying that the value can't be stuffed into a temp value, what do you mean? The above code certainly looks to be able to converted to that pattern.
Evaluate once and assign to a variable - use the variable in your conditional:
var item = db.MyTables.FirstOrDefault(x => x.MyID == idToFind);
MyDate = (item.DateValue == DateTime.MinValue)
? DateTime.Now
: item.DateValue
Or:
var theDate = db.MyTables.FirstOrDefault(x => x.MyID == idToFind).DateValue;
MyDate = (theDate == DateTime.MinValue)
? DateTime.Now
: theDate
You can still use a temporary variable, declare it but assign it inside that expression.
Lots of code might make it hard to read in some situations, but at least reduces duplication.
MyDate =
(temp = db.MyTables.FirstOrDefault(x => x.MyID == idToFind).DateValue) == DateTime.MinValue)
? DateTime.Now
: temp
Use LINQ's let statement
from a in someSource
let d = db.MyTables.FirstOrDefault(x => x.MyID == a.idToFind).DateValue
select (d == DateTime.MinValue) ? DateTime.Now : d;

LINQ WHERE clause using if statements

I am using c#.net
I have two textboxes which if !empty need to be part of a WHERE clause within a LINQ query.
Here is my code
var result = from a in xxxx select a;
if(!string.IsNullOrEmpty(personName))
{
return result.Where(a >= a.forename.Contains(personName) || a.surname.Contains(personName)
}
else if(!string.IsNullOrEmpty(dateFrom))
{
return result.Where(a >= a.appStartDateTime >= dateFrom.Date)
}
else if(!string.IsNullOrEmpty(personName) && !string.IsNullOrEmpty(dateFrom))
{
return result.Where(a >= a.forename.Contains(personName) || a.surname.Contains(personName) && a.appStartDateTime >= dateFrom.Date);
}
I thought this would work but it doesn't like the .Where and I cant access the 'a' for example a.forename (The name 'a' does not exist in the current context)
What am I going wrong, or can this not actually be done?
Thanks in advance for any help.
Clare
Instead of this:
result.Where(a.forename.Contains(personName))
Try this:
result.Where(a => a.forename.Contains(personName))
You appear to be missing the Lambda operator (=>).
try this
var result = from a in xxxx select a
where (string.IsNullOrEmpty(personName) || a.forename.Contains(personName)
|| a.surname.Contains(personName))
&& (string.IsNullOrEmpty(dateFrom)
|| a.appStartDateTime >= DateTime.Parse(dateFrom).Date);
dateFrom appears to be a string so you have to parse it to get a date time.
This logic should work but I have not tested it. I could be wrong.
You seem to only care if the forename or surname contain personName if the personName is not null or empty. So you can rewrite it to return things if the personName is null or empty or the fore or sur name contains person name. Since the || operator is short circuiting it will not check Contains if the personName is null or empty.
You can also combine the predicates and make the logic shorter and easier to read:
var result = from a in xxxx select a;
if (!string.IsNullOrEmpty(personName))
result = result.Where(a => a.forename.Contains(personName) || a.surname.Contains(personName)
if (!string.IsNullOrEmpty(dateFrom))
result = result.Where(a >= a.appStartDateTime >= dateFrom.Date)
return result;
This method of combining predicates works well with 'AND' conditions. If you need to 'OR' conditions, it's a little bit more involved. Thankfully, Joe Albahari has created PredicateBuilder (as part of LINQKit) that helps with this greatly.
Hope this helps.
..or with just one exit point:
var result = from a in xxxx select a;
Func<string, bool> func = null;
if(!string.IsNullOrEmpty(personName))
{
func = (a) => {a.forename.Contains(personName) || a.surname.Contains(personName)};
}
else if(!string.IsNullOrEmpty(dateFrom))
{
func = (a) => {a.appStartDateTime >= dateFrom.Date};
}
else if(!string.IsNullOrEmpty(personName) && !string.IsNullOrEmpty(dateFrom))
{
func = (a) => {a.forename.Contains(personName) || a.surname.Contains(personName) && a.appStartDateTime >= dateFrom.Date;};
}
return result.Where(func);

Categories