Handling Nulls in a model passed over into a controller - c#

I am trying to fix these linq statements so that they wont error if the model is null.
For example:
model.FilterSet.Dispositions may be null. While model.FilterSet.GenderFilters may not.
My current linq statement gives error if there is a null so I tried to add .Where(item => item != null) in the DispositionFilters linq, but it gives me an error saying that this will always be true.
Here is my code:
var filters = new RespondentCSVFilters
{
CSVFilters = new CSVFilters
{
DispositionFilters = model.FilterSet.Dispositions.Where(item=>item != null).ToList().ConvertAll(item => (int)((RespondentStatus)Enum.Parse(typeof(RespondentStatus), item.ToString()))),
EthnicitiesFilters = model.FilterSet.Ethnicities.ConvertAll(item => (int)((Ethnicity)Enum.Parse(typeof(Ethnicity), item.ToString()))),
GenderFilters = model.FilterSet.Genders.ConvertAll(item => (int)((Gender)Enum.Parse(typeof(Gender), item.ToString()))),
SourcesFilters = model.FilterSet.Sources.ConvertAll(item => (int)((RespondentSource)Enum.Parse(typeof(RespondentSource), item.ToString()))),
}
};
I am not sure how to make the changes in order to handle nulls.

I tried to add .Where(item => item != null)
But this will only check each item for null, not the source ("Dispositions"). To check for null fluently (without adding a bunch of "if" statements), an EmptyIfNull extension can be helpful:
public static class Extensions
{
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> self)
{
return self == null ? Enumerable.Empty<T>() : self;
}
}
Now you can write ...
model.FilterSet.Dispositions.EmptyIfNull().Where( ... )
and if model.FilterSet.Dispositions is null, you won't get an exception, but the statement will evaluate to an empty set.

Related

how to check null values in a foreach loop

I keep getting null exceptions when iterating over my foreach loop and I think it is because they
way I am trying to check, iterate and skip over the nulll values is incorrects. Basically when it doesnt fine "New" it throws an exception.
public async Task<IActionResult> NewRequisitionsEntryList()
{
//var requisitions = _repositoryWrapper.Requisition.GetAllNewRequisitions();
//UserInformation UserInformation = await _userManager.GetUserAsync(User);
var newReqList = _context.Requisition.OrderByDescending(r => r.Status.Equals("New")).ToList();
foreach (var reqList in newReqList ?? Enumerable.Empty<Requisition>())
{
UserInformation identityUser = await _userManager.GetUserAsync(User);
var departmentId = _context.Department.Where(x => x.DepartmentName == identityUser.Department).FirstOrDefault();
var userDepartmentId = departmentId.DepartmentId;
if (departmentId.DepartmentId == reqList.DepartmentId)
{
if (_context.ProcessStepLog.Where(d => d.RequistionId == reqList.RequisitionId).FirstOrDefault().ProcessStepName == "New")
{
CurrentStepName = _context.ProcessStepLog.Where(d => d.RequistionId == reqList.RequisitionId).FirstOrDefault().ProcessStepName;
requistionsNew.Add(new NewRequistionModel()
{
RequistionNo = reqList.RequisitionNo,
RequisitionTitle = reqList.RequisitionTitle,
DateCreated = reqList.DateCreated,
ProcessStepName = CurrentStepName,
RequestedBy = reqList.QueriesEmail,
});
}
}
}
await Task.Run(() =>
{
Console.WriteLine("workspace");
});
ViewData["requistionsNew"] = requistionsNew;
return View(newReqList);
}
Its breaking on the second if clause on the line that has the where statement
So you mean the second if clause makes error. We can analysis that firstly you use the _context before the if clause without any error, so it will not be null for the context. Then we can see you use FirstOrDefault which can get the model data, here can receive null value. Null value is acceptbale but if you want to get the property of the null object, it will throw exception.
You can simply use ? to avoid null exception:
_context.ProcessStepLog.Where(d => d.RequistionId == reqList.RequisitionId).FirstOrDefault()?.ProcessStepName;
Or change the if clause:
var model = _context.ProcessStepLog.Where(d => d.RequistionId == reqList.RequisitionId).FirstOrDefault();
if (model !=null && model .ProcessStepName == "New")
Suggestions:
Actually debugging code is the most efficient way to find out the error and know how to modify. You need set the breakpoint to your action and quick watch the line which makes error, step by step to find where is the real error. For example, right-click and choose quick watch, then in the search bar type the _context.ProcessStepLog to check the value or Results View. If it contains value, then type Where(d => d.RequistionId == reqList.RequisitionId).FirstOrDefault() to check it. Step by Step.....
Refer to:
Use breakpoints in the Visual Studio debugger
Watch variables with Watch windows and QuickWatch

