I am using c#.net
I have two textboxes which if !empty need to be part of a WHERE clause within a LINQ query.
Here is my code
var result = from a in xxxx select a;
if(!string.IsNullOrEmpty(personName))
{
return result.Where(a >= a.forename.Contains(personName) || a.surname.Contains(personName)
}
else if(!string.IsNullOrEmpty(dateFrom))
{
return result.Where(a >= a.appStartDateTime >= dateFrom.Date)
}
else if(!string.IsNullOrEmpty(personName) && !string.IsNullOrEmpty(dateFrom))
{
return result.Where(a >= a.forename.Contains(personName) || a.surname.Contains(personName) && a.appStartDateTime >= dateFrom.Date);
}
I thought this would work but it doesn't like the .Where and I cant access the 'a' for example a.forename (The name 'a' does not exist in the current context)
What am I going wrong, or can this not actually be done?
Thanks in advance for any help.
Clare
Instead of this:
result.Where(a.forename.Contains(personName))
Try this:
result.Where(a => a.forename.Contains(personName))
You appear to be missing the Lambda operator (=>).
try this
var result = from a in xxxx select a
where (string.IsNullOrEmpty(personName) || a.forename.Contains(personName)
|| a.surname.Contains(personName))
&& (string.IsNullOrEmpty(dateFrom)
|| a.appStartDateTime >= DateTime.Parse(dateFrom).Date);
dateFrom appears to be a string so you have to parse it to get a date time.
This logic should work but I have not tested it. I could be wrong.
You seem to only care if the forename or surname contain personName if the personName is not null or empty. So you can rewrite it to return things if the personName is null or empty or the fore or sur name contains person name. Since the || operator is short circuiting it will not check Contains if the personName is null or empty.
You can also combine the predicates and make the logic shorter and easier to read:
var result = from a in xxxx select a;
if (!string.IsNullOrEmpty(personName))
result = result.Where(a => a.forename.Contains(personName) || a.surname.Contains(personName)
if (!string.IsNullOrEmpty(dateFrom))
result = result.Where(a >= a.appStartDateTime >= dateFrom.Date)
return result;
This method of combining predicates works well with 'AND' conditions. If you need to 'OR' conditions, it's a little bit more involved. Thankfully, Joe Albahari has created PredicateBuilder (as part of LINQKit) that helps with this greatly.
Hope this helps.
..or with just one exit point:
var result = from a in xxxx select a;
Func<string, bool> func = null;
if(!string.IsNullOrEmpty(personName))
{
func = (a) => {a.forename.Contains(personName) || a.surname.Contains(personName)};
}
else if(!string.IsNullOrEmpty(dateFrom))
{
func = (a) => {a.appStartDateTime >= dateFrom.Date};
}
else if(!string.IsNullOrEmpty(personName) && !string.IsNullOrEmpty(dateFrom))
{
func = (a) => {a.forename.Contains(personName) || a.surname.Contains(personName) && a.appStartDateTime >= dateFrom.Date;};
}
return result.Where(func);
Related
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();
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.
I'm working right now to build LINQ up to the side where it needs to retrieve some information from the user about how many applications as you have got to be friends. - user exists in tablen.
venner Numberoffriends = db.venners.Where(UseridFriends => UseridFriends.To == 1 && UseridFriends.Godkendt == 0).Count();
if(Numberoffriends != null)
{
//true
}
it is such that it now makes mistakes here
db.venners.Where(UseridFriends => UseridFriends.To == 1 && UseridFriends.Godkendt == 0).Count();
ERROR ARE: The type 'int' can not be implicitly converted to 'LinqData.venner'
The error is because the return type of the Count() extension method is int, and you are trying to assign the return value to a LinqData.venner.
There is no implicit conversion from int to LinqData.venner, so that is the source of your error.
You can confirm this by refactoring your code into the following equivalent code:
int count = db.venners.Where(UseridFriends => UseridFriends.To == 1 && UseridFriends.Godkendt == 0).Count();
venner Numberoffriends = count;
if ( Numberoffriends != null )
{
//true
}
which will transfer your compilation error to the
venner Numberoffriends = count;
line.
It seems from your code that instead of having a count (using the Count() method), you want something like FirstOrDefault() or ToList(). This will enable you to do something with the venner object(s) you get.
I think the following would build, but I'm not sure about your intention and hence if it's what you want:
List<venner> venners = db.venners
.Where(UseridFriends => UseridFriends.To == 1 && UseridFriends.Godkendt == 0)
.ToList();
if ( venners.Count != 0 )
{
//true
// do something with venners here
}
What are you trying to do with your linq?
In my asp.net application i am using linq. I need a help what is the syntax for if-elseif-else using linq in single line.
genericReportList =
(from CD in list
select new GENERICREPORT
{
CITATIONNO = CD.CITATIONNO,
DATE = CD.DATE,
LOCATION = CD.LOCATION,
//STATUS = CD.STATUS,
PLATENO = Utilities.DecryptData(CD.PLATENO),
PSOURCE = CD.PSOURCE,
MAKE = CD.MAKE,
ID = Utilities.DecryptData(CD.ID),
NATIONALITY = CD.NATIONALITY,
SOURCE = CD.SOURCE,
NAME = Utilities.DecryptData(CD.NAME),
VIOLATION = CD.VIOLATION,
STATUS = CD.STATUS == short.Parse("1") ? "Complete" : "Incomplete"
}).ToList();
If STATUS = CD.STATUS == short.Parse("1") ? "Complete" : and 2 for "Incomplete" and 3 for "Void"
I don't understand why you are doing short.Parse("1"). This will always be 1. If you want multiple if-else in a one-liner, combine ternary operators:
STATUS = CD.STATUS == 1 ? "Complete" : CD.STATUS == 2 ? "Incomplete" : "Void"
If this is going to be used in the context of Entity Framework (or other ORM with IQueryable support), it will translate to a CASE WHEN SQL statement.
What I understand from your question if I'm not wrong, is that you might be asking about where clause.
if yes then you can always use multiple where in your query.
example :
Collection.Where(x => x.Age == 10)
.Where(x => x.Name == "Fido")
.Where(x => x.Fat == true)
more about LINQ query
http://msdn.microsoft.com/en-us/library/gg509017.aspx
You can just continue with what you already have.
STATUS = CD.STATUS == 1 ? "Complete" : (CD.STATUS == 2 ? "Incomplete" : "InProgress")
There nothing wrong with writing your own method and using it in a LINQ query. It will be far more understandable and readable than a long conditional operator.
Consider using something like:
private String GetStatus(int value)
{
if (value == 1)
return "Complete";
if (value == 2)
return "Incomplete";
if (value == 3)
return "Void";
}
Then you would call it like:
STATUS = GetStatus(CD.STATUS)
I have the following method:
public IQueryable<Profile> FindAllProfiles(string CountryFrom, string CountryLoc)
{
return db.Profiles.Where(p => p.CountryFrom.CountryName.Equals(CountryFrom,
StringComparison.OrdinalIgnoreCase));
}
What is the best way to write the where clause that would filter all the possible combinations of input parameters in one statement:
BOTH CountryFrom and CountryLoc = null
Only CountryFrom null
Only CountryLoc null
BOTH CountryFrom and CountryLoc are not null.
Soon .. I would need to filter out profiles by Age, Gender, Profession .. you name it.
I am trying to find a way to write it efficiently in C#. I know how to do it in a clean manner in TSQL. I wish I knew the way. Thanks for all the responses so far.
A good old binary XNOR operation will do the trick here:
db.Profiles.Where(p => !(p.CountryFrom == null ^ p.CountryTo == null))
It's effectively equating two booleans, though to me it's more direct, less convoluted even, than writing ((p.CountryFrom == null) == (p.CountryTo == null))!
I would use this simple LINQ syntax...
BOTH CountryFrom and CountryLoc = null
var result = from db.Profiles select p
where (p.CountryFrom == null) && (p.CountryLoc == null)
select p
Only CountryFrom null
var result = from db.Profiles select p
where (p.CountryFrom == null) && (p.CountryLoc != null)
select p
Only CountryLoc null
var result = from db.Profiles select p
where (p.CountryFrom != null) && (p.CountryLoc == null)
select p
BOTH CountryFrom and CountryLoc are not null.
var result = from db.Profiles select p
where (p.CountryFrom != null) && (p.CountryLoc != null)
select p
Hope it helps ;-)
I wouldn't call this elegant:
public IQueryable<Profile> FindAllProfiles(string CountryFrom, string CountryLoc)
{
return db.Profiles.Where(p =>
{
p.ContryFrom != null &&
p.CountryFrom.CountryName != null &&
p.CountryFrom.CountryName.Equals(CountryFrom, StringComparison.OrdinalIgnoreCase)
});
}
I may be missing something, but as written, your combination of operators will either let all values through or no values through depending on whether you use || or && to combine them together.
I'm in favor of not trying to cram too much logic into a linq expression. Why not contain your comparison logic in a separate function like this?
EDIT: I provided an example implementation of the MatchesCountry function.
class Example
{
public IQueryable<Profile> FindAllProfiles(string CountryFrom, string CountryLoc)
{
return db.Profiles.Where(p => p.MatchesCountry(CountryFrom, CountryLoc));
}
}
public static class ProfileExtensions
{
public static bool MatchesCountry(this Profile profile, string CountryFrom, string CountryLoc)
{
// NOTE: Your comparison logic goes here. Below is an example implementation
// if the CountryFrom parameter was specified and matches the profile's CountryName property
if(!string.IsNullOrEmpty(CountryFrom) && string.Equals(profile.CountryName, CountryFrom, StringComparison.OrdinalIgnoreCase))
return true; // then a match is found
// if the CountryLoc parameter was specified and matches the profile's CountryCode property
if (!string.IsNullOrEmpty(CountryLoc) && string.Equals(profile.CountryCode, CountryLoc, StringComparison.OrdinalIgnoreCase))
return true; // then a match is found
// otherwise, no match was found
return false;
}
}