Best way to determined null or count 0 - c#

I have a iQueryable and I need to know if it's null or has no values.
IQueryable<people> L = (from p in people
where p.lastname.Contains("y")
select p);
if (L != null && L.Count() > 0) {
return "Something";
} else {
return "NOTHING";
}
Well if you use the L.Count() it will use more resources. Is there a better way? Something that does not use L.Count()

It's recommended that you use .Any().
IQueryable<people> L = (from p in people
where p.lastname.Contains("y")
select p);
if (L.Any()) {
return "Something";
} else {
return "NOTHING";
}

Does L need to be IQueryable<> ?
By using SingleOrDefault() it will be a single people (Person?) or null (presuming people is a class)
var result = (from p in people
where p.lastname.Contains("y")
select p).SingleOrDefault();
return result == null
? "NOTHING"
: "Something";
Other: Is SingleOrDefault() what you want to use or do you mean FirstOrDefault() or do you mean Any()?
Maybe LINQ: When to use SingleOrDefault vs. FirstOrDefault() with filtering criteria can help.
hth,
Alan.

An example of using the .Any method
return people.Any(p => p.lastname.Contains("y")) ? "something" : "nothing";
This is an example that would return an IQueryable if the .Any returns true, however it might be too ineffecient since it requires two round trips to the database. I'm sure a better method could be written, given enough time and thought.
return sis.Students.Any(p => p.LastName.Contains("y")) ?
people.Where(p => p.lastname.Contains("y")) : "nothing";

L.Any(), L.FirstOrDefault() will pretty much both have the same performance as they have almost identical implementation and are probably what you are looking for. Your SingleOrDefault is probably unintentional as it will throw an exception if there is more than one result.
Performance of LINQ Any vs FirstOrDefault != null
It's worth saying some of this depends on your provider. IQueryable just implies an intention. If It's Linq2Sql or something then yes L.Count() will request more resources - except that your also calling SingleOrDefault in the above line which means your null check is all you need, but your types don't match...
If I am running this statement against a Linq provider I write myself (or Amazons, or any other given LINQ provider) .Count() might be faster depending on what the provider is doing, but your assumptions hold if you're using the usual Microsoft Linq to SQL implementations.

Related

Null coalesce not working in LINQ query

