LINQ conditional where only when prop is not null - c#

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();

Related

Can anybody tell me why this linq query won't work?

Sorry if I'm missing something really obvious, I've been coding for a lot of hours in a row and my brain is crawling to a halt. I have the following statement:
var hcn = "";
var forename = "";
var surname = "";
foreach (var casenoteResult in casenoteResults)
{
personResults.AddRange(_ctx.People.Where
(x => x.PAS_INT_NO == casenoteResult.PAS_INT_NO
&& x.CSA_NO.Contains(hcn)
&& x.FORENAMES.ToLower().Contains(forename.ToLower())
&& x.SURNAME.ToLower().Contains(surname.ToLower()))
.ToList());
}
And I get no result. The only thing I'm really looking for is the casenote. Yet if I comment out each of the '&&'s, so I'm left with this:
foreach (var casenoteResult in casenoteResults)
{
personResults.AddRange(_ctx.People.Where
(x => x.PAS_INT_NO == casenoteResult.PAS_INT_NO)
.ToList());
}
I get 1 result, which is what I'm expected.
Can anyone help me? Why does the first statement not return this 1 result? Could it be that some of the fields that I'm comparing the empty strings to are null? The one record that gets found doesn't have any nulls in it. I'm lost here. Please help my poor battered brain!
If I were you, I would re-write this code like below. It's safer to build the queryable in parts to make sure you have a good handle on which values you are actually passing in to the query. The reason why you are not getting any rows is probably because the query values going in to the query is not what you think they are or your database doesn't treat empty string as a wildcard. (Because based on what you posted, you are checking if a string contains an empty string which is always true in C# but may not be true for your database provider).
var queryable = _ctx.People.Where(w => caseNoteResults.Select(s => s.PAS_INT_NO).Contains(w.PAS_INT_NO));
queryable = string.IsNullOrEmpty(hcn) ? queryable : queryable.Where(w => w.CSA_NO.Contains(hcn, StringComparison.InvariantCulture));
queryable = string.IsNullOrEmpty(forename) ? queryable : queryable.Where(w => w.FORENAMES.Contains(forename, StringComparison.InvariantCultureIgnoreCase));
queryable = string.IsNullOrEmpty(surname) ? queryable : queryable.Where(w => w.SURNAME.Contains(surname, StringComparison.InvariantCultureIgnoreCase));
personResults.AddRange(queryable.ToList());
The idea is, if your hcn, forename and surname are empty, no point in checking them.
Also, make sure that you handle nulls safely if each of these fields are nullable.

c# linq crm select where contains

I'm trying to select from CRM entity with "contains" key.
I tryed this:
var results = crm.new_supplycontractSet
.Where(x => x.new_city != null &&
x.new_city.Name.Contains("myChars"))
.ToList();
but it give me this error:
Invalid 'where' condition. An entity member is invoking an invalid property or method.
and this:
var result = (
from c in crm.new_supplycontractSet
from a in crm.new_comuneSet
where a.new_name.Contains(comune)
where c.new_city.Id == a.Id
select c)
.ToList();
But i can't figure out how to do it. The second try gives me this error:
A 'SelectMany' operation must be preceeded by a 'Where' operation that filters by an entity ID.
How can I select by a contains filter? "x.new_city" is an entity ref from crm.new_comuneSet.
PS:
I've just read something about the inaccessibility of the "entity.entityRef.Name.Contains()" because the "Name" property is not ground level and so it's not available for the ".contains" check.
At the end i got it. instead of contains clause, I had to install SqlClient and use the following:
results = (from x in crm.new_supplycontractSet
where x.new_city != null
where x.new_address != null
where SqlMethods.Like(x.new_city.Name, "city")
where SqlMethods.Like(x.new_address.Name, "street")
select ....).ToList();
hope this will help someone else :)
you can try converting your field and your filter to lower or upper case.
var results = crm.new_supplycontractSet
.Where(x => x.new_city != null &&
x.new_city.Name.ToLower().Contains(("myChars").ToLower()))
.ToList();
I think is better if you use StartsWith instead (and if is possible).

Sitecore: efficient way to use LINQ to compare against an ID

