Lambda expression like Sql where value or null - c#

I'm coding a search for an MVC app we're building, the thing is I would like to search by various properties of an object. In this particular case this is my expected behavior:
If both parameters are null or empty, return all.
If any parameter has a value, select all filtered by that parameter using Contains.
This is what I'm doing:
var model = _svc.GetList(q => q.Name == (string.IsNullOrEmpty(entity.Name) ? q.Name : entity.Name) &&
q.Description == (string.IsNullOrEmpty(entity.Description) ? q.Description : entity.Description));
This returns either all elements if both fields are null or empty, or any element that matches exactly the Name AND/OR Description.
The thing here is I would like this to behave as a Contains.
I've managed to get this working with one field:
var model = _svc.GetList(q => (string.IsNullOrEmpty(entity.Name) ||
q.Name.ToLower().Contains(entity.Name.ToLower()))).ToList();
But when I add the Description field, it throws a NullReferenceException: Object reference not set to an instance of an object.
Just to check I've tried this and it didn't work either:
var model = (from q in _svc.GetList()
where (string.IsNullOrEmpty(module.Name) ||
q.Name.ToLower().Contains(module.Name.ToLower()))
select q).ToList();
model = (from q in model
where (string.IsNullOrEmpty(module.Description) ||
q.Description.ToLower().Contains(module.Description.ToLower()))
select q).ToList();

