If condition in LINQ Where clause - linq

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.

Related

How to compare IEnumerable<string> for null in Linq query

For the following query:
var result = from sch in schemeDashboard
join exp in Expenditure on sch.schemeId equals exp.SchemeCode
into SchExpGroup
where sch.SectorDepartmentId == selectedDepartmentId &&
sch.YearCode == StateManager.CurrentYear
orderby sch.ADPId
select new
{
ModifiedAmounts = SchExpGroup.Select(a => a.ModifiedAmounts),
ProjectName = sch.schemeName,
ADPNo = sch.ADPId,
Allocation = sch.CurrentAllocation,
Expenditures = from expend in SchExpGroup
where expend.YearCode == StateManager.CurrentYear &&
expend.DepartmentId == selectedDepartmentId &&
InvStatus.Contains(expend.Status)
orderby expend.ADPId
group expend by expend.InvoiceId
};
I want to filter the above query on a condition so that result gives only those records where "ModifiedAmounts" are not null. I have tried as follow:
if (rbList2.SelectedIndex == 6)
{
result = result.Where(a => a.ModifiedAmounts != null));
}
but this gives error as:
Cannot compare elements of type
'System.Collections.Generic.IEnumerable`1'. Only primitive types,
enumeration types and entity types are supported.
Any suggestions as I am lost as how to rephrase the filtered query.
I think the problem is that ModifiedAmounts will never be null. Select will return an empty list. Unless SchExpGroup is null in which case you will get a null reference exception.
Try changing your code to
result = result.Where(a => a.ModifiedAmounts.Any());
if (rbList2.SelectedIndex == 6)
{
result = result.Where(a => a.!ModifiedAmounts.Any());
}

multiple where conditions with linq and list

I am attempting to use the following code to do an update on a table with entity framework. The where statement will only work if I remove the and, either side of the and will bring results, but the and results in a null. I know that the value I am searching for exists.
foreach (fdd element in FddList)
{
var slist = context.ResidenceFDDs.ToList<ResidenceFDD>();
ResidenceFDD fddtoupdate = slist
.Where(s =>
s.StName.Contains("Adrienne") &&
s.StNum == element.addressnumb.ToString())
.FirstOrDefault<ResidenceFDD>();
fddtoupdate.Comments = "Comment newly added.";
context.SaveChanges();
}
if you are using .toString() then use as below
ResidenceFDD fddtoupdate = slist.Where(
s =>
s.StName.Contains("Adrienne") &&
s.StNum == element.addressnumb.ToString())
.FirstOrDefault<ResidenceFDD>().AsEnumerable();

How do I add a condition to a LINQ query that uses groups?

Here is my current, working, query:
var lsFooterRow = from i in _context.Inventory
where i.ClaimId == claimID
&& i.Taxable == false
group i by new { i.ClaimId }
into grp
select new
{
SumOfReplValue = grp.Sum(i => i.Price),
SumOfACV = grp.Sum(i => i.ACV),
SumOfReplCost = grp.Sum(i => i.ReplacementCost)
};
What I'd like to add, to make it conditional, is something like this so that it adds a filter to the base query along with ClaimID and Taxable:
if (reportType == "R")
lsFooterRow = lsFooterRow.Where(i => i.ReplCost > 0);
This is not working because is does not recognize ReplCost, only SumOfReplValue, SumOfACV and SumOfReplCost.
Can someone please tell me, without doing the query in two steps, a way to add this condition? If there is no way to do it, a two step approach would be greatly appreciated :-)
Thanks in Advance!
If I understand correctly, you should probably break out intial query into multiple pieces.
var lsFooterRow = from i in _context.Inventory
where i.ClaimId == claimID
&& i.Taxable == false
select i;
// conditional where
if (reportType == "R")
lsFooterRow = lsFooterRow.Where(i => i.ReplacementCost > 0);
var aggregateFooterRow = from i in lsFooterRow
group i by new { i.ClaimId }
into grp
select new
{
SumOfReplValue = grp.Sum(i => i.Price),
SumOfACV = grp.Sum(i => i.ACV),
SumOfReplCost = grp.Sum(i => i.ReplacementCost)
};
That way you are filtering on the replacement cost before it gets aggregated, which sounds like what you want to do.
You did express concerns about a two part query but that shouldn't really pose a problem. The nice thing about this is it will not compose and execute the sql until you enumerate over the final version of the query. The entity framework engine is smart enough to simply add the second where as another condition in the final where statement in SQL. That means that your contitional where will neatly be part of the query rather than an afterthought. You can break up the query and add conditional things as much as you want.
The ability to break up the query into multiple pieces and conditionally compose the query is a tremendous benefit that LINQ has over SQL.
#Devin's answer is probably cleanest and makes an important pedant point about the delayed execution of linq and how 2 steps doesn't mean two queries. That being said, if you want to do it in one query, you could write the first query to include the extra condition like so:
var lsFooterRow = from i in _context.Inventory
where i.ClaimId == claimID
&& i.Taxable == false
&& (i.ReplacementCost > 0 || reportType != "R")
group i by new { i.ClaimId }
into grp
select new
{
SumOfReplValue = grp.Sum(i => i.Price),
SumOfACV = grp.Sum(i => i.ACV),
SumOfReplCost = grp.Sum(i => i.ReplacementCost)
};
EDIT:
Hmm, the only thing I can think of that would make this fail, and #Devin's work, is if you're changing the value of reportType between this declaration and where ever the actual enumeration of lsFooterRow is occurring. If this is happening, you can always just .ToList() it immediately. Or, less resource intensively, copy the reportType to a temporary variable that is never changed, and reference that within your query instead.
string _reportType = reportType //only set here, nowhere else
var lsFooterRow = from i in _context.Inventory
where i.ClaimId == claimID
&& i.Taxable == false
&& (i.ReplacementCost > 0 || _reportType != "R")
group i by new { i.ClaimId }
into grp
select new
{
SumOfReplValue = grp.Sum(i => i.Price),
SumOfACV = grp.Sum(i => i.ACV),
SumOfReplCost = grp.Sum(i => i.ReplacementCost)
};
But now it's no longer as clean, and captures the _reportType variable unnecessarily within the closure.
if you know what sql query needs to be executed at the DB...you can try http://www.linqpad.net/
which I feel is pretty handy when u work with linq...

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.

Complex Expressions in a LINQ Where Clause

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"));
});

Resources