How to check null collection? - c#

I have the algorithm that select a data from collection of reportData
private IDictionary<Guid, JobStatistic> GetAgentsStatistics(IList<Guid> agentIds)
{
var agentReportItems = from data in reportData
from agentId in data.AgentIds
where agentIds.Contains(agentId)
group data by agentId;
...
}
But what if agentIds is an empty collection? How to check this situation?

var agentReportItems = from data in reportData
from agentId in data.AgentIds
where agentIds != null && agentIds.Contains(agentId)
group data by agentId;
Just check if it's null, and if it's not, then use it as you already did.

The logic you've posted will work for an 'empty collection'
However, assuming you meant null instead of 'empty', this is what an ArgumentNullException would be used for.
Throwing ArgumentNullException
private IDictionary<Guid, JobStatistic> GetAgentsStatistics(IList<Guid> agentIds)
{
if (agentIds == null)
throw new ArgumentNullException("agentIds"); // use nameof(agentIds) instead of "agentIds" if using C# 6 or later
var agentReportItems = from data in reportData
from agentId in data.AgentIds
where agentIds.Contains(agentId)
group data by agentId;
//...
}

You can quickly check if an enumerable is empty using .Any()
if (!agentIds.Any()) //It's empty.
{
throw new ArgumentException("agentIds cannot be empty.");
}
Be aware that 'null' does not mean the list is empty. Null would be a complete absence of the list.

Related

Sort List in Descending Order via Array Count

I'm trying to sort a list of data I get from my database in descending order by the amount of upvotes held in each record. This is what I have currently but does not work. NOTE: the upvotes field is a string[] array within the model class.
//Method to get all popular posts and display them in a list
public async void GetPostInfo()
{
PostsMod = new ObservableCollection<IPosts>();
var temp = await _postsProxy.GetAllPosts();
if (temp != null)
{
if (temp.Count > 0)
{
var orderedList = temp.OrderByDescending(x => x.UpVoteId.Count()).ToList(); //Orders the records in descending order based on how many upvotes they contain, puts it into a new list
foreach (var item in orderedList)
{
PostsMod.Add(item);
}
}
else
PostsMod.Add(temp[0]);
}
}
All posts are put into a var temp and then the temp list is supposed to be ordered in descending order via the amount of upvotes counted.Keep in mind that again the upvote field is a string[] array in the model class.
It comes up with an error message: System.ArgumentNullException: 'Value cannot be null. Parameter name: source'
EDIT: Sorry it was a mistake on my part, I made the stupid mistake of the fields in the model class not matching. Therefore resulting in my upvotes being null.
System.ArgumentNullException: 'Value cannot be null. Parameter name: source' comes from a null IEnumerable<T> passed to a Linq extension method, the only one from your code being Count.
Try this:
var orderedList = temp.OrderByDescending(x => x.UpVoteId?.Count()).ToList();
This will fail since temp.Count will be 0:
else
PostsMod.Add(temp[0]);
That being said and althought not part of the problem, you should not return a null here. You should always get a collection, empty or not or throw an exception instead of swallowing it.
if (temp != null)

Count how many cells are null in a row using Linq and c#

I have a database for some movies. I want to count for each individual id how many cells are empty or null and display the count.
ex:
Id Title Price Genre
1 Last of Us 10 null
2 The others 8 Horror
3 X-Men null null
I want to be able to display for id 3 that there are 2 missing values or for id 1 that there is 1 missing value
so far I have this code:
Movie movieId = _context.Movies.Find(id);
var movies = from m in _context.Movies where m.ID == id select m ;
var model = new PartialModel();
model.Count = movies.Count();
Unfortunately the code only displays how many movies with the specific id were selected. I tried x => x = null for count but it gives a syntax error.
I think i need a way to get the empty fields from movie but i don't know how to do that.
Thank you
Your best bet is to add a property or method on the type and use reflection to count the number of public properties that have a null value. Do not do it inside of the linq to sql call, it does not belong there.
public class Movie {
// existing properties
// this should not be mapped back to the EF store
public int NumberOfNullPropValues => typeof(Movie).GetProperties().Count(x => x.GetValue(this, null) == null);
}
Note that this only works for reference types and Nullable<T>, it will not check for empty strings or for default values in the case the type is a struct (like 0 for int). Also if you want to check non public properties you need to pass binding flags to the GetProperties call
You can use Reflection
Like this (a method that returns how many null properties does an object have):
public int GetNullProperiesCount(object anyObject)
{
var objType = anyObject.GetType();
var nullCount = 0;
foreach(var propInfo in objType.GetProperties())
{
if(propInfo.CanRead)
{
object val = propInfo.GetValue(anyValue, null);
if(val == null) ++nullCount;
}
}
return nullCount;
}
Then you can iterate through any collection and get how many null properties its items have.
Reflection may be expensive in terms of CPU cycles. I'd use a DataSet to read and examine the data. It's easy to do and does not impose the overhead of Linq

C# LINQ inner object query