Well, for a multi-optional-criteria search, you can do (asserting "module" is your "search class").
Simple, and (I think) more readable. Add a null check for the description and entity property of your model.
//take all elements
var model = _svc.GetList();
if (!string.IsNullOrEmpty(module.Description))
model = model.Where(m =>
m.Description != null &&
m.Description.ToLower().Contains(module.Description.ToLower());
if (!string.IsNullOrEmpty(module.Name))
model = model.Where(m =>
m.Name != null &&
m.Name.ToLower().Contains(module.Name.ToLower());
return module.ToList();
Null check, because a ToLower() on a null will raise a NRE !

This is a little ugly but should do the trick, you are getting the null reference because of entries with empty Description. If what you are doing with the Name is any clue, you are probably doing something like this q.Description.ToLower().Contains(..) without checking that q.Description is not null
var model = _svc.GetList(q =>
(string.IsNullOrEmpty(entity.Name) || string.IsNullOrEmpty(q.Name)
|| q.Name.ToLower().Contains(entity.Name.ToLower()))
&& (string.IsNullOrEmpty(entity. Description) || string.IsNullOrEmpty(q. Description)
|| q.Description.ToLower().Contains(entity. Description.ToLower())))

Related

Filter a list of class with element which starts with specific letters

I have a list of a class object, I need to filter that list with element which starts with these letters "GHB" and then set a listview control dataconext to it to display the elements
if(myList.ToList().FindIndex(x=> x.Name !=null)!=-1 )
{
listview1.DataContext = myList.ToList().where(x=> x.Name.StarstWith("GHB"))
}
But it gives me an error when an element is null
It gives you the error because your if condition is actually useless. You check whether Name in at least one element is not null, if so you try to access the variable. This of course will fail, because you need only 1 element with a valid name and the rest still can have null values which will lead to the NullReferenceException
What you can do is: check in the where clause additionaly whether Name is not null and if so only then check whether it StartsWith("GHB"):
listview1.DataContext = myList.Where(x => x?.Name != null && x.Name.StartsWith("GHB")).ToList();
this way you can save yourself the if condition.
I guess what you where trying to check is if Name in all elements is not null. In this case you can use the All method:
if (myList.All(x=>x.Name != null)
EDIT: using the ? will avoid that Name is checked if an element in the List is entirely null:
myList.Where(x => x?.Name != null && x.Name.StartsWith("GHB")).ToList();
Try this:
listview1.DataContext = myList
.Where(x => x != null
&& !string.IsNullOrEmpty(x.Name)
&& x.Name.StarstWith("GHB"))
.ToList();
...and remove the if statement.

LINQ conditional where only when prop is not null

I have this line:
products = products.Where(p => p.Brand.Equals(s) ||
p.CatCodeNaam.Equals(s) ||
p.SubCategoryName.Equals(s)).ToList();
Which is fine, until there is no SubCategoryName.
So I tried:
var t = products.Where(p => p.Brand.Equals(s) ||
p.CatCodeNaam.Equals(s))
.Where(p => !string.IsNullOrWhiteSpace(p.SubCategoryName) && p.SubCategoryName.Equals(s));
But if there is no SubCategoryName, it should just ignore it and do not filter so no empty collection.
I know that I can fix it by having the database returning an empty string isnull(SubCategoryName,'') as SubCategoryName, but I do not want that. I am looking for a LINQ fix.
Some test data to explain my issue:
I have an array with keywords like:
"car", "ford", "focus"
And I iterate over the keyword array with a foreach.
And since I do not know if the keyword is a brand, category or subcategory I have to check if it is one of them. But there are objects in the dataset products who have a null value in subcategory causing the first line to crash.
Reverse the condition:
s.Equals(p.SubCategoryName)
That way it won't matter if p.SubCategoryName is null.
I am sorry if I didn't understood properly, but wouldn't it work this way:
products = products.Where(p => p.Brand.Equals(s) ||
p.CatCodeNaam.Equals(s) ||
p.SubCategoryName == null ||
p.SubCategoryName.Equals(s)).ToList();

Check system.nullreferenceexception

My code as follows:
#{var UName = ((IEnumerable<Pollidut.ViewModels.ComboItem>)ViewBag.UnionList).FirstOrDefault(x => x.ID == item.UNION_NAME_ID).Name;<text>#UName</text>
if ViewBag.UnionList is empty then it troughs system.nullreferenceexception.How to check and validate this?
Well, you're calling FirstOrDefault - that returns null (or rather, the default value for the element type) if the sequence is empty. So you can detect that with a separate statement:
#{var sequence = (IEnumerable<Pollidut.ViewModels.ComboItem>)ViewBag.UnionList;
var first = sequence.FirstOrDefault(x => x.ID == item.UNION_NAME_ID);
var name = first == null ? "Some default name" : first.Name; }
<text>#UName</text>
In C# 6 it's easier using the null conditional operator, e.g.
var name = first?.Name ?? "Some default name";
(There's a slight difference here - if Name returns null, in the latter code you'd end up with the default name; in the former code you wouldn't.)
First of all, you should not be doing this kind of work in the View. It belongs in the Controller. So the cshtml should simply be:
<text>#ViewBag.UName</text>
And in the controller, use something like:
var tempUnion = UnionList.FirstOrDefault(x => x.ID == item.UNION_NAME_ID);
ViewBag.UName = tempUnion == null ? "" : tempUnion.Name;

Null object in Func

I have a filter as below:
filter = project => project.Plan.ProgressStatus == progressStatus;
I am creating a filter based on progressStatus that is passed into method.
Then I pass this filter to a where operator.
var projects = _projectService.Where(filter);
I get back NRE since Plan is null.
How can I safely query objects whose Plan.ProgressStatus is equal to what i pass in as parameter?
Make your filter check if it is null:
filter = project => project.Plan != null && project.Plan.ProgressStatus == progressStatus;
If _projectService possibly contains null then add that check as well:
filter = project => project != null
&& project.Plan != null
&& project.Plan.ProgressStatus == progressStatus;
Add a null check to the project and/or project.Plan objects in your func.
filter = project =>
{
if(project == null || project.Plan == null)
return false;
return project.Plan.ProgressStatus == progressStatus;
};

Only do Where condition if a value is passed in

I have the following LINQ statement that does on where on the date and a LabID.
I'm passing in a list of LABS and a date, however they are not required, and I could potentially only pass in a date, and no lab, in which case I'd like to get results for all labs for that particular lab.
here is what I have now:
List<dExp> lstDatExp = (from l in ctx.dExp.Include("datLab")
where values.Contains(l.datL.Lab_ID)
&& l.reportingPeriod == reportingPeriod
select l).ToList<dExp>();
But this breaks if the value getting passed in is not there. How do I change this to make sure both of my where statements are optional?
With IQueryable you can simply add conditions in steps:
int? reportingPeriod = ...;
IQueryable<dExp> resultsQuery = // don't use `var` here.
ctx.dExp.Include("datLab");
if (values != null)
resultsQuery = resultsQuery.Where(exp => values.Contains(exp.datL.Lab_ID));
if (reportingPeriod.Hasvalue)
resultsQuery = resultsQuery.Where(exp => exp.reportingPeriod == reportingPeriod.Value);
// additional .Where(), .OrderBy(), .Take(), .Skip() and .Select()
// The SQL query is made and executed on the line below
// inspect the string value in the debugger
List<dExp> results = resultsQuery.ToList();
Here are two ways to do that.
But first, please don't use a single lowercase l as an identifier. It is way too easy to confuse it with the number 1. More generally, stp using abbrevs in yr cde, it mks it hrdr to rd.
First technique:
var query = from lab in ctx.dExp.Include("datLab")
where values == null || values.Contains(lab.datL.Lab_ID)
where reportingPeriod == null || lab.reportingPeriod == reportingPeriod
select lab;
var list = query.ToList<dExp>();
Second technique:
IEnumerable<dExp> query = ctx.dExp.Include("datLab");
if (values != null)
query = query.Where(lab=>values.Contains(lab.datL.Lab_ID));
if (reportingPeriod != null)
query = query.Where(lab=>lab.reportingPeriod == reportingPeriod);
var list = query.ToList<dExp>();
What we do is something like (l.reportingPeriod == reportingPeriod || reportingPeriod == null) So you check to see if the parameter is its default meaning it hasnt been used or if there is something there check it against the database.
You need to check if your values are null before doing the query, and if they are, don't do the extra condition.
List<dExp> lstDatExp =
(from l in ctx.dExp.Include("datLab")
where
(values == null || values.Contains(l.datL.Lab_ID)) &&
(reportingPeriod == null || l.reportingPeriod == reportingPeriod)
select l).ToList<dExp>();
This way if values or reportingPeriod are null they are essentially optional.

Categories

Resources