Using Linq to replace foreach statement - c#

I'm a little bit confused by the explanations of linq online so I figured I'd make a thread.
I want to replace my foreach statements with linq statements as I'm sure doing little things like this will make me program better.
Example of what I'd like to use LINQ with:
public bool CheckForAccount(int accountID)
{
bool status = false;
foreach (AccountHolders accountHolder in AccountHoldersList)
{
if (accountHolder.AccountNumber == accountID)
{
status = true;
break;
}
}
return status;
}
Please can you provide an explanation of how it works too so I understand what you're doing.
Thanks in advance!

So probably the most succinct statement would be:
public bool CheckForAccount(int accountId)
{
return this.AccountHolderList.Any(x => x.AccountNumber == accountId);
}
In English this says
Check AccountHolderList for any cases where the AccountNumber property is equal to accountId. If there are any, return true, otherwise return false.

both
AccountHolderList.Any(x => x.AccountNumber == accountId);
and
AccountHoldersList.Exists(p => p.AccountNumber == accountId);
work equally well. Here is a more in-depth explanation of the differences:
Linq .Any VS .Exists - Whats the difference?
You could also use this:
var match = AccountHolderList.FirstOrDefault(x => x.AccountNumber == accountId);
return match != null ? true : false;
if you want to get a reference to the match as well. This works assuming accountId is unique

Related

Ternary operator add or not where conditions in linq

I have this query:
if(!string.IsNullOrEmpty(code) && !string.IsNullOrEmpty(name))
{
return this.context.Table.Where(
x => EF.Functions.Contains(x.Code, $"\"{code}\"")
&& EF.Functions.Contains(x.Name, $"\"{name}\""));
}
else if(string.IsNullOrEmpty(code) && !string.IsNullOrEmpty(name))
{
return this.context.Table.Where(x => EF.Functions.Contains(x.Name, $"\"{name}\""));
}
else
{
return this.context.Table.Where(x => EF.Functions.Contains(x.Code, $"\"{code}\""));
}
I tried to do it again using the ternary operators then check if the string is not null or empty and if so add the where clause or not
I tried such a thing but obviously expects that after the "?" there is the alternative of ":"
return this.context.Table.Where(
x => !string.IsNullOrEmpty(code)
? EF.Functions.Contains(x.Code, $"\"{code}\"")
&& !string.IsNullOrEmpty(name)
? EF.Functions.Contains(x.Name, $"\"{name}\""));
Since unlike the example in my case I have to check 8 different input parameters that if not passed must not be used in the where for the controls, I wanted to avoid filling the code of many if cases and rewriting the query n times for the different combinations, is there a way or should I resign myself?
You can just return true for any you don't want to check for like below
!string.IsNullOrEmpty(code) ? EF.Functions.Contains(x.Code, $"\"{code}\"") : true;
This means if the string is null or empty then it will return true which should provide the behaviour you're expecting.
Do not use ternary operators for combining query. EF will create non optimal SQL.
Usually such task is done in the following way:
var query = this.context.Table.AsQueryable();
if (!string.IsNullOrEmpty(code))
{
query = query.Where(
x => EF.Functions.Contains(x.Code, $"\"{code}\""));
}
if (!string.IsNullOrEmpty(name))
{
query = query.Where(
x => EF.Functions.Contains(x.Name, $"\"{name}\""));
}
var result = query.ToList();

Construct expression for where through lambdas