I am too new to LINQ.
public static Dictionary<Type, ConfigurationObjectBase> MyDictionary;
This expression returns "Object reference not set to an instance of an object":
MyDictionary.First(o => o.Value.ID == 12).Key;
In fact, the object with ID = 12 is there.
Update:
In fact, the object with ID = 12 is there
it means, there is objects within dictionary where ID is 12.
If there were no such item, then it would throw InvalidOperationException with message "Sequence contains no matching element".
It means that either:
your Dictionary is not initialized. You said that there is an object with ID = 12. So, it means that is it initialized.
there is at least one item in your dictionary where a value is null. So, while iterating, it tries to access its Value.ID and throws a NullReferenceException.
Imagine a simple loop over an array:
ConfigurationObjectBase Search()
{
ConfigurationObjectBase[] array = { someObject1, someObject2, null, someObject3 };
foreach (var item in array)
{
if (item.ID == 12) return item;
// here, if item is null, you will try to access its ID and get NullReferenceException
}
throw new InvalidOperationException("Sequence contains no matching elements");
}
Actually, that's what LINQ exactly does. It iterates through the dictionary and once it tries to access a property of null, it throws an exception.
You may use the following code to avoid accessing the property of null:
MyDictionary.First(o => o.Value != null && o.Value.ID == 12).Key;
If there is an object with ID 12 it means that your dictionary contains objects which are null. You can filter for them:
MyDictionary.Where(x=>x.Value!=null).First(o => o.Value.ID == 12).Key;
It will skip all objects with null Value. I prefer chaining here, because it shows intention clearly.
EDIT:
as #Yeldar Kurmangaliyev said this answer is good only for small dictionaries. If you want to go with big dictionaries better do:
MyDictionary.First(o => o.Value!=null && o.Value.ID == 12).Key;

Calling FirstOrDefault multiple times in LINQ to set object properties

I have a LINQ query like so:
var Item = (from s in contextD.Items where s.Id == Id select s).ToList();
Further down the Item object properties are set like so:
Item.FirstOrDefault().Qty = qtyToUpdate;
Item.FirstOrDefault().TotalPrice = UItem.FirstOrDefault().Qty * UItem.FirstOrDefault().Price;
...
My question is, will calling FirstOrDefault always loop through the result set returned by the query?
Shouldn't a single call be made and put in an object like so:
MyObject objMyObject = new MyObject;
objMyObject = Item.FirstOrDefault();
and then go about setting objMyObject properties.
The first part using FirstOrDefault is actually in the production, I am trying to find out if that's the right way.
Regards.
will calling FirstOrDefault always loop through the result set
returned by the query?
FirstOrDefault() never loops through all result set - it either returns first item, or default if set is empty. Also in this particular case even enumerator will not be created - thus you are calling FirstOrDefault() on variable of List<T> type, simply item at index 0 will be returned (if list is not empty). If you will investigate Enumerable.FirstOrDefault() implementation:
IList<TSource> list = source as IList<TSource>;
if (list != null)
{
if (list.Count > 0)
{
return list[0];
}
}
But this will be invoked each time you are calling FirstOrDefault().
Also you are missing 'default' case. If you are chaining methods like in your first sample, you can get NullReferenceException if list is empty. So, make sure something was returned by query:
var item = Item.FirstOrDefault();
if (item == null)
return; // or throw
item.Qty = qtyToUpdate;
var uitem = UItem.FirstOrDefault();
if (uitem == null)
return; // or throw
item.TotalPrice = uitem.Qty * uitem.Price;
One more note - you have little difference in performance if you are performing FirstOrDefault() on in-memory collection. But difference will be huge if you will perform it without saving query results into list. In that case each FirstOrDefault() call will cause new database query.

Checking for null value in c# var with LINQ & Entity framework

I'm quite new to LINQ & Entity framework as well as the var keyword in c# so please pardon me if this sounds like a 'newbie' question.
I have problems checking for null values after doing something like this:
var entry = myDB.Entries.Where(e => e.Email == entry.Email);
Even when the email does not exist in the database, entry does not equate to null.
So instead of if (entry == null) i had to do if (entry.Count() < 1) to check for existing Entry before i execute my next batch of statements. Is there any reason why the variable wouldn't be considered null?
In your example, entry will never be null. What you think of as null is in fact an IEnumerable<Entry> with no items.
If you want to check if there is at least one entry with your criteria, you normally do something like:
var entries = myDB.Entries.Where(e => e.Email == entry.Email);
if (entries.Any()) {
// ...
}
If you know that there will be at most one entry, then you can also do:
var entry = myDB.Entries.Where(e => e.Email == entry.Email).SingleOrDefault();
if (entry != null) {
// ...
}
This is closer to what you imagined, but will throw an exception if there is more than one matching entry.
"var" keywords make it possible to achieve any type based on the assignment at runtime, so when you query using "Where" the var entry becomes "IEnumerable" that is returned by Where, that's why you have to check for count.
In VB
Dim entry = myDB.Entries.Where(Function(e) e.Email = entry.Email).SingleOrDefault()
If entry IsNot Nothing Then
' we have a value
else
' we dont have a value
End If

Categories