Linq with null parameters not working - c#

I don't understand why this query is not working, can you please help me?
public static IEnumerable<SelectListItem> MyList(int? id, string name="")
{
var list =db.Entity
.Where(p=>
(name==null? p.Name !=null : p.Name==name) &&
(id.hasValue || p.Id==id)
.Select(n=>new SelectListItem()
{
Text=n.Name,
Value=n.Id.ToString()
}).ToList();
return list;
}
I want to have the full list when both parameters are null!! but I get an empty list when both parameters are null.
The snippet code is from a big method which contain several query like this one.

If I understand you correctly you do not want to perform filtering when the value is null. Then you should write:
.Where(p=>
(name == null || p.Name == name) &&
(id == null || p.Id == id)
And you should change the signature of the function to set the default value for parameter name to null rather than empty string.
public static IEnumerable<SelectListItem> MyList(int? id, string name = null)

Per the comments, there are two problems:
Firstly, your condition for id isn't quite right. Secondly, your default argument for name is an empty string, not null.
However, there is room for more improvements: by embedding your name == null (and same for id) in the query, your query will be constructed in a way that translates the null check to SQL. That's good if there's a chance of name changing its value after the query has constructed, but there's no chance of that happening here. You can instead dynamically construct your query:
I commented out the p.Name != null check. If your names are possibly null, then according to the question, you don't want them filtered out when name == null.
public static IEnumerable<SelectListItem> MyList(int? id, string name=null)
{
IQueryable<Entity> query = db.Entity;
if (id != null)
query = query.Where(p => p.Id == id);
if (name != null)
query = query.Where(p => p.Name == name);
//else
// query = query.Where(p => p.Name != null);
return query.Select(...).ToList();
}

Related

LINQ query with variable that may or maynot be null does not return results when null

I have a LINQ query with a WHERE clause that has a variable that sometimes will be NULL. When this variable is NULL I can not get it to pull any results even though there are results to be taken. I found Linq where column == (null reference) not the same as column == null and tried the solutions provided there with no success. What am I doing wrong?
public async Task<List<SectionNavigationMenuDTO>> GetSectionNavigationMenu(int? SectionID, bool IsAdmin = false)
{
return await _siteDbContext.SectionNavigationMenuItems
//.Where(snmi => snmi.SectionID == SectionID && snmi.IsAdminOnly == IsAdmin) //No results at all
//.Where(snmi => object.Equals(snmi.SectionID, SectionID)) //No results at all
//.Where(snmi => (snmi.SectionID == SectionID || (SectionID == null && snmi.SectionID == null))) //No results at all
//.Where(snmi => snmi.IsAdminOnly == IsAdmin) //Returns 3 results
.OrderBy(snmi => snmi.Name)
.Select(sni => new SectionNavigationMenuDTO()
{
Name = sni.Name,
URL = sni.URL
})
.ToListAsync();
}
Edit:
The SectionID should be either filled with a int or null and be valid in both cases. If the SectionID variable is NULL then it should pass NULL as the argument in for the LINQ query. The database does contain entries with NULL for the SectionID and the query SELECT * FROM dbo.SectionNavigationMenuItems WHERE SectionID IS NULL does return 3 results as expected.
The top commented WHERE clause is what I want to happen (with the two comparisons) the other 3 are what I have tried to get this to work and the results.
Try this:
public async Task<List<SectionNavigationMenuDTO>> GetSectionNavigationMenu(int? SectionID, bool IsAdmin = false)
{
return await _siteDbContext.SectionNavigationMenuItems
.Where(snmi => snmi.IsAdminOnly == IsAdmin && (SectionID==null || SectionID==smi.SectionID ) )
.OrderBy(snmi => snmi.Name)
.Select(sni => new SectionNavigationMenuDTO()
{
Name = sni.Name,
URL = sni.URL
})
.ToListAsync();
}

LINQ and optional parameters

I'm designing a web service to be consumed by an MVC app (pretty simple stuff), but I need a single method in the web service that takes up to four optional parameters (i.e. catId, brandId, lowestPrice and highestPrice).
How do I write the Linq query so that it essentially executes
databaseObject.Products.Where(p=> (p.Category == ANY if catId==null, else catId))
I hope that makes sense.
The parameters of the method could accept null values and the Where restriction could be evaluated for each not null parameter:
IQueryable<Product> q = databaseObject.Products;
if (catId != null)
{
q = q.Where(p => p.Category == catId);
}
if (brandId != null)
{
q = q.Where(p => p.Brand == brandId);
}
// etc. the other parameters
var result = q.ToList();
If this is Linq To SQL:
databaseObject.Products.Where(p=> (catId == null || p.Category == catId) );
Linq To SQL, efficiently writes the SQL sent to backend without a where clause if CatId is null. You can have multiple of these constructs, and only those with a nonNull value is included in the where construct.
databaseObject.Products.Where(p=> ((catId==null) || (p.Category == catId)))
For your other 3 optional parameters, you can AND them in, doing your entire search in one linq statement.
Something along the lines of the following should do the trick:
IEnumerable<Product> GetProducts(int? categoryId)
{
var result = databaseObject.Products.Where(product => ProductCategoryPredicate(product, categoryId)
}
/// <summary>
/// Returns true if the passed in categoryId is NULL
/// OR if it's not null, if the passed in categoryId matches that
/// of the passed in product
///
/// This method will get called once for each product returned from
/// databaseObject.Products</summary>
bool ProductCategoryPredicate(Product product, int? categoryId)
{
if (categoryId.HasValue)
{
return product.Category == categoryId.Value;
}
else
{
return true;
}
}
This could/can be simplified into a single line LINQ statement (see below) but I've written it long-hand above for clarity:
IEnumerable<Product> GetProducts(int? categoryId)
{
var result = databaseObject.Products.Where(product => !categoryId.HasValue || product.Category == categoryId.Value);
}

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.

Linq Query Crash if Return Null

I run this query And if The Query is return with empty values the programme is crashed.
var cust = db.Customers.FirstOrDefault(x => x.telephone == txtTel.Text);
if (cust.BlackList == 1)
{
MessageBox.Show("This customer is blacklisted, Do you wish to continue with this job?");
}
Please Suggest me Some Efficient solution
Thanks.
You are getting a null pointer because FirstOrDefault returns the default value of the object if the result is not found (in this case it is null):
var cust = db.Customers.FirstOrDefault(x => x.telephone == txtTel.Text);
if (cust != null && cust.BlackList == 1)
{
MessageBox.Show("This customer is blacklisted, Do you wish to continue with this job?");
}
You need to check for null because that's what FirstOrDefault returns if there is no record that satisfies your condition:
if(cust != null && cust.BlackList == 1)
FirstOrDefault will return a default value if there is no element in the list which satisfy the condition, in this case it will be null. As you call a property on the null value it will naturally cause an exception.
You should check if cust is null, like:
if(cust != null && cust.BlackList == 1)
Of course you can display another message if the user doesn't exist based on the logic of your application.

c# dealing with all possible null and non null values

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

Categories