The situation
I have a method that takes in a POCO. This POCO is like the following
private class SearchCriteria
{
public string name get;set;
public string age get;set;
public string talent get;set;
..
....
}
The method basically has a query to the db , that uses the above criteria.
public void query(SearchCriteria crit)
{
if(crit.name!=null && crit.age!=null && crit.talent !=null)
{
dbContext.Students.Where(c=>c.name ==crit.name && c.age==crit.age...)
}
else if(crit.name !=null && crit.age!=null)
{
}
else if(....
{
}
As you can see there is a definite problem above , where in, in case of large number of criteria, I will have to write a lot of if-elses to drop out specific arguments from the where clause .
The possible solution ?
I am actually new to the lambda expressions world but I believe we must be having a facility which would allow us to do something like below.
dbContext.Students.Where(processCriteria(searchCriteriaPOCO)).
Can you folks lead me to the proper direction ?. Thanks
Get a queryable and then keep adding where clauses to it. That way you only need to test each possible criteria the once and also only generate the number of where clauses that are absolutely needed.
IQueryable<Student> q = dbContext.Students.AsQueryable();
if (crit.name != null)
q = q.Where(c => c.name == crit.name);
if (crit.age != null)
q = q.Where(c => c.age== crit.age);
Let me start by saying that this answer uses the same basic idea as #PhilWright's answer. It just wraps it up in an extension method that applies this pattern for you, and allows you to have a syntax that reads nice.
public static class SearchExtensions
{
public static IQueryable<Student> Query(this SearchCriteria criteria, IQueryable<Student> studentQuery)
{
return studentQuery
.Match(criteria.name, (student) => student.name == criteria.name)
.Match(criteria.age, (student) => student.age == criteria.age)
.Match(criteria.talent, (student) => student.talent == criteria.talent);
// add expressions for other fields if needed.
}
private static IQueryable<Student> Match<T>(
this IQueryable<Student> studentQuery,
T criterionValue,
Expression<Func<Student, bool>> whereClause) where T : class
{
// only use the expression if the criterion value is non-null.
return criterionValue == null ? studentQuery : studentQuery.Where(whereClause);
}
}
You can then use it in your code like this:
var criteria = new SearchCriteria() {
name = "Alex",
talent = "Nosepicking"
};
var results = criteria.Query(dbContext.Students);
Maybe I'm missing something, as the code example is not the clearest I've seen, but for your specific example, I would think the following should be fine:
dbContext.Students.Where(c => (crit.name == null || crit.name == c.name) &&
(crit.age == null || crit.age == c.age) &&
(crit.talent == null || crit.talent == c.talent));
No need to chain a bunch of if statements.
For more complicated scenarios, you might prefer something like PredicateBuilder
You can use a pattern like this:
dbContext.Students.Where(c=>(crit.name == null || c.name ==crit.name) && ...)
A search criterion which is null will give a subexpression which is always true.

FindAll multiple conditions

So I'm trying to use FindAll to return a list of objects that match. It works great when I only use one condition, for example
patientstatus = statuslist.FindAll(delegate(StatusReader.onestatus p1)
{
return p1.WL_ID == patlist[i].wl_id;
});
But I would like to use multiple conditions, some of which contain if statements and I can't figure out how. It seems like the format needs to have a return statement, and the example from Microsoft (http://msdn.microsoft.com/en-us/library/fh1w7y8z.aspx) only uses one condition.
I could either have multiple FindAll methods for each condition, or just make a loop that scans through all the values myself, but there's got to be an obvious thing I'm missing, right?
I'm not quite sure what "if loops" are, but you can always just stitch them together:
patientstatus = statuslist.FindAll(delegate(StatusReader.onestatus p1)
{
if(p1.WL_ID != patlist[i].wl_id)
return false;
if(otherStuff != 5)
return false;
for(var x in p1.stuff)
if(x == 7)
return false;
return true;
});
Try the following:
var patientStatus = statusList.Where(p => p
{
// Put in logic here as you need
return p.WL_ID == patlist[i].wl_id || p.YourSecondProperty == WhateverYouWantToCheck;
}
You can think about something like
public abstract class Condition
{
public abstract bool Sutisfied(StatusReader.onestatus status);
}
public class Condition1 : Condition
{
public override bool Sutisfied(StatusReader.onestatus status) {
//check for something and return
}
}
public class Condition2 : Condition
{
public override bool Sutisfied(StatusReader.onestatus status) {
//check for something and return
}
}
After can have a list of conditions, like
List<Condition> conditions =
new List<Condition>{new Conditon1(), new Condition2()}
and after this list use inside
patientstatus = statuslist.FindAll(delegate(StatusReader.onestatus status)
{
return conditions.TrueForAll(c=>c.Sutisfied(status));
});
Your delegate simply needs to return true for a match to your conditions and false for a non-match to your conditions. It doesn't have to be a single line return statement. You can create boolean values, have if statements, for loops, and anything else you want in your delegate - so long as it returns true or false along all code paths.
So you can do as many if statements or loops as you want.
If you really want to maintain it as one line, you can do something like the following...
return (condition1 == condition1) || (condition2 == condition2) || (condition3 == condition3);

C#/LINQ: Trying to optimize performance

This is my setup
class EditorTabViewModel : TabViewModel {
...
public bool CanSave { get; set; };
}
ObservableCollection<TabViewModel> _tabs
I want to check if there are any tabs in _tabs that are EditorTabViewModel that has property CanSave set to true
i did something like ...
var tabs = from t in _tabs
where t is EditorTabViewModel
&& ((EditorTabViewModel)t).CanSave == true
select t;
if (tabs.Count() > 0)
return true;
else
return false;
I wonder if there is a better way to do this? maybe i won't need to retrieve all tabs, or maybe I just need to query the count or something?
How about:
return _tabs.OfType<EditorTabViewModel>().Any(t => t.CanSave);
Here:
OfType<> is a non-buffering filter that restricts us to EditorTabViewModel
Any is short-circuiting, so returns true as soon as a match is found
Yes, you can improve. Something like this would probably work:
return _tabs.Any(x => x is EditorTabViewModel && ((EditorTabViewModel)x).CanSave);
Using the linq extensions you could write something like
_tabs.Any( p => p is EditorTabViewModel && ((EditorTabViewModel)t).CanSave)
Try something like:
return _tabs.FirstOrDefault(y => y is EditorTabViewModel && ((EditorViewModel)t).CanSave) != null;

what LINQ query will return results based on partial matches against arbitrary number of an entity's fields?

consider the following code, which represents an attempt to implement partial matching. the intended result is a row for any 1 or more fields that match between the query entity and the data store.
so if you supply person.email we want a match against that, if you supply person.email and person.FirstName we should filter the results further, and so on.
var results = from p in db.Persons
where p.CrookBookUserName.Trim().Contains(person.CrookBookUserName.Trim()) ||
p.email.Trim().Contains(person.email.Trim()) ||
p.FirstName.Trim().Contains(person.FirstName.Trim()) ||
p.LastName.Trim().Contains(person.LastName.Trim()) ||
p.phone.Trim().Contains(person.phone.Trim())
select p;
return results;
unfortunately, this code always returns all rows in the db. why, and what should be the fix?
thanks in advance.
Does person have all fields populated? A p.email.Trim().Contains("") from an empty email address will return true for everything.
An extra (!String.IsNullOrEmpty(..)) && before each parameter, while verbose, will fix it if this is the problem.
Edit re comments:
Yeah, a helper method or something could help here... something like
public static bool ContainsIfNotEmptyTrimmed(this string source, string param)
{
return !String.IsNullOrEmpty(param.Trim()) && source.Trim().Contains(param.Trim());
}
where p.CrookBookUserName.ContainsIfNotEmptyTrimmed(person.CrookBookUserName) || ...
You need to check if the user is providing empty strings.
var query = db.Persons;
if (!string.IsNullOrEmpty(person.CrookBookUserName.Trim()) {
query = query.Where(p => p.CrookBookUserName.Trim().Contains(person.CrookBookUserName.Trim()));
}
if (!string.IsNullOrEmpty(person.email.Trim()) {
query = query.Where(p => p.email.Trim().Contains(person.email.Trim()));
}
// etc...
return query;

Categories