Null reference exception after checking for null (checking for null doesn't work)

Take a look at this code:
var categories = tokens.SelectMany(x => x.Categories);
if (categories != null)
{
if (categories.Contains("interp")) //null ref exception
{
return null;
}
}
I get Null Reference Exception when I try fo find "interp" string within categories. So it seems that "categories != null" doesn't work.
I found some suggestions (here How to check if IEnumerable is null or empty?) but they involve using .Any(). But it only makes the exception accure earlier (while using .Any()). Even ?.Any() throws the exception.
Any ideas?
This code will throw an NRE in categories.Contains only if the Categories property is null.
The following code will throw :
class Token
{
public string[] Categories{get;set;}
}
var tokens=new []{new Token()};
var categories = tokens.SelectMany(x => x.Categories);
if (categories != null)
{
if (categories.Contains("interp"))
{
Console.WriteLine("Found");
}
}
But so would
tokens.SelectMany(x => x.Categories).ToArray();
The thing that actually throws is the nested iterator inside SelectMany, not ToArray() or Contains. The stack trace for that exception is :
at System.Linq.Enumerable.<SelectManyIterator>d__17`2.MoveNext()
at System.Linq.Enumerable.Contains[TSource](IEnumerable`1 source, TSource value, IEqualityComparer`1 comparer)
at UserQuery.Main()
SelectMany will try to iterate over each Categories entry, find that the property is actually null and throw.
The quick fix is to add a Where before SelectMany to eliminate null Categories :
var categories = tokens.Where(x=>x.Categories!=null).SelectMany(x => x.Categories);
The real solution is to ensure Categories is never empty - it should be initialized to an empty array, list, whatever upon construction. When reassigned, it should never be set to null.
This example sets the _categories field to new string[0] even if a caller passes null to Categories
class Token
{
string[] _categories=new string[0];
public string[] Categories{
get => _categories;
set => _categories = value??new string[0];
}
}
With that, Where(x=>x.Categories !=null) is no longer necessary
When working with collections and IEnumerable<T> avoid using null; if you have nothing to return, return an empty collection (not null).
In your particular case SelectMany will never return null, but empty collection, that's why categories != null check is useless,
and you have to check tokens instead
if (null != tokens)
// Where(x => x != null) - to be on the safe side if x == null or x.Categories == null
if (tokens
.Where(x => x != null && x.Categories != null)
.SelectMany(x => x.Categories)
.Contains("interp"))
return null;
However, constant checking for null makes code being unreadable, that's why try check for null once:
// if tokens is null change it for an empty collection
tokens = tokens ?? new MyToken[0];
...
if (tokens
.Where(x => x != null && x.Categories != null)
.SelectMany(x => x.Categories)
.Contains("interp"))
return null;
var categories = tokens.SelectMany(x => x.Categories).ToList();
add .ToList() and you should know more about where the error is
with that information we have in the post, we can only guess
Can use where clause and make it as list , then just check if there is any element in the list
var categories = list.Where(x => x.Categories.Contains("interp")).ToList();
if (categories.Count() == 0)
{
return null;
}

How to deal with null linq query result?

I have a linq query in which i am getting the user list and after that we are getting the record by userid filter.
var user = UserDal.GetAllUsers().First(n => n.UsersID == 1);
But what should do if GetAllUsers() function returns null ? Should we check it by applying Any() ? Or something else ?
But what should do if GetAllUsers() function returns null ?
If UserDal.GetAllUsers returns null, then you can't use Any, you will have to check it against null. You can only use Any or FirstOrDefault if the method returns a collection (even empty).
If the method could return null then:
var temp = UserDal.GetAllUsers();
if(temp != null)
{
var user = temp.FirstOrDefault(n=> n.UserID == 1);
}
If your method could return an empty collection instead of null then you can do:
var user = UserDal.GetAllUsers().FirstOrDefault(n => n.UsersID == 1);
You should modify your method UserDal.GetAllUsers to not return a null. If there are no records then an empty collection should be returned.
Why not simply check if it is null?
var users = UserDal.GetAllUsers();
if (users == null || !users.Any())
{
// handle error
}
var user = users.First(x => x.Id == someId);
However, if you're in control of the code-base, I'd certainly say that you should make it so GetAllUsers never returns null.
You could use [DefaultIfEmpty] and use that instance as default value: to handle null value
You could use Monads, e.g. https://github.com/sergun/monads.net
Or you could implement your own extension methods.
This code is adapted from the original extension method and returns default(T) instead of throwing an exception.
// UserDal.GetAllUsers() returns null and user is null
var user = UserDal.GetAllUsers().MyFirstOrDefault(n => n.UsersID == 1);
public static class IEnumerableExtensions
{
public static T MyFirstOrDefault<T>(this IEnumerable<T> source,
Func<T, bool> predicate)
{
if (source == null) return default(T);
if (predicate == null) return default(T);
foreach (T element in source)
{
if (predicate(element)) return element;
}
return default(T);
}
}
Or you could use null checks or ensure never to return null to safeguard against null reference exception when using the standard extension methods.

NullReferenceException using where clause

I have a scenario that I am getting the result properly .But i have to search it in that result.Here is my code.
if(productSearch.Keyword !=null || productSearch.Code!=null)
{
var key = productSearch.Keyword;
var cod = productSearch.Code;
if (productSearch.Code != null)
{
var Selected_Result = result.Results.Where(s => s.Code.ToLower().Contains(cod.ToLower())).ToList();
result.Results = Selected_Result;
}
else
{
var Selected_Result = result.Results.Where(s => s.Keyword.ToLower().Contains(key.ToLower())).ToList();
result.Results = Selected_Result;
}
}
But it gives following exception :
Object reference not set to an instance of an object on result.Results.Where(s => s.Code.ToLower().Contains(cod.ToLower())).ToList();
I know s => s.Code.ToLower() is coming NULL, but i dont know why, result has records.
Thanks in advance.
If it's null in the query then chances are it's null in the DB. To be on the safe side you can use the null coalescing operator to make sure you have at least something to call ToLower on e.g.
result.Results.Where(s => (s.Code ?? "").ToLower().Contains(cod.ToLower()))
.ToList();

LINQ query null exception when Where returns 0 rows

I have the following LINQ method that works as expected except if there are No Rows Found then I get a Null Exception. I am struggling on how I modify this to return 0 if that occurs.
public static int GetLastInvoiceNumber(int empNumber)
{
using (var context = new CmoDataContext(Settings.Default.LaCrosse_CMOConnectionString))
{
context.Log = Console.Out;
IQueryable<tblGreenSheet> tGreenSheet = context.GetTable<tblGreenSheet>();
return (tGreenSheet
.Where(gs => gs.InvoiceNumber.Substring(2, 4) == empNumber.ToString())
.DefaultIfEmpty()
.Max(gs => Convert.ToInt32(gs.InvoiceNumber.Substring(6, gs.InvoiceNumber.Length)))
);
}
}
Thanks
I tried one of Jon Skeet's suggestions, below, and now I get Unsupported overload used for query operator 'DefaultIfEmpty'
public static int GetLastInvoiceNumber(int empNumber)
{
using (var context = new CmoDataContext(Settings.Default.LaCrosse_CMOConnectionString))
{
context.Log = Console.Out;
IQueryable<tblGreenSheet> tGreenSheet = context.GetTable<tblGreenSheet>();
return tGreenSheet
.Where(gs => gs.InvoiceNumber.Substring(2, 4) == empNumber.ToString())
.Select(gs => Convert.ToInt32(gs.InvoiceNumber.Substring(6, gs.InvoiceNumber.Length)))
.DefaultIfEmpty(0)
.Max();
}
}
You're using
.Where(...)
.DefaultIfEmpty()
which means if there are no results, pretend it's a sequence with a single null result. You're then trying to use that null result in the Max call...
You can probably change it to:
return tGreenSheet.Where(gs => ...)
.Max(gs => (int?) Convert.ToInt32(...)) ?? 0;
This uses the overload finding the maximum of int? values - and it returns an int? null if there were no values. The ?? 0 then converts that null value to 0. At least, that's the LINQ to Objects behaviour... you'll have to check whether it gives the same result for you.
Of course, you don't need to use the ?? 0 if you're happy to change the method signature to return int? instead. That would give extra information, in that the caller could then tell the difference between "no data" and "some data with a maximum value of 0":
return tGreenSheet.Where(gs => ...)
.Max(gs => (int?) Convert.ToInt32(...));
Another option is to use the overload of DefaultIfEmpty() which takes a value - like this:
return tGreenSheet.Where(gs => ...)
.Select(gs => Convert.ToInt32(...))
.DefaultIfEmpty(0)
.Max();
In situations like this when there may or may not be a matching item, I prefer to return an object rather than a value type. If you return a value type, you have to have some semantics about what value means "there is nothing here." I would change it to return the last invoice, then (when it is non-null) get the invoice number from the invoice. Add a method to the class to return the numeric invoice number from the string.
public static tbleGreenSheet GetLastInvoice(int empNumber)
{
using (var context = new CmoDataContext(Settings.Default.LaCrosse_CMOConnectionString))
{
context.Log = Console.Out;
return context.GetTable<tblGreenSheet>()
.Where(gs => gs.InvoiceNumber.Substring(2, 4) == empNumber.ToString())
.OrderByDescending(gs => Convert.ToInt32(gs.InvoiceNumber.Substring(6, gs.InvoiceNumber.Length)))
.FirstOrDefault();
}
}
public class tbleGreenSheet
{
....
public int NumericInvoice
{
get { return Convert.ToInt32(InvoiceNumber.Substring(6, InvoiceNumber.Length)); }
}
...
}
Used as
var invoice = Foo.GetLastInvoice( 32 );
if (invoice != null)
{
var invoiceNumber = invoice.NumericInvoice;
...do something...
}
else
{
...do something else...
}
I had a remarkably similar experience with IQueryable<T> and NHibernate. My solution:
public static TExpr MaxOrDefault<TItem, TExpr>(this IQueryable<TItem> query,
Expression<Func<TItem, TExpr>> expression) {
return query.OrderByDescending(expression).Select(expression).FirstOrDefault();
}
The only drawback is that you are stuck with the standard default value, instead of getting to specify one.

Categories