Avoid NullReferenceException in LINQ Expression on Datatable column - c#

I am stuck with null values in my Datatable "articles". Using LINQ to get a list of articles works for column ArticleId but with column "ArticleVariations" the null values are killing me.
var result = this.articles.AsEnumerable().Where(r =>r.Field<String>("ArticleId").Equals(artNo)); // works. no nulls there ;)
var result = this.articles.AsEnumerable().Where(r =>r.Field<String>("ArticleVariations").Equals(artNo)); // stuck with nulls here
If the column contains nulls, I get an NullReferenceException, Can I avoid this somehow and is it possible to merge both expressions?

You can use null-conditional and null-coalescing operators:
var result = this.articles.AsEnumerable()
.Where(r =>r.Field<String>("ArticleVariations")?.Equals(artNo) ?? false);

The problem obviously occurs because r.Field<String>("ArticleVariations") retuns null. Thus you have to check for null before calling Equals on it.
For that you can call multiple statements within a LINQ-expression:
var result = this.articles.AsEnumerable().Where(r => {
var res = r.Field<String>("ArticleVariations");
if (res != null) return res.Equals(artNo);
else return false;
});

If the field can be null then just reverse your test:
var result = this.articles.AsEnumerable().Where(r => artNo.Equals(r.Field<String>("ArticleVariations")));
Then all you need to do is check that artNo is not null before making the call:
List<Type> result;
if (string.IsNullOrWhiteSpace(artNo))
{
result = new List<Type>();
}
else
{
result = this.articles.... as before
}

Where just takes a function which returns a bool to determine if it should filter an item out of a collection. You can write it with a multi-statement body just like any other function to make nulls easier to deal with. Something like this should be a good starting point:
.Where(r => {
string articleVariations = r.Field<string>("ArticleVariations");
return articleVariations != null && articleVariations.Equals(artNo);
});
IF you want to combine those checks somehow to build a list where one or the other of the given fields matches your artNo, you can just add it to the function body.

If the column contains nulls, I get an NullReferenceException, Can I avoid this somehow
Avoid using instance Equals methods where possible. Use the respective operators or static Equals methods because they handle correctly nulls for you.
In your concrete case, the easiest way is to replace Equals with ==:
var result = this.articles.AsEnumerable()
.Where(r => r.Field<string>("ArticleId") == artNo);
var result = this.articles.AsEnumerable()
.Where(r => r.Field<string>("ArticleVariations") == artNo);
and is it possible to merge both expressions?
It depends on what do you mean by "merging" them. If you mean matching article or variation by the passed artNo, then you can use something like this
var result = this.articles.AsEnumerable()
.Where(r => r.Field<string>("ArticleId") == artNo
|| r => r.Field<string>("ArticleVariations") == artNo);

Related

LINQ statement using one of two parameters