I have a LINQ query retrieving a list of , such as this:
var results = SearchContext.GetQueryable<Person>()
.Where(i => i.Enabled)
.Where(i => i.TemplateName == "Person")
.Random(6);
Each object of type "Person" has a "Location" field which is also a Glass mapped item, and hence has an ID; I would like to only select items whose Location has a specific ID.
How can I go about doing this in an efficient manner?
EDIT: I should probably clarify that I am unable to perform this comparison, efficiently or not. Because the GUID is an object and I cannot perform ToString in a LINQ query, I am unable to only pick the items whose Location item has a specific ID. Any clues on how this could be achieved?
EDIT 2: Adding the clause
.Where(i => i.Location.Id == this.Id)
Doesn't work, for... some reason, as I'm unable to debug what LINQ "sees". If I convert the other ID I'm comparing it against to string this way:
var theOtherID = this.Id.ToString("N");
Then it works with this LINQ line:
.Where(i => i["Location"].Contains(theOtherID))
I still have no idea why.
One approach is to include a separate property on Person that is ignored by Glass mapper, but can be used in searches:
[SitecoreIgnore]
[Sitecore.ContentSearch.IndexField("location")]
public Sitecore.Data.ID LocationID { get; set; }
You can use this in your search as follows:
Sitecore.Data.ID locationId = Sitecore.Data.ID.Parse(stringOrGuid);
var results = SearchContext.GetQueryable<Person>()
.Where(i => i.Enabled)
.Where(i => i.TemplateName == "Person")
.Where(i => i.LocationID == locationId)
.Random(6);
I think the efficiency of using multiple where clauses vs. conditionals is debatable. They will likely result in the same Lucene query being performed. I would prefer readability over optimization in this instance, but that's just me.
I can't think of a more efficient methods than using a simple where statement like in:
var results = SearchContext.GetQueryable<Person>()
.Where(i => i.Enabled && i.TemplateName == "Person" &&
i.Location != null && i.Location.Id == 1)
.Random(6);
Keep in mind that if you use the && statement instead of a where for each parameter, you reduce the complexity of the algorithm.
You could also use an Inverse Navigation Property on Location to a virtual ICollection<Person> and then be able to do this:
var results = SearchContext.GetQueryable<Location>()
.Where(i => i.Id == 1 && i.Persons.Where(p => p.Enabled && p.TemplateName == "Person").Any())
.Random(6);
The first option would still be the most efficient, because the second one uses sub-queries. But it is worth knowing you can do your search the other way.

Lambda expression like Sql where value or null

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())))

lambda expression trying to query one list based on another

After some advice on how to do this nicely using lambda expressions.
What I want is to get a list of placements based on an agency. I want the placements where the placement.agency != agency but where placement.agencypersonnel contains any of the agency staff.
So where the placement is not for that agency, but there are staff from that agency involved in another agency's placement
I don't know how to query based on the second condition.
So something like:
// agency is being passed in
var agencySupervisors = agency.AgencyPersonnel;
return agency.Placements
.Where(p => p.Supervisors.Contains(agencySupervisors))
.Where(p => p.Agency != agency);
I get that Contains is supposed to refer to a single object rather than a collection - which is why its erroring.. but I'm not sure how to get it to check against all objects in the collection.
Have also tried Any
return agency.Placements
.Where(p => agencySupervisors.Any<PlacementSupervisor>(p.Supervisors))
.Where(p => p.Agency != agency);
So hopefully its just I'm using the wrong one!!
Another spanner in the works is trying to figure out how the placement supervisor and the agency personnel entities relate to one another.. I think its linked on AgencyPersonnelId = SupervisorId so I'm guessing that will also have to be factored into my expression.
Thanks!
Edit: How do I handle if the type of objects in the two list aren't the same - but I know that the Id will match. Do I have to write a comparer and somehow incorporate that into the expression?? ie. AgencyPersonnelId = SupervisorId
I have tried:
return placements
.Where(p => p.Supervisors.Any(supervisor => agencySupervisors.Any(ap => ap.AgencyPersonnelId == supervisor.SupervisorId)));
But it is giving me no results so it is obviously wrong.
Edit: Actually when I try to iterate through the placements in the returned collection I'm getting a null reference exception - so I'm not sure if its something to do with my expression or the way I'm returning the results.
You are close with Any & Contains - try both at once
return agency.Placements
.Where(p => agencySupervisors.Any(supervisor => p.Supervisors.Contains(supervisor))
.Where(p => p.Agency != agency);
I think you can do it with .Intersect also:
return agency.Placements
.Where(p => agencySupervisors.Intersect(p.Supervisors).Any()
&& p.Agency != agency);
Thanks everyone for the help - Because the objects were of different types I ended up having to do something a little different - but then found I was able to use their Ids for the comparison so the result was:
var agencySupervisors = (from ap in agency.AgencyPersonnel
where ap != null
select ap.AgencyPersonnelId).ToList();
return
(from p in m_PlacementRepository.Linq orderby p.PlacementId select p)
.Where(p => p.Agency != agency)
.Where(p => p.Supervisors != null && p.Supervisors.Any(s => agencySupervisors.Contains(s.SupervisorId)));
Plus as Mikael rightly pointed out I was starting with the wrong collection in the first place :)

Categories

Resources