Unpassable Where Clauses LINQ-to-SQL - linq

As I'm struggling to learn LINQ I’ve managed to generate a SQL statement with "AND (0 = 1)" as part of the where clause. I'm just wondering if this result is common in poorly written queries and is a known issues to try and avoid or if I am doing something totally backwards to end up with this.
Update
public static IEnumerable<ticket> GetTickets(stDataContext db,string subgroup, bool? active)
{
var results = from p in db.tickets
where
( active == null || p.active == active )
/*(active == null ? true :
((bool)active ? p.active : !p.active))*/ &&
p.sub_unit == db.sub_units.Where(c=>subgroup.Contains(c.sub_unit_name))
select p;
return results;
}
If I ignore the active part and just run
public static IEnumerable<ticket> GetTickets1(stDataContext db,string subgroup, bool? active)
{
return db.tickets.Where(c => c.sub_unit.sub_unit_name == subgroup);
}
It returns the groups of tickets I want ignoring the active part.

I'd pull the processing out of the ternary operators.
where ( active == null || p.active == active )
EDIT
The rest of the where clause looks funky too... why is it not just doing
&& p.sub_unit.sub_unit_name == subgroup
or
&& subgroup.Contains(p.sub_unit.sub_unit_name)
?

That is some pretty heavy abuse of the ternary operator.
This expression:
(active == null ? true :
((bool)active ? p.active : !p.active))
Is equivalent to the following logic:
bool result;
if (active == null)
{
result = true;
}
else
{
if ((bool)active)
{
result = p.active;
}
else
{
result = !p.active;
}
}
result &= ...
Think carefully about what this is doing:
If active is null, you're fine, it skips to the next condition.
If active is true, result is true at the end of the conditional.
If active is false, result is false at the end of the conditional.
In the last case, the query can never return any rows!
#Tanzelax has already supplied a simple rewrite. The main idea is that you want to compare p.active to active, not actually evaluate the condition as p.active.

This is probably caused by a null value in one you the columns you have declared as non-nullable. LINQ2SQL makes all columns non-nullable by default. Go back to the designer and change the fields to allow values to be null. Unfortunately this feature is By Design (see connect.microsoft.com link below.)
(linq) incorrect sql generated for row.column == localVar when localVar is null (should be "is null" check)

Related

How to pass an external parameter to LINQ where clause in CRM

I have a LINQ query which works fine as for stand alone lists but fails for CRM
var lst = new List<bool?>();
lst.Add(null);
lst.Add(true);
lst.Add(false);
bool IsWet = false;
var newlst = from exch_HideVoiceSignature in lst where
(((exch_HideVoiceSignature!=null && exch_HideVoiceSignature==false
|| exch_HideVoiceSignature== null) )&& !IsWet) select exch_HideVoiceSignature;
newlst.Dump();
var question = from q in exch_questionSet where ((q.exch_HideVoiceSignature != null
&& q.exch_HideVoiceSignature.Value == 0 )|| q.exch_HideVoiceSignature == null )
&& !IsWet select q.exch_HideVoiceSignature;
question.FirstOrDefault().Dump();
As you can see I can pass the variable IsWet to LINQ query for a standard list fine and get values for first list. But when I execute the same for second list, I get the following error
Invalid 'where' condition. An entity member is invoking an invalid property or method
The CRM LINQ provider won't support the evaluation you attempting. It only supports evaluation of where criteria is evaluating an entity field.
That's not a problem. Since you want the LINQ query to only use the where clause if IsWet is false (correct me if I'm wrong on that.) So we simply do the evaluation to determine if the where clause should be added or not. Then execute your query.
var question = from q in exch_questionSet
select q.exch_HideVoiceSignature;
if (!IsWet)
{
question.Where(x => ((x.exch_HideVoiceSignature != null
&& x.exch_HideVoiceSignature.Value == 0) || x.exch_HideVoiceSignature == null));
}
question.FirstOrDefault().Dump();
I am constantly confronted with that problem.
Try to "detach" (for example call .ToArray()) your query (while it is "clear") from CRM and then filter query using external parameter. This should help.
var question =
(from q in exch_questionSet
where (
(q.exch_HideVoiceSignature != null && q.exch_HideVoiceSignature.Value == 0 ) ||
q.exch_HideVoiceSignature == null )
select q.exch_HideVoiceSignature
).ToArray().Where(q => !IsWet);
question.FirstOrDefault().Dump();
UPDATE
If you are using IsWet flag to control blocks of conditions (enable and disable them from the one point in the code) then probably you may be interested in class named PredicateBuilder which allows you to dynamically construct predicates.
Just because I had an existing query with lot of other joins etc. and I wanted to pass this additional parameter to it I ended up using a var statement which dumps the rows to a list and applies the where clause in the same statement
bool IsWet =true ;
var question = ...existing query ...
select new {
...existing output ...,
Wet =q.exch_HideVoiceSignature != null &&
q.exch_HideVoiceSignature.Value == 119080001,
Voice = q.exch_HideVoiceSignature == null ||
(q.exch_HideVoiceSignature != null &&
q.exch_HideVoiceSignature.Value == 119080000) ,
}
;
var qq = IsWet ?
question.ToList().Where(X=> X.Wet ) :
question.ToList().Where(X=> X.Voice );
qq.FirstOrDefault().Dump();