I'm working on a LINQ statement. I have a table of cities where the records have either a countryId or a stateId. I'd like to just write the one statement and have the where clause check to see which of the two parameters is null and then select on the one that is not.
Here's what I'm working with:
public List<City> Cities(int? countryTypeId, int? stateTypeId)
{
if (countryTypeId == null && stateTypeId == null)
return null;
return _db.City
.Where(x => x.StateTypeId == stateTypeId
&& x.CountryTypeId == countryTypeId)
.OrderBy(x => x.Description)
.ToDTOs();
}
I'm pretty new to LINQ, and I know this code isn't right, just adding it for context.
If the State and Country ids are all >0 you simply can do this, no need to check for null
.Where(x => x.StateTypeId == stateTypeId.GetValueOrDefault()
&& x.CountryTypeId == countryTypeId.GetValueOrDefault())
Else you need to add the condition if those nullable inputs have value or not, as mentioned in the comment
Edit: after seeing some comments, if you are looking for list of cities based on either of the parameters, then you should be using || not && in your where condition
Where(x => (stateTypeId.HasValue && stateTypeId.Value == x.StateTypeId)
|| (countryTypeId.HasValue && countryTypeId.Value == x.CountryTypeId))
Note the order matters, this code will first check if stateTypeId has value and if it has it'll match only the cities with that stateTypeId
_db.City.Where(c => c.CountryTypeId?.Equals(countryTypeId) ?? false
| c.StateTypeId?.Equals(stateTypeId) ?? false);
Using null conditional operators - when a type Id is null use the null coalescing operator to return false and fail the match - otherwise check for equality and return matching.
Note you cannot short circuit the OR operator here!
I'm not sure if this is the case, but if one of the input parameters was always null and the entries were guaranteed to always have one property null, the following would be a cool solution:
_db.City.Where(c => (c.CountryTypeId ?? c.StateTypeId) == (countryTypeId ?? stateTypeId))
My DBA has sufficiently beaten it into my head that ignoring parameters in a query (ex: WHERE Field = #PARAM or #PARAM IS NULL) can result in very bad things. As a result, I would encourage you to conditionally add only the parameters that you absolutely need. Fortunately, given that you are working with just two possible parameters, this is trivial.
Start with the base of your query, and then add to it.
var queryBase = _db.City.OrderBy(x => x.Description);
if (countryTypeId.HasValue)
{
queryBase = queryBase.Where(x => x.CountryTypeId == countryTypeId);
}
if (stateTypeId.HasValue)
{
queryBase = queryBase.Where(x => x.StateTypeId == stateTypeId);
}
return queryBase.ToDTOs(); // or .ToList() for a more universal outcome
Add whatever logic you may need if parameters are mutually exclusive, one supercedes the other, etc.

LINQ C# Join with Left-Join result

I'm trying to do a left-join with the result of another left-join but in some cases it returns me null reference error (obviously), is there any way to do this without using foreach after select?
var result = from f in foo
from ex in exp.Where(w => w.id == f.idFoo).DefaultIfEmpty()
from te in tes.Where(w => w.id == ex.anyID).DefaultIfEmpty()
select new
{
var1 = f.something,
...
var2 = te.anything
};
That's happening because DefaultIfEmpty is returning a sequence with a single null element. Then you are trying to access properties on a null element.
Use the overload of DefaultIfEmpty to return a non-null default value (of the appropriate data type):
var result = from f in foo
from ex in exp.Where(w => w.id == f.idFoo)
.DefaultIfEmpty(new Exp())
from te in tes.Where(w => w.id == ex.anyID)
.DefaultIfEmpty(new Tes())
select new
{
var1 = f.something,
...
var2 = te.anything
};
That way you won't be trying to access properties on null elements.
Edit based on comments. In the query, one place that null elements arise is when a Where results in zero matches. For instance, in the third from the ex variable might be a sequence of one null element, and again in the select the te might also be such a sequence. In both cases, accessing a property will throw an exception.
Alternatively, you could do a null test every time you are going to access a property. That would become verbose fast.
Something like this:
var2 = te != null ? te.anything : null

Better way to write this lambda expression?

I'm trying to write a lambda expression that looks at an array of objects ('fields') that may or may not have specific items in it. I would like to retrieve values if they exist; if no field in the array has a code of 'SomeCode', then there is no value to retrieve. I'm curious if there is a cleaner way to write the below, since I'll need to do this for a lot of fields and done want to run through the 'FirstOrDefault' call twice.
if (fields.FirstOrDefault(x => x.Code == "SomeCode") != null)
{
obj.CodeValue = fields.FirstOrDefault(x => x.Code == "SomeCode").Value;
}
EDIT: Thanks for any insight on doing this in a 'Lambda only' fashion; I'd like to improve my skills in this area and figured that there would be something cleaner than either calling it twice or simply assigning it to an interim object.
Just cache the return value from FirstOrDefault and then compare it.
var item = fields.FirstOrDefault(x => x.Code == "SomeCode");
if (item != null)
{
obj.CodeValue = item.Value;
}
In your current code you are querying twice, once for checking null, later to access value.
Try this:
var field = fields.FirstOrDefault(x => x.Code == "SomeCode");
if (field != null)
obj.CodeValue = field.Value;

LINQ check if FirstOrDefault is null and use it

I'm writing a query that uses FirstOrDefault after an OrderBy query, which should check if it isn't null first then use some data in it. Is there a better way than writing it like this:
int count = db.Items.Count(i =>
i.Assignments.OrderByDescending(a =>
a.DateAssigned).FirstOrDefault() != null
&&
i.Assignments.OrderByDescending(a =>
a.DateAssigned).FirstOrDefault().DateReturned == null)
What this code does is there are items that has many assignments, I take the latest assignment by date, then check if it exist, then run a condition on a property (DateReturned). As you see, this query is long, and most of my queries seem to look like this where I check for null first then run a second query on it using their properties. Is there a better way of doing this?
Just call .Any(a => a.DateReturned == null) to check whether there are any items that meet the condition.
If you only want to check the latest assignment, add .Take(1) before the .Any().
My take:
int count =
itemsQuery.Select(i => i.Assignments.OrderByDescending(a => a.DateAssigned))
.Count(i => i.FirstOrDefault() != null &&
i.First().DateReturned == null);
You can put the result in a variable to avoid doing the same thing twice:
int count = itemsQuery.Count(i => {
var f = i.Assignments.OrderByDescending(a => a.DateAssigned).FirstOrDefault();
return f != null && f.DateReturned == null;
});

Where Predicates in LINQ

How can I specify conditions in Where predicates in LINQ without getting null reference exceptions. For instance, if q is an IQueryable how can I do like:
Expression<Func<ProductEntity,bool>> predicate = p => !search.CategoryId.HasValue || (search.CategoryId.HasValue && search.CategoryId == p.CategoryId);
var q2 = q.Where(predicate);
Here search is an object that holds possible search conditions that may or may not be set like search.CategoryId might not be set but if it is I want to get the products that are set by that condition.
When I do this I get null reference exceptions.
You can use the null-coalescing operator ?? to replace a possible null value with a default value. The following sets tries to match the search.Category if it exists or simply creates an "always true" expression. This will be optimized by any good Linq query provider (e.g. LinqToSql).
Expression<Func<ProductEntity,bool>> predicate = p => (search.CategoryId ?? p.CategoryId) == p.CategoryId);
var q2 = q.Where(predicate);
Another possibility would be to dynamically compose a query predicate using PredicateBuilder. That's the way I do it for searches with a similar pattern as you use:
var predicate = PredicateBuilder.True<Order>();
if (search.OrderId))
{
predicate = predicate.And(a => SqlMethods.Like(a.OrderID, search.OderID);
}
// ...
var results = q.Where(predicate);
Let's dissect the line:
Expression<Func<ProductEntity,bool> predicate = p => !search.CategoryId.HasValue
|| (search.CategoryId.HasValue && search.CategoryId == p.CategoryId)
var q2 = q.Where(predicate);
So how many ways can we get null problems?
search (your "captured" variable) could be null
p could be null, meaning there is a null in the list
you've handled the case of search.CategoryId being null (Nullable<T>)
but maybe p.CategoryId (the category on a record in the list) is null (Nullable<T>) - however, I'm not sure that this would cause a NullReferenceException
q (the list / source) could be null
So: out of 5 options you've eliminated 1; look at the other 4? There is also the definite possibility that the issue is caused by something invisible not shown in the code; for example the get could be:
public int? CategoryId {
get {return innerObject.CategoryId;}
}
and innerObject could be null; if you eliminate the other 4 (pretty easy to do), look at at this one as a last resort.

Categories

Resources