Complex Expressions in a LINQ Where Clause - linq

I was wondering if it is possible to include inner variables or delegates in linq statements?
I currently am using Linq to XML with XPath extensions and am using a where clause on an element that I cannot guarantee will exist.
Here is a sample of what I mean:
var result =
from record in xml.Root.XPathSelectElements("record")
where ...
select record;
I want the where to be something like:
where
{
var element = record.XPathSelectElement("element[#type='sometype']");
return (element != null && element.Value.Contains("keyword"));
}

You want the "let" keyword in Linq. Something like this?
var result =
from record in xml.Root.XPathSelectElements("record")
let element = record.XPathSelectElement("element[#type='sometype']")
where (element != null && element.Value.Contains("keyword"))
select record;

You could use a "let" clause here;
from record in xml.Root.XPathSelectElements("record")
let element = record.XPathSelectElement("element[#type='sometype']")
where element != null && element.Value.Contains("keyword")
select record;

I don't know the query syntax well enough to say for sure, but this would be trivial with the functional syntax:
var result = xml.Root.XPathSelectElements("record").Where(record => {
var element = record.XPathSelectElement("element[#type='sometype']");
return (element != null && element.Value.Contains("keyword"));
});

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();

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.

How can i use LINQ with property

Hi I have a array list if type class "DtContract".
ArrayList listOfContracts_;
foreach (DTContract contract in listOfContracts_)
{
if (contract.Engine != DTIsland.EngineType.AMADEUS && contract.Engine !=DTIsland.EngineType.SABRE)
continue;
}
I want to do it through LINQ.
I want to filter the Contract whose EngineType == AMADEUS && EngineType == SABRE. Please suggest how can i do it through Linq and get the result in List or in array list.
I am doing this to Optimize the code.
Please Help...
var result = listOfContracts_.Where(contract=>contract.Engine != DTIsland.EngineType.AMADEUS && contract.Engine !=DTIsland.EngineType.SABRE).ToList();
your foreach loop doen't do anything meaningful, what you are trying achieve?
If you want to use linq
listOfContracts_.OfType<DTContract>()
.Where(contract => contract.Engine != DTIsland.EngineType.AMADEUS &&
contract.Engine != DTIsland.EngineType.SABRE);

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.

LINQ BuildContainsExpression With OR conditions

I'm trying to get the following SQL query to work in LINQ:
Select id from table1 where id in (1,2) or canceledId in (1,2)
I'm using BuildContainsExpression to achieve the "IN" condition, but I can't figure out how to implement the "or" condition.
My shot in the dark is as follows:
var identifiers = new List<int> {1,2};
var query = (from t in Context.Table1
select t);
var query =
query.Where(BuildContainsExpression<Table1, int>(t => t.Id, identifiers));
if (showCanceled)
{
var expression = query.Where(BuildContainsExpression<Table1, int>(t => t.CanceledId.Value, identifiers)).Expression;
Expression.Or(expression, transactionsQuery.Expression);
}
But I get the following exception:
The binary operator Or is not defined for the types 'System.Linq.IQueryable1[Table1]' and 'System.Linq.IQueryable1[Table1]'..
Any ideas? -Am I in the right direction?
Thanks,
Nir.
You are appending your OR in the wrong place. What you are doing now is effectively something like this:
(from t in Context.Table1
where identifiers.Contains(t.Id)
select t)
OR
(where identifiers.Contains(t.CanceledId))
The second problem is that the BuildContainsExpression method you use, returns a lambda expression, something that looks like this:
t => t.Id == 1 || t.Id == 2 || ...
You can't change this expression once it's generated. However, that's what you want because you'd like to have something like this:
t => t.Id == 1 || t.Id == 2 || ... || t.CanceledId == 1 || t.CanceledId == 2 || ...
You can't simply take the body of this lambda expression and or it together with another expression because it depends on the parameter t.
So what you can do is the following:
// Overload of BuildContainsExpression.
private static Expression<Func<T, bool>> BuildOtherContainsExpression<T>(
ParameterExpression p, Expression field1, Expression field2, int[] values)
{
var eq1 = values.Select(v => Expression.Equal(field1, Expression.Constant(v)));
var eq2 = values.Select(v => Expression.Equal(field2, Expression.Constant(v)));
var body = eq1.Aggregate((acc, equal) => Expression.Or(acc, equal));
body = eq2.Aggregate(body, (acc, equal) => Expression.Or(acc, equal));
return Expression.Lambda<Func<T, bool>>(body, p);
}
// Create a parameter expression that represents something of type Table1.
var parameter = Expression.Parameter(typeof(Table1), "t");
// Create two field expressions that refer to a field of the parameter.
var idField = Expression.Property(parameter, "Id");
var canceledIdField = Expression.Property(parameter, "CanceledId");
// And finally the call to this method.
query.Where(BuildContainsExpression<Table1>(
parameter, idField, canceledIdField, identifiers));
Your if statement would now look like this:
if (!showCanceled)
{
// Use original version of BuildContainsExpression.
}
else
{
// Create some expressions and use overloaded version of BuildContainsExpression.
}
I know I'm a bit late to the party here - but I think the original code in the original poster's question was 99% right.
The only wrong was that instead of
Expression.Or
it should have been
Expression.OrElse

Resources