Optional condition not working linq - c#

I having some problem in Optional condition in Lambda express like
var shouldCheckDate = !string.IsNullOrEmpty(fromDate);
var result = (from r in db.Notify
where r.ApplicationId == applicationId
&& (shouldCheckDate || r.CreatedDate.Date > date)
select r
).Count;
problem is here it always excuting both condition either shouldCheckDate true are false. I am doing any thing wrong?

This is due to the fact, that LINQ is translated to SQL and SQL, in contrast to C#, executes all parts of a condition, no matter if one of them allready returned true.

You could compose the IQueryable by adding the where clauses only when needed:
var query = db.Notify;
query = query.Where(r => r.ApplicationId == applicationId);
if (shouldCheckDate) {
query = query.Where(r => r.CreatedDate.Date > date);
}
var result = query.Count();

You have OR statement here,
(shouldCheckDate || r.CreatedDate.Date > date)
so even one of these conditions which is true is enough for whole statement to be true.
Maybe r.CreatedDate.Date is always > date ?

Related

Convert if else statement to simple linq query

I have been trying to convert a simple if else to a linq statement.
The if else statement goes something like this:
if ( MessageStatus = 1 )
then MessageCount > 0
else if ( MessageStatus = 2 )
then MessageCount = 0
else
do nothing
This is what I have in my linq so far and it didn't query out the data I wanted
var query = _context.Message
.Where(c => request.MessageStatus.Equal(2) ? c.MessageCount.Equals(0) :
request.MessageStatus.Equal(1) ? c.MessageCount > 0 :
request.MessageStatus.Equal(0));
It should be able to return rows of data but instead it return nothing.
Where does the problems lie at?
Thanks
What you're asking about is conditional filtering. request.MessageStatus is a value you know before you execute the query, so you can use it to define which Where to add to the query.
This works by creating a IQueryable<...> for the whole data set (your table), then adding on the Where that you want based on some condition. It's important to note that you're always doing query = query.Where(...);, if you don't re-assign query you lose the additional filter.
// "request" is defined earlier
IQueryable<Message> query = _context.Message; // might need .AsQueryable() here
if (request.MessageStatus == 1)
{
query = query.Where(r => r.MessageCount > 0);
}
else if (request.MessageStatus == 2)
{
query = query.Where(r => r.MessageCount = 0);
}
else
{
// assuming that this means "include all records",
// don't perform any additional filtering
// you don't need this "else"
}
List<Message> results = query.ToList();
It's also good to know that this pattern works outside of EF as well with basic IEnumerables and in memory collections.

Why does Linq on date range return null on a combined query, works fine on a seperated query