Is there a way to improve this LINQ?

Code :
IList<Evento> Eventi = new List<Evento>() { };
Eventi = (from Evento ae in new Eventi()
select ae).ToList();
if (strNome != "")
{
Eventi = Eventi.Where(e => e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != "").ToList();
}
if (strComune != "")
{
Eventi = Eventi.Where(e => e.Comune != null && e.IDComune == strComune).ToList();
}
if (strMesi != "")
{
Eventi = Eventi.Where(e => MesiSelezionati.Contains(DateTime.Parse(e.DataEvento).Month.ToString())).ToList();
}
I know all query are merged, during running time of code, in only 1 LINQ statement. But, as you can see, I convert the List -> ToList() many times. This, I think, here, is the only part when I waste time, right? How can I avoid this and improve performance?
Why so many lists/ToLists? What's wrong with IEnumerable/IQueryable?
var eventi = (from Evento ae in new Eventi()
select ae);
if (strNome != "")
{
eventi = eventi.Where(e => e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != "");
}
if (strComune != "")
{
eventi = eventi.Where(e => e.Comune != null && e.IDComune == strComune);
}
if (strMesi != "")
{
eventi = eventi.Where(e => MesiSelezionati.Contains(DateTime.Parse(e.DataEvento).Month.ToString()));
}
// if you do need a list, then do so right at the end
var results = eventi.ToList();
EDIT: To clarify some principles for Caesay
Caesay, thank you for taking the time to test the implementation to confirm the deferred loading works as intended; much kudos!
I wanted to explain why I disagree with your comment about the above approach being optimized at run-time whilst yours being optimized at compile time.
The above approach is, for lack of a better description, the intended approach. This is because the assignments to eventi are correctly appending expressions to the source of the IEnumerable/IQueryable.
Your approach is only supported by certain providers, such as Linq to Entities, which expect a Func(Of T, TResult) to be passed to their Select, Where, etc Extensions. Many providers, such as Entity Framework and Linq to Sql provider, provide IQueryable, which implements IEnumerable. The difference here, however, is that IQueryable's Select, Where, etc, expect you to pass an Expression(Of Func(Of T, TResult)).
In those cases, your code will not behaveas expected (or at least as I would expect), because Expression does not support multi-line lambda, where as the compiler will correctly interpret my statements and compile them into Expression>.
As a simple example:
public void Test<T1, T2>(System.Linq.Expressions.Expression<Func<T1, T2>> arg)
{
throw new NotImplementedException();
}
public void Test()
{
Test((string x) => x.ToLower());
Test((string x) =>
{
return x.ToLower();
});
}
In the above example, the first expression is absolutely fine. The second, which is based loosely on your example, will fail with the exception:
A lambda expression with a statement body cannot be converted to an expression tree
The compiler may recognise your statements as a Func which it knows is supported on IEnumerable. The result would be that the query to the database would not include any of your Where expressions, returning the whole data source. Once the data source was in-memory, it would then apply your IEnumerable Where clauses. Personally, I much prefer passing these kind of things to the database so that I'm not wasting bandwidth on returning much more data than I need, and I can utilise my Data Sources ability to filter data which is likely (and vastly in Sql Server's case) better than doing so in-memory.
I hope that makes sense and is of some use to you?
You can use fluent syntax to start with (to avoid List).
Alternatively you can combine the condition in one query.
Eventi =(new Eventi()).Where(e => e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != "" && e.Comune != null && e.IDComune == strComune &&MesiSelezionati.Contains(DateTime.Parse(e.DataEvento).Month.ToString())).ToList();
I have assumed Eventi implements IEnumerable < Evento > as you have used similarly in query syntax
Given that you test for strNome not being empty, the second half of your first Where clause will never be called.
So e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != ""
can be written e.Titolo.ToLower().Contains(strNome.ToLower())
You could also calculate strNome.ToLower() outside the lambda to ensure it is calculated just once.
In addition you can streamline the Where clauses and remove the ToList() as others have suggested.
Another alternative you should be aware of is LinqKit which allows you to combine lambda expressions more easily which although not needed in this case since Where ... Where is good enough for an 'And', you might one day need an 'Or' and then you need a different solution.
Or, better yet, use the method explained here to create your own And and Or methods that perform Expression 'magic' to give you a single expression that you can hand off to Linq-to-Sql or any other Linq provider.
Combine the whole thing into 1 linq query like this:
var eventi = from Evento e in new Eventi() select e;
eventi = eventi.Where(e =>
{
if (strNome != "")
{
if(!(e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != ""))
return false;
}
if (strComune != "")
{
if(!(e.Comune != null && e.IDComune == strComune))
return false;
}
if (strMesi != "")
{
if(!(MesiSelezionati.Contains(DateTime.Parse(e.DataEvento).Month.ToString())))
return false;
}
return true;
});
var results = eventi.ToList();
This is logically equivalent to your code, but should be much faster. Although I was not able to test it, because I can't compile it.

