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;
};
Related
I'm having an issue where I want to return results where something matches and I get an error if one of the properties I'm trying to match is null.
if (!string.IsNullOrEmpty(searchString))
{
Infos = Infos.Where(
x =>
x.FirstName.ToLower().Contains(searchString) ||
x.LastName.ToLower().Contains(searchString) ||
x.ContractNum.ToLower().Contains(searchString) ||
x.VIN.ToLower().Contains(searchString) ||
x.Claim.InitiatedBy.ToLower().Contains(searchString)
).ToList();
}
If ContractNum or VIN, for example, are null then it throws an error. I'm not sure how to check if one of these are null inside of a linq query.
You can add explicit null checks:
Infos = Infos.Where(
x =>
(x.FirstName != null && x.FirstName.ToLower().Contains(searchString)) ||
(x.LastName != null && x.LastName.ToLower().Contains(searchString)) ||
(x.ContractNum != null && x.ContractNum.ToLower().Contains(searchString)) ||
(x.VIN != null && x.VIN.ToLower().Contains(searchString)) ||
(x.Claim != null && x.Claim.InitiatedBy != null && x.Claim.InitiatedBy.ToLower().Contains(searchString))
).ToList();
You have multiple options, first is to do an explicit check against null and the other option is to use Null propagation operator.
x.FirstName != null && x.FirstName.ToLower().Contains(searchString)
or
x.FirstName?.ToLower()?.Contains(searchString) == true
But I would suggest you to use IndexOf instead of Contains for case
insensitive comparison.
something like:
x.FirstName?.IndexOf(searchString, StringComparison.CurrentCultureIgnoreCase) >= 0)
Checking the property is null or empty before comparing it it's the only way I know
if (!string.IsNullOrEmpty(searchString))
{
Infos = Infos.Where(
x =>
(!String.IsNullOrEmpty(x.FirstName) && x.FirstName.ToLowerInvariant().Contains(searchString)) ||
(!String.IsNullOrEmpty(x.LastName) && x.LastName.ToLowerInvariant().Contains(searchString)) ||
(!String.IsNullOrEmpty(x.ContractNum) && x.ContractNum.ToLowerInvariant().Contains(searchString)) ||
(!String.IsNullOrEmpty(x.VIN) && x.VIN.ToLowerInvariant().Contains(searchString)) ||
(x.Claim != null && !String.IsNullOrEmpty(x.Claim.InitiatedBy) && x.Claim.InitiatedBy.ToLowerInvariant().Contains(searchString))
).ToList();
}
EXTRA: I added a check on the Claim property to make sure it's not null when looking at InitiatedBy
EXTRA 2: Using the build in function IsNullOrEmpty to compare string to "" and nullso the code is clearer.
Extra 3: Used of ToLowerInvariant (https://msdn.microsoft.com/en-us/library/system.string.tolowerinvariant(v=vs.110).aspx) so the lowering action will act the same no matter of the culture.
You could use ?? to replace it with a acceptable value.
(x.ContractNum??"").ToLower()
I would use the null conditional operator ?, this will however, return a nullable bool? so you will need to handle that appropriately.
Some examples on how to do this:
x?.FirstName?.ToLower().Contains(searchString) == true;
x?.FirstName?.ToLower().Contains(searchString) ?? false;
An alternative method to keep the comparison logic in one place to use a sub collection of the properties and check on those:
Infos = Infos.Where(i=>
new[] {i.FirstName,i.LastName,i.ContractNum /*etc*/}
.Any(w=> w?.ToLower().Contains(searchString) ?? false))
.ToList();
(It does read out all properties, but that shouldn't cost much performance and gains much maintainability )
I'd like to tidy up this piece of code, any idea?
1 object is not null
1 of its property is not null either
var v = Values.Find(x => x.id.Equals(Properties.Resources.myString));
if (v != null && v.Property != null)
{
// do something with 'v.Property'
}
C# 6.0 will probably best serve you here with the null conditional operator:
var v = Values.Find(x => x.id.Equals(Properties.Resources.myString));
var result = v?.Property?.DoSomething();
bool Postkey =
statement
.ThreadPostlist
.First(x => x.ThreadKey == ThreadKey && x.ClassKey == classKey)
.PostKey;
This Ling query is giving me "Sequence contains no matching element" but I know I can use .FirstorDefault(). When I use .FirstorDefault() it will return me false, the default value for bool, when there are no matching records.
But I get a "Object not set to an instance of an object" error. I need to check the bool for null with .HasValue and .Value. I don't know how to do it.
Here is how you can use a nullable bool to solve this:
bool? postKey = null;
// This can be null
var post = statement.ThreadPostlist.FirstOrDefault(x=>x.ThreadKey == ThreadKey && x.ClassKey == classKey);
if (post != null) {
postKey = post.PostKey;
}
// Now that you have your nullable postKey, here is how to use it:
if (postKey.hasValue) {
// Here is the regular bool, not a nullable one
bool postKeyVal = postKey.Value;
}
You could do:-
bool? postkey = threadPostList
.Where(x=>x.ThreadKey == threadKey && x.ClassKey == classKey)
.Select(x => (bool?)x.PostKey)
.DefaultIfEmpty()
.First();
I think that better captures the intent of what you are trying to accomplish.
If you want to treat a null value as false (and don't want to use a nullable bool), you could just check if the resulting post is null before referencing the .PostKey property, like this:
var threadPost = statement.ThreadPostlist.FirstOrDefault(x =>
x.ThreadKey == ThreadKey && x.ClassKey == classKey);
bool PostKey = threadPost != null && threadPost.PostKey;
Or, a longer form would be:
bool PostKey;
if (threadPost != null)
{
PostKey = threadPost.PostKey;
{
else
{
PostKey = false;
}
List<OfferDTO> offers = dbContext.Offer.Where(x => x.IsDeleted)
.OrderBy(i => i.OfferID)
.Skip(start).Take((length))
.Select(y => new OfferDTO
{
OfferStageValue = y.OfferStage.Value ?? null,
PropertyAddressLine1 = (y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null) ? y.PropertyAuction.Property.Address.AddressLine1 : string.Empty,
PropertyAddressLine2 = (y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null) ? y.PropertyAuction.Property.Address.AddressLine2 : string.Empty,
PropertyCity = (y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null) ? y.PropertyAuction.Property.Address.City : string.Empty,
PropertyZip = (y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null) ? y.PropertyAuction.Property.Address.PostalCode : string.Empty,
})
.ToList();
In the above example, I am casting directly to an object with a select statement.
Certain properties need to be checked for a null value before their values are used in the new object.
However, as you see, different properties check the same value for null over and over again. Specifically here, you see:
(y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null)
is checked a few times in a row.
In the ensuing SQL, you are seeing that the query is checking for null each time:
CASE WHEN (([Extent12].[PropertyID] IS NOT NULL) AND ([Extent13].[AddressID] IS NOT NULL)) THEN [Extent14].[AddressLine1] ELSE #p__linq__1 END AS [C5],
CASE WHEN (([Extent12].[PropertyID] IS NOT NULL) AND ([Extent15].[AddressID] IS NOT NULL)) THEN [Extent16].[AddressLine2] ELSE #p__linq__2 END AS [C6],
CASE WHEN (([Extent12].[PropertyID] IS NOT NULL) AND ([Extent17].[AddressID] IS NOT NULL)) THEN [Extent18].[City] ELSE #p__linq__3 END AS [C7]
Is there a way to check once and carry that over, or is this the best way to safely use these values?
NOTE
y is the base table
PropertyAuction,
Property, and
Address are all separate tables as well and may not contain data.
You can get a bit more elegant in the LINQ itself by switching to Query syntax, and using the let keyword, but I think you're going to find your generated SQL is (has to be) pretty similar:
var offers = (from o in dbContext.Offers
where o.IsDeleted
let p = o.PropertyAuction.Property
let a = p != null ? p.Address : null
orderby o.OfferID
select new OfferDTO
{
OfferStageValue = o.OfferStage.Value,
PropertyAddressLine1 = a != null ? a.AddressLine1 : string.Empty,
PropertyAddressLine2 = a != null ? a.AddressLine2 : string.Empty,
PropertyCity = a != null ? a.City : string.Empty,
PropertyZip = a != null ? a.PostalCode : string.Empty,
})
.Skip(start).Take(length)
.ToList();
EDIT
Just got a chance to put the full model into the compiler and check the SQL - as expected, the generated SQL is identical to the SQL generated from your original query; (i.e., even though extracted into a single check in the LINQ, the generated SQL inlines these checks four times, resulting in identical SQL code).
If your primary goal was cleaner LINQ code, at least this answer accomplishes that.
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())))