Issue: while filtering records within a date range & matching a CityID using LINQ, the query succeeds when written in 2 steps; however, it fails when combined as one query!
How can the LINQ query be rewritten so that -- it can perform both filters (i.e. match the CityId & retrieve records in the date range in the same step to improve performance?
I got it to work in two steps fine,
i.e. do a
var Step1 = db.weekRecord.Where(x => x.CityId == CityRecord.Id).ToList();
and then
Step1.Where(x => x.date.Date >= fromDate.Date
&& x.date.Date <= toDate.Date)
.ToList();
it fails when I combine them!!
// works when done in 2 steps!!
var weeklyWeather = db.weekRecord
.Where(x => x.CityId == CityRecord.Id
&& (x.date >= weekStarting && x.date <= weekEnding))
// - when combined results are NULL!??
var weeklyWeather2 =
db.weekRecord(x => x.date.Date >= fromDate.Date && x.date.Date <= toDate.Date)
.ToList();
After looking up other SO answers, I tried this TruncateTime as well... could not get it to work..
// is this correct, from SO answers, DbFunctions.TruncateTime
var testQueryRecrods = db.weekRecord
.Where(x => x.CityId == CityRecord.Id)
.Where(x =>
DbFunctions.TruncateTime(x.date.Date) >= DbFunctions.TruncateTime(fromDate.Date)
&& DbFunctions.TruncateTime(x.date.Date) <= DbFunctions.TruncateTime(toDate.Date))
.ToList();
ERROR:
[NotSupportedException: The specified type member 'Date' is not
supported in LINQ to Entities. Only initializers, entity members, and
entity navigation properties are supported.]
System.Data.Entity.Core.Objects.ELinq.MemberAccessTranslator.TypedTranslate(ExpressionConverter
parent, MemberExpression linq) +452
System.Data.Entity.Core.Objects.ELinq.TypedTranslator`1.Translate(ExpressionConverter
parent, Expression linq) +49
The question is confused, but I would assume the problem is the .Date. Unlike linq2sql, entity framework can not translate .Date to sql. But you can rewrite it like
var fromDateDate = fromDate.Date;
var toDateDate = toDate.Date;
var testQueryRecrods = db.weekRecord
.Where(x => x.CityId == CityRecord.Id)
.Where(x => DbFunctions.TruncateTime(x.date) >= fromDateDate
&& DbFunctions.TruncateTime(x.date) <= toDateDate)
.ToList();
And it would work. To some point. What EF generates is actually totally stupid in this case. Unlike linq2sql, EF generates query, that is not sargable (in my case*). It can run thousands of times slower than necessary. I would recommend to avoid the conversion to date completely:
var fromDateDate = fromDate.Date;
var toDateDate1 = toDate.Date.AddDays(1);
var testQueryRecrods = db.weekRecord
.Where(x => x.CityId == CityRecord.Id)
.Where(x => x.date >= fromDateDate
&& x.date < toDateDate1)
.ToList();
As #juharr pointed out, when you split the query, you run first half against server and the second half as linq to objects. In that case the .Date works, but you download many more records in the first half than you need.
*the datetime type may be the problem, maybe it would work better with datetime2, I did not test this scenario
An offbest suggestion is to write your own LINQ extensions for this..
public static class ext
{
//This extension compares one date to another... if you can call from Linq
public static bool GreaterThan(this DateTime self, DateTime CompareDate
{
if (self.Year > CompareDate.Year) return true;
else if ((self.Year == CompareDate.Year) && (self.Month > CompareDate.Month)
return true;
else if ((self.Year == CompareDate.Year) && (self.Month == CompareDate.Month) && (self.Day > CompareDate.Day))
return true;
return false;
}
}
I think, there is no option except using DbFunctions.TruncateTime for Linq to Entities. Because, as SQL Server query Linq to Entities should perform convertion datetime to date and the best method which can be used is DbFunctions.TruncateTime. I just debugged the DbFunctions.TruncateTime convertion and the translated query seems like;
WHERE (convert (datetime2, convert(varchar(255), [Extent1].[CreationDate], 102) , 102)) > #p__linq__0
As you see, while performing the conversation, there is a redundant string conversation here. However, the EF would convert the datetime to date in SQL just like this 'cast(CreationDate as date)'. But it is not.
So, there are two options here.
1- If you have very huge table which the performance is affected by redundant string conversations, you should build your query manually in SQL as stored procedure or something like and execute it from context.
2- If you don't have performance considerations like that; just use DbFunctions.TruncateTime(x.date)
var testQueryRecrods = db.weekRecord
.Where(x => x.CityId == CityRecord.Id)
.Where(x => DbFunctions.TruncateTime(x.date) >= fromDate.Date && DbFunctions.TruncateTime(x.date) <= toDate.Date)
.ToList();

How to use OR operator in Linq

I want to use OR function in my linq query.
Ex:
Sql:
select * from tblusers where userid=1 and status='a' or status='b';
Linq:
var result= _Repository.selectAll().where(x=>x.UserID==1 && x.status=="a" OR x.status=="B");
It's does work for linq query. so does anyone have any idea?
So you are aware about the && operator for comparison, then why not make a try with || operator? Anyway here is the solution for your problem, Following code will get you the result with UserID is 1 and status is either a or B.
_Repository.selectAll().where(x=>x.UserID==1 && (x.status=="a" || x.status=="B"));
Create an array of status you want to check and then use contains. something like this.
var statusList = new[] {"a", "b"};
.Where(x=> x.UserID == 1 && statusList.Contains(x.status));
Adding my 2 cents in here, there is another way you can write your sql query in C# which more or less resembles the sql syntax.
var result = from x in _Repository.SelectAll() where x.UserID == 1 && (x.Status == "a" || x.Status == "B") select x;
This syntax is Query Syntax/Expression where as your snippet is Method Syntax/Expression. Both will achieve same results. Also behind the scene, query syntax is compiled to Method syntax.
At compile time, query expressions are converted to Standard Query Operator method calls according to the rules set forth in the C# specification. Any query that can be expressed by using query syntax can also be expressed by using method syntax. However, in most cases query syntax is more readable and concise.
Or other approach with List.Contains, which generates sql query like
SELECT * FROM tblusers WHERE userid=1 AND status IN ('a','b');
var acceptedStatus = new List<string> { 'a', 'b' };
var result= _Repository.selectAll()
.Where(x => x.UserID == 1)
.Where(x => acceptedStatus.Contains(x.Status));
Notice that instead of && operator you can use another Where function and chain them. Can be more readable then fit all conditions in one line.
Try code:
var result =( from x in _Repository.selectAll()
where x.UserID==1 && (x.status=="a" || x.status=="B") select x);
another Solution
var result =( from x in _Repository.selectAll().where(c=>c.status=="a" || x.status=="B")
where x.UserID==1 select x);

Cannot implicity convert type System.Linq.Expression<System.Func<Object, bool>> to bool

actually I have an Expression like this that work very well in the case of Linq to Entity
public static Expression<Func<Tender, bool>> GetPublic()
{
var now = DateTime.Now.GetEasternStandardDateTime();
return tender => tender.EndDate > DateTime.Now &&
tender.IsClosed == false &&
tender.IsCancel == false &&
tender.PrivacyLevelId == 1;
}
I can use the expression like this : _repositoryObject.Where(GetPublic()) and I will get results.
But I cannot use the same expression when I have to do Linq to SQL pattern like this
var results = from t in Tenders
where Tender.GetPublic()
select t;
I have this error
Cannot implicity convert type
System.Linq.Expression> to bool for Linq to
SQL
and I don't know why...
Karine
Firstly, I suspect you actually mean you can call it as:
_repositoryObject.Where(GetPublic())
It's important that you call the method to get the expression tree.
Your query expression is being converted into:
var results = Tenders.Where(t => Tender.GetPublic());
... which isn't what you're looking for. Basically, query expressions are translated into method calls using lambda expressions. You don't want that in this case, so you shouldn't use a query expression.
If you want to do other things with a query expression, you can always mix and match, e.g.
var results = from t in Tenders.Where(Tender.GetPublic())
where t.SomeOtherProperty
select t.SomethingElse;
If you change your method to take an IQueryable<Tender>, and return another IQueryable<Tender> where your criteria are added, then it might work.
public static IQueryable<Tender> AddPublicCriteria(this IQueryable<Tender> tenderQuery) {
// I've omitted your call to DateTime.Now.GetEasternStandardDateTime()
// since you do not use it.
return tenderQuery
.Where(tender => tender.EndDate > DateTime.Now &&
tender.IsClosed == false &&
tender.IsCancel == false &&
tender.PrivacyLevelId == 1
);
}
var query = Tenders; // Original table or data-set
query = query.AddPublicCriteria(); // Add criteria
var results = query.ToList(); // Run query and put the results in a list

Combining LINQ queries in LINQ to CRM causes problems?

Something weird is going on.
If I do this:
var allAccountsQuery = from acc in baseQ
where
//high potential check - 1, 2, 3
(acc.mcpl_potencjal_klienta == 1 || acc.mcpl_potencjal_klienta == 2 || acc.mcpl_potencjal_klienta == 3) &&
//directors block check
((acc.mcpl_blokada_dyrektorska == true && acc.mcpl_blokada_do <= date) || acc.mcpl_blokada_dyrektorska == false || acc.mcpl_blokada_dyrektorska == null) &&
//sack assign date check
(acc.mcpl_dataprzypisaniazworka == null || acc.mcpl_dataprzypisaniazworka < date) &&
//owner change check
(acc.mcpl_datazmianywasciciela == null || acc.mcpl_datazmianywasciciela < date) &&
//creation date check
//TODO:For testing!
//(acc.mcpl_data_utworzenia_test < date)
(acc.createdon < date)
select acc;
var joinQuery = from acc in allAccountsQuery
join opp in ctx.opportunityopportunities on acc.accountid equals opp.customerid.Value
select new
{
Account = acc,
Opportunity = opp
};
Plugins.Common.XrmHelper.ClearCache("account");
var joinResult = joinQuery.ToList();
Then I'll get an unknown platform error when executing this query. I need to copy-paste the WHOLE where clause from allAccountsQuery to the joinQuery and use baseQ again, and then it works.
What's going on here? I thought you can safely join LINQ queries as long as you're not doing any unsupported operations.
PS. The STRANGEST part is that the pasted code WILL work with slightly different where conditions.
PPS. baseQ is just an even simpler where query, much like the allAccountsQuery.
Maybe is not the answer but as I can't leave a comment and no one has answer I think this could help.
Why you don't do the join in the first query? As from I know the LINQ CRM queries have problems joining tables when in the clause WHERE we have the OR Predicate, and not when we try to select from different tables, I think for you query should work. I have one post explaining what I learned.
Linq-to-CRM has a limited set of supported operations compared to other providers life EF or Linq-to-SQL.
You may have better success hydrating one or both of the two queries. Since your account query has a where clause try hydrating it:
var joinQuery = from acc in allAccountsQuery.ToList() // call ToList() to hydrate the query
join opp in ctx.opportunityopportunities
on acc.accountid equals opp.customerid.Value
select new
{
Account = acc,
Opportunity = opp
};
If you have a LARGE number of Opportunities you may want to try and filter that query based on the accounts returned from the first query before doing the Join.

Categories