Linq with Logic

I have simple Linq statement (using EF4)
var efCars = (from d in myentity.Cars
where d.CarName == inputCar.CarName
&& d.CarIdNumber == inputCar.IdNumber
&& d.Make == inputCar.Make
select d.Car);
I want it to be smarter so that it will only query across one or more of the 3 fields IF they have values.
I can do a test before, and then have a separate linq statement for each permutation of valyes for inputcar
(i.e. one for all 3, one for if only carname has a value, one for if carname AND CarIdNumber has a value etc etc)
but there must be a smarter way
Thanks!
If "has no value" means null then you can use the null coalescing operator ?? to say take the first value if populated, otherwise take the second:
var efCars = (from d in myentity.Cars
where d.CarName == (inputCar.CarName ?? d.CarName
&& d.CarIdNumber == (inputCar.IdNumber && d.CarIdNumber)
&& d.Make == (inputCar.Make && d.Make)
select d.Car);
This basically says if a value exists it must match, otherwise treat it as matching
However if instead you're saying "when a special value (empty string) ignore it, otherwise match" then you can do one of two approaches (or possibly more!):
where (inputCar.CarName == "" || d.CarName == inputCar.CarName)
where (string.IsNullOrEmpty(inputCar.CarName) || d.CarName == inputCar.CarName)
For performance (when dealing with database queries) it can sometimes be beneficial to let EF generate queries based on the filters, instead of using one generic query. Of course you will need to profile whether it helps you in this case (never optimize prematurely), but this is how it would look if you dynamically build your query:
var efCars =
from car in myentity.Cars
select car;
if (inputCar.CarName != null)
{
efCars =
from car in efCars
where care.CarName == inputCar.CarName
select car;
}
if (inputCar.IdNumber != null)
{
efCars =
from car in efCars
where care.CarIdNumber == inputCar.IdNumber
select car;
}
if (inputCar.Make != null)
{
efCars =
from car in efCars
where care.Make == inputCar.Make
select car;
}
where (inputCar.CarName != null || d.CarName == inputCar.CarName) &&...

Linq Where Clause Change based on Parameters

I have a linq statement that returns a list of records based on where clause
This where clause checks for two parameter values.
Out of which one parameter is optional.
so i need a suggestions if i can switch my where clause based on the optional Parameter
something like this
if(locid==0)
{
where (p.CustomerID == custid)
}
else{
where (p.CustomerID == custid) & (p.LocationID == locid )
}
can any one help me how can i get this work.
thanks
You could try writing it like this:
where (p.CustomerID == custid) && (locid == 0 || p.LocationID == locid )
Yes - queries can be composed (although you don't need this for this particular case as #rsbarro pointed out):
var query = p;
if(locid==0)
query = query.Where( p =>p.CustomerID == custid);
else
query = query.Where( p =>p.CustomerID == custid & p.LocationID == locid);
//any other conditions
As BrokenGlass mentioned, you should use composition:
IQueryable<Foo> query = unfiltered.Where(p => p.CustomerID == custId);
if (locid != 0)
{
query = query.Where(p => p.LocationID == locid);
}
Note that the query is not executed until you start reading data from it, so you needn't worry about this making multiple requests.
It looks like in your original post you were trying to use query syntax piecemeal - that won't work, but the "dot notation" is pretty simple here. You can always create your initial query using a query expression if you want - again, that query won't be executed immediately anyway.

If condition in LINQ Where clause

With Linq, can I use a conditional statement inside of a Where extension method?
var query = someList.Where(a => (someCondition)? a == "something" : true);
so, if 'someCondition' is false, 'Where' will be skipped.
Yes you can like:
var query = someList.Where(a => a == "something");
if (condition)
{
query = query.Where(b => b == "something else");
}
var result = query.ToList();
Because Where is producing an IQueryable, the execution is deferred until the ToList in my example so you can chain Wheres together as much as you want and then just execute it after you have passed all your conditions.
Make use of WhereIf extenstion method avaialbe in linq
Example
if (SearchControlMain.PostingID.HasValue)
query = query.Where(q => q.PostingID == SearchControlMain.PostingID);
instead of above go for the below
query = query.WhereIf(SearchControlMain.CategoryID.HasValue, q => q.CategoryID == SearchControlMain.CategoryID);
LINQ WhereIf Extension Method
LINQ to SQL Where Clause Optional Criteria
Not sure if this is appropriate but it is quite useful, you can use ifs quite handily with conditional where clauses:
var r = (from p in productinfo.tblproduct
where p.Accountid == accountid
select p);
if (uuf1 != null)
r = r.Where(p => p.UnitUserField1 == uuf1);
if (uuf2!= null)
r = r.Where(p => p.UnitUserField2 == uuf2);
So the where clause will be amended according to what is in UUF1 or UUF2 i.e. you might have only UUF1 with info, in which case it will take that and ignore the UUF2 where clause, you might have both in which it will take both or you might not have anything in UUF1 or 2 and your where clause will just take the accountid as the where clause.
In my case there were two "conditional" where depending on search keys, so I did:
var query = db.Package.Include("SomeThing")
.Where(item => searchString1 == null || searchString1 == "" || item.Contains(searchString1))
.Where(item => searchString2 == null || searchString2 == "" || item.Contains(searchString2));
...
from item in items
where condition1
&& (condition2 ? true : condition3)
select item
This is how can you can do it with the noob Linq syntax.
This applies the condition3 only if condition2 is false.
If condition2 is true, you are essentially doing && true which has no effect on the where clause.
So it is essentially doing this:
if(condition2)
{
from item in items
where condition1
select item
else
{
from item in items
where condition1
&& condition3
select item
}
I had a scenario like this where I had to check for null within the list itself. This is what I did.
items = from p in items
where p.property1 != null //Add other if conditions
select p;
// Use items the way you would use inside the if condition
But as Kelsey pointed out this would work too -
items = items.Where(a => a.property1 != null);
I'm not sure what the question is, but a possible answer could be:
Yes,
list.Where(item => { if (Foo(item)) return true; else return false; });
It would be a complicated way of saying something simple, though.
In my case, I wanted to keep the elements which met my criteria and log the ones that didn't without iterating multiple times.
var merchantsWithLocations = allMerchants.Where(m =>
{
if (m.Locations?.Any() != true)
{
_logger.Log("Merchant {merchantId} has no locations", m.Id);
return false;
}
return true;
};
Any time you want to do a side-effect per element (such as logging), breaking out the lambda into a statement body makes it easy to reason about.

Resources