Take this:
int? item1 = null;
int? item2 = null;
someObjectList.Where(x => x.SomeItem1 == (item1 ?? x.SomeItem1)
&& x.SomeItem2 == (item2 ?? x.SomeItem2)
);
Where someObjectList is not empty and SomeItem1 and SomeItem2 is null in all the objects in the list.
Why is it returning nothing?
EDIT:
My Code:
public void GetPlacementsByMaterial(long clientMaterialID)
{
ClientMaterial clientMaterial = ((ApplicationEntityModel)NavigationItem.ObjectContext).ClientMaterial.FirstOrDefault(x => x.ClientMaterialID == clientMaterialID);
var list = GetPlacementList(supplier, mediaSpace);
PlacementsList = list.Where(x => x.MediaCategoryFormatID == (clientMaterial.MediaCategoryFormatID ?? x.MediaCategoryFormatID)
&& x.MediaCategorySizeID == (clientMaterial.MediaCategorySizeID ?? x.MediaCategorySizeID)
);
}
All ID's are Nullable<long>.
EDIT:
SQL Profiler:
SELECT *
FROM [dbo].[CampaignSchedulePlacements] AS [Extent5]
WHERE ([Extent5].[MediaCategoryFormatID] = [Extent5].[MediaCategoryFormatID]) AND ([Extent5].[MediaCategorySizeID] = [Extent5].[MediaCategorySizeID])
Note: cleaned up the `SQL.
In SQL, NULL is not equal to NULL.
You can interpret NULL as meaning: "there is value, but I don't know what it is". So if you're comparing two NULL values, you're really asking "is the first unknown value equal to the second unknown value?" Of course, there is no reason to assume they are, so SQL will say "no".
I am assuming that this is causing your problem. You can verify that by looking at the actual SQL produced. If it's using the SQL = operator, this is indeed the problem. You can verify that by running the SQL in a database tool, such as SQL Management Studio in case you're using SQL Server.
UPDATE:
The condition
([Extent5].[MediaCategoryFormatID] = [Extent5].[MediaCategoryFormatID])
will indeed return false when [Extent5].[MediaCategoryFormatID] is NULL.
That answers the question "Why is it returning nothing?"
However, another question come to mind: why would the entity framework generate that SQL from this linq query?
I'm afraid that linq to entities is not exactly known for the quality of its SQL generation, and this case seems to confirm that. You might consider Linq to SQL. Even if that seems to be a dead-end track in the long run, the current implementation if a lot better than linq to entities.
In either case, have you tried something like
someObjectList.Where(x =>
!item1.hasValue ||
x.SomeItem1.HasValue && x.SomeItem1.Value == item1.Value)
Make sure to verify that under the profiler as well though, linq to entities might mess it up too.

Convert LINQ orderby to inplace list sort

Currently, i'm sorting a list using LINQ to objects, then doing a ToList() on the results:
var SortedPossibleMoveLocations = (from PML in PossibleMoveLocations
orderby Randomiser.Next()
orderby IsSameType(PML) ? (_Owner[PML] as TileFlowing).UnitsWithin : 0
orderby PossibleMoveLocationOrdering(PML)
select PML).ToList();
I want to convert this to do an inplace sort, i guess using List<T>.Sort() method. If i was only ordering by one thing i'd know how to do this, however, as i'm ordering by PossibleMoveLocationOrdering (which returns an int) then by IsSameType(PML) ? (_Owner[PML] as TileFlowing).UnitsWithin : 0 which evaluates to an int, then by Randomiser.Next() (which returns a random int) i don't know how to do this.
Question: How do i write the comparison function (or is there a better method) to do an implace sort of the LINQ query above.
To start with, specifying three orderby clauses is a bad idea - instead, specify multiple orderings just by using comma separation.
I'm also not keen on the idea of using Randomiser.Next() for ordering, but that's an aside.
Your LINQ query should look like this (still with Randomiser in for the moment):
var query = (from PML in PossibleMoveLocations
orderby PossibleMoveLocationOrdering(PML),
IsSameType(PML) ? (_Owner[PML] as TileFlowing).UnitsWithin : 0,
Randomiser.Next()
select PML).ToList();
Personally I'd just use dot notation for this:
var query = PossibleMoveLocations
.OrderBy(pml => PossibleMoveLocationOrdering(PML))
.ThenBy(pml => IsSameType(pml) ?
(_Owner[pml] as TileFlowing).UnitsWithin : 0)
.ThenBy(pml => Randomiser.Next())
.ToList();
To sort in place, you basically need a Comparison<T> or IComparer<T> which can test multiple things, and also an implementation which creates a comparer using properties. You can do that manually (as per Marc's code), but as it happens, I have some helper classes and extension methods in MiscUtil:
var comparer = ProjectionComparer<PossibleMove>
.Create(pml => PossibleMoveLocationOrdering(PML));
.ThenBy(pml => IsSameType(pml) ? ...)
.ThenBy(...);
list.Sort(comparer);
Note that using a Randomizer here is definitely a bad idea, as it will be called on each comparison (for objects with equal first parts)... this can lead to an inconsistent comparison such that x < y < z < x.
Most commonly:
list.Sort((x,y) => {
int result = /* first comparison, for example
string.Compare(x.Name, y.Name) */
if (result == 0) result = /* second comparison,
for example x.Id.CompareTo(y.Id) */
...
if (result == 0) result = /* final comparison */
return result;
});
or similar (perhaps in a comparer class, if it is non-trivial).

linq where clause and count result in null exception

The code below works unless p.School.SchoolName turns out to be null, in which case it results in a NullReferenceException.
if (ExistingUsers.Where(p => p.StudentID == item.StaffID &&
p.School.SchoolName == item.SchoolID).Count() > 0)
{
// Do stuff.
}
ExistingUsers is a list of users:
public List<User> ExistingUsers;
Here is the relevant portion of the stacktrace:
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Linq.Enumerable.WhereListIterator1.MoveNext()
at System.Linq.Enumerable.Count[TSource](IEnumerable1 source)
How should I handle this where clause?
Thanks very much in advance.
I suspect p.School is null, not SchoolName. Simply add a null check before accessing SchoolName. Also, use Any() to check if there are any results instead of Count() > 0 unless you're really in need of the count. This performs better since not all items are iterated if any exist.
var result = ExistingUsers.Where(p => p.StudentID == item.StaffID
&& p.School != null
&& p.School.SchoolName == item.SchoolID)
.Any();
if (result) { /* do something */ }
For all database nullable columns, we should either add null check or do simple comparision a == b instead of a.ToLower() == b.ToLower() or similar string operations.
My observation as below:
As they get iterated through Enumerable of LINQ Query for comparision against with input string/value, any null value (of database column) and operations on it would raise exception, but Enumerable becomes NULL, though query is not null.
In the case where you want to get the null value (all the student, with school or not) Use left join.
There are a good example on MSDN
If I remember correctly (not at my developer PC at the moment and can't check with Reflector), using the == operator results in calling the instance implementation string.Equals(string), not the static implementation String.Equals(string, string).
Assuming that your problem is due to SchoolName being null, as you suggest, try this:
if (ExistingUsers.Where(
p => p.StudentID == item.StaffID
&& String.Equals( p.School.SchoolName, item.SchoolID)).Count() > 0)
{
// Do stuff.
}
Of course, comments by other answers count as well:
Using Any() instead of Count() > 0 will generally perform better
If p.School is the null, you'll need an extra check
Hope this helps.

Counting the result LINQ

I am using the following
var validLogin = from P in this.DataContext.Persons
where P.UserName.Equals(login) && P.Password.Equals(password)
select new
{
P.FirstName,
P.LastName,
P.EmailAddress
};
In this now i want to know, is there any result returned by this query? How to do this.
Don't use Count() - use Any() unless you actually care about the count.
You can actually simplify this a lot, because you don't use the rest of the results either:
bool validLogin = DataContext.Persons.Any(p => p.UserName == login &&
p.Password == password);
The nice thing about Any() is that whatever's processing the query can stop as soon as it's found any matching results - it doesn't need to keep looking for other potential matches. (Count() will work of course, it's just not as efficient, mostly because the operator itself isn't describing what you really care about as accurately.)
This should work:
if (validLogin.Count() > 0)
{
//do work
}
if (validLogin.Count() > 0){}

LINQ to Xml not equal to operator

I am using LINQ to XML. I want to use an equivalent of sql's <> operator in the where clause below....
var myBooks = from book in xDoc.Descendants("BOOKOB")
where book.Element("AUTHOR").Value
Please help!
Isn't != working?
As others have said, you can use != perfectly easily - don't forget that even when you're using LINQ, you're writing C#, not SQL.
You need to provide a value for it not to be equal to, of course, along with a select clause:
var myBooks = from book in xDoc.Descendants("BOOKOB")
where book.Element("AUTHOR").Value != "Jeff Atwood"
select book;
For simple queries like this, I usually find "dot notation" simpler to read:
var myBooks = xDoc.Descendants("BOOKOB")
.Where(b => b.Element("AUTHOR").Value != "Jeff Atwood");
You should be able to use != for not equal and == for equal to.

Categories

Resources