LINQ not evaluating check for null object - c#

I'm wondering why my linq statement doesn't properly evaluate the null check on the Agency object which is a property of the Users model.
var invalidUsers = this.DbContext.Users.Where(p => p.Agency != null).ToList();
var invalidUsersList = invalidUsers.Where(p => p.Agency != null).ToList();
When I run the code above, the first line returns a list of all Users, regardless of the Agency object being null or not. However, the second line executes and properly filters the list and returns a list that is correctly returning Users where Agency is not null.

Chances are since this appears to be a foreign key table you need to include it first in LINQ so it can query against it.
So something like.
var invalidUsers = await this.DbContext.Users
.Include(p => p.Agency)
.Where(p => p.Agency != null)
.ToListAsync();
Give that a try and see if it helps.

Related

C# Query - Non Static Method Requires a Target

i am getting an error that says 'Non Static Method requires a target'
Here is the code that is causing me the error, could anyone possible shed some light on this?
//TODO: Error, Non static method requires a target.
var orderItem =
_context.PurchaseOrderItems.FirstOrDefault(
p => p.JobReference == item.JobReference && p.ItemNumber == item.ItemNumber);
return _context.DeliverySchedules.Include(d => d.PurchaseOrderItem)
.Where(d => d.PurchaseOrderItem.Id == orderItem.Id)
.ToList();
The FirstOrDefault method may return null value if no query results returned there:
var orderItem = _context.PurchaseOrderItems.FirstOrDefault(
p => p.JobReference == item.JobReference && p.ItemNumber == item.ItemNumber);
Since orderItem.Id throws NullReferenceException when orderItem is null, it will propagate to LINQ throwing TargetException as mentioned in question (see this post and this post for more info).
Hence, you need to check presence of null value from orderItem by modifying second LINQ query to this one:
return _context.DeliverySchedules.Include(d => d.PurchaseOrderItem)
.Where(d => (orderItem != null && d.PurchaseOrderItem.Id == orderItem.Id))
.ToList();
NB: Null checking must takes place before retrieving property Id of orderItem to prevent NullReferenceException.
As an alternative, if condition to check against null value may be used without modifying second query:
if (orderItem != null)
{
return _context.DeliverySchedules.Include(d => d.PurchaseOrderItem)
.Where(d => d.PurchaseOrderItem.Id == orderItem.Id)
.ToList();
}
Change the FirstOrDefault to Single because in the next line you will get to its properties and you dont want a NullReferenceException

ASP.NET Entity FrameWork remove() + ToList()

There is a list of managers (in fact a history), where current is marked as property with TillDate == null and IsIsCurrentManager = true.
db.MyManagers.Remove(db.MyManagers
.Where(e => e.ProjectId == projectId
&& e.MyManagerId == Id).First());
var newCurrentManager = db.MyManagers
.Where(e => e.ProjectId == projectId)
.OrderByDescending(i => i.FromDate)
.FirstOrDefault();
newCurrentManager.TillDate = null;
newCurrentManager.IsCurrentManager = true;
db.SaveChanges();
The problem is that Remove() method will only mark entity as deleted, but, as far as i can see, it still will be added to the list. How can deleted 1 record and than build a list of managers without it, without using SaveChanges() 2 times or hardcoding MyManagerList[2]
It's important to remember that Entity Framework is just an interface into your data store and all you are usually doing is building queries, queries that don't actually run until you materialise the results of a select (i.e. by enumerating through the results or calling something like ToList() or Single()) or, in the case of an update/delete operation, when you call SaveChanges().
So while you can do something like checking that the entity is tracked in the context as a deleted item, you are probably much better off sending the results to the database by calling SaveChanges() twice.
The secondary benefit of that is that another query that doesn't use the same context object (e.g. another web request) will be able to see that the item has been deleted while the original query is trying to pull the list of managers.
You can query the Entity State to exclude it from the second list.
I also have updated the linq request with some cleaning.
Also there is no chek for null, your db query can return null and should be managed as such.
db.MyManagers.Remove(db.MyManagers
.FirstOrDefault(e => e.ProjectId == projectId
&& e.MyManagerId == Id));
var newCurrentManager = db.MyManagers
.ToList()
.Where(e => e.ProjectId == projectId
&& db.Entry(e).State != EntityState.Deleted)//This will query the entity traker
.OrderByDescending(i => i.FromDate)
.FirstOrDefault();
newCurrentManager.TillDate = null;
newCurrentManager.IsCurrentManager = true;
db.SaveChanges();

LINQ/lambda: How can I query a DB table based on information from another table? (many to many relationship)

I have a database scheme with 3 tables. One for requisitions, one for hospitals, and one joining the two (many-to-many relationship).
I'd like to list all requisitions in the database that are linked to a selected hospital.
This is what I have so far:
var valgtSykehus = Db.Sykehus.Where(n => n.Navn == sykehus).Single(); //this gives me a variable with my current hospital. I want to list all requistions that contains this.
var Rekvisisjoner = Db.Rekvisisjoner
.Where(r => r.Arkivert == true) //get only archived requsitions
.Include(p1 => p1.Sykehus) //include hospitals
.ToList() //this generates a list of -all- requisitions with the hospitals they are attached to.
.Where(x => x.Created > DateTime.Now.AddYears(-3)) /only go 3 years back
.Where(x => x.Sykehus.Contains(valgtSykehus)); //here is the problem. I want to discard all requisitions that does NOT contain the hospital in the valgtSykehus variable
Anyway, this gives me zero requistions, but if I skip the last line, I get all archived requistions.
x.Sykehus.Contains(valgtSykehus) executes in LINQ to Objects context (due to the intermediate ToList call) and most likely uses reference equality, which normally should work as soon as you use tracking queries.
Still, it's safer and also more efficient to do the whole thing with a single db query using Any condition with primitive key. Something like this:
var Rekvisisjoner = Db.Rekvisisjoner
.Include(r => r.Sykehus) //include hospitals
.Where(r => r.Arkivert == true) //get only archived requsitions the hospitals they are attached to.
.Where(r => r.Created > DateTime.Now.AddYears(-3)) /only go 3 years back
.Where(r => r.Sykehus.Any(s => s.Navn == sykehus));
If there is an issues with using DateTime.Now.AddYears(-3) inside the query, just put into variable outside of the query and use it inside.
var minDate = DateTime.Now.AddYears(-3);
var Rekvisisjoner =
// ...
.Where(r => r.Created > minDate)
//...
The issue may lie in the implementation of Contains. Contains has to check equality somehow. Anyway, if your valgtSykehus object is logically contained in x.Sykehus (i.e. has the same data), but not exactly the same object (i.e. the same reference), it's possible that Contains fails to find it, due to the default implementation of == in reference types (== is true, if the objects are exactly the same reference, false otherwise, even though all the data is the same).
You could try the following:
var Rekvisisjoner = Db.Rekvisisjoner
.Where(r => r.Arkivert == true)
.Include(p1 => p1.Sykehus)
.ToList()
.Where(x => x.Created > DateTime.Now.AddYears(-3))
.Where(x => x.Sykehus.Any(sh => sh.Id == valgtSykehus.Id));
If Id (or whatever your ID property is named) is a value field (most likely) this will return true whenever the ID of an Sykehus matches the ID of valgtSykehus.
Oh my.
I just realised that none of the archived requisitions contains any connections to the hospitals, as they apparently are removed one-by-one when the requisition is processed in the program.
I figured this out while trying to reverse the query, so thanks for that tip.

Comparing a nullable column throws "Unable to cast the type..." exception

My entity NewsItem has a nullable foreign key property: LibraryID of type int?.
My issue is when I query the property and compare it with any value except null, I get exceptions.
Initially my code was:
int? lid = ...
var results = context.NewsItems
.Where(n => n.LibraryID == lid);
but it gives me no results at all, no matter what lid is.
So, I tried:
var results = context.NewsItems
.Where(n => n.LibraryID.Equals(lid));
gives exception:
Unable to create a constant value of type 'System.Object'. Only primitive types or enumeration types are supported in this context.
and then I tried:
var results = context.NewsItems
.Where(n => lid.Equals(n.LibraryID));
and got:
Unable to cast the type 'System.Nullable`1' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.
and this:
var results = context.NewsItems
.Where(n => object.Equals(lid, n.LibraryID));
gives same exception as the last one.
Now I was desperate, so I tried to complicate stuff (like other forums suggested, for example here):
var results = context.NewsItems
.Where(n => (lid == null ? n.LibraryID == null : n.LibraryID == lid));
but still getting same exception.
So... any SIMPLE workarounds?
How about
var results = context.NewsItems
.Where(n => lid.HasValue ? lid.Value == n.LibraryId.Value : (!n.LibraryId.HasValue) );
Hmm, that first snippet should work. I've used nullables like that many times. First thing I'd do is a sanity check just to make sure LibraryID is really int? and not long? or similar.
Other than that, you can try this:
var results = context.NewsItems
.Where(n => (lid.HasValue ? n.LibraryID == lid.Value : !n.LibraryID.HasValue));
Or to avoid the ?: within the query:
var results = lid.HasValue
? context.NewsItems.Where(n => n.LibraryID == lid.Value)
: context.NewsItems.Where(n => !n.LibraryID.HasValue);
It seems that EF does not find the correct operator overload. Therefore it produces wrong results if you set lid = null.
Use linq to objects by adding AsEnumerable() to your query and everything is fine:
var results = context.NewsItems.AsEnumeryble().Where(n => n.LibraryID == lid);
According to the MSDN docs (which I finally found), .Where() will only filter your collection. If you want to see if there are actually results, resolve by lazily executing the filtered query with .ToList(), GetEnumerator, or enumerating the collection with foreach;
This method is implemented by using deferred execution. The immediate
return value is an object that stores all the information that is
required to perform the action. The query represented by this method
is not executed until the object is enumerated either by calling its
GetEnumerator method directly or by using foreach in Visual C# or For
Each in Visual Basic.
http://msdn.microsoft.com/en-us/library/bb534803.aspx
int? lid = ...
var results = context.NewsItems
.Where(n => n.LibraryID == lid).ToList();
var results = context.NewsItems
.Where(n => n.LibraryID.HasValue && n.LibraryID.Value == lid.Value );
edit:
Previous filter was based on my understanding that you wanted to filter to entires having a particular value. Updated will filter to null or value.
var results = context.NewsItems
.Where(n => !n.LibraryID.HasValue || n.LibraryID.Value == lid.Value );

lambda expression trying to query one list based on another

After some advice on how to do this nicely using lambda expressions.
What I want is to get a list of placements based on an agency. I want the placements where the placement.agency != agency but where placement.agencypersonnel contains any of the agency staff.
So where the placement is not for that agency, but there are staff from that agency involved in another agency's placement
I don't know how to query based on the second condition.
So something like:
// agency is being passed in
var agencySupervisors = agency.AgencyPersonnel;
return agency.Placements
.Where(p => p.Supervisors.Contains(agencySupervisors))
.Where(p => p.Agency != agency);
I get that Contains is supposed to refer to a single object rather than a collection - which is why its erroring.. but I'm not sure how to get it to check against all objects in the collection.
Have also tried Any
return agency.Placements
.Where(p => agencySupervisors.Any<PlacementSupervisor>(p.Supervisors))
.Where(p => p.Agency != agency);
So hopefully its just I'm using the wrong one!!
Another spanner in the works is trying to figure out how the placement supervisor and the agency personnel entities relate to one another.. I think its linked on AgencyPersonnelId = SupervisorId so I'm guessing that will also have to be factored into my expression.
Thanks!
Edit: How do I handle if the type of objects in the two list aren't the same - but I know that the Id will match. Do I have to write a comparer and somehow incorporate that into the expression?? ie. AgencyPersonnelId = SupervisorId
I have tried:
return placements
.Where(p => p.Supervisors.Any(supervisor => agencySupervisors.Any(ap => ap.AgencyPersonnelId == supervisor.SupervisorId)));
But it is giving me no results so it is obviously wrong.
Edit: Actually when I try to iterate through the placements in the returned collection I'm getting a null reference exception - so I'm not sure if its something to do with my expression or the way I'm returning the results.
You are close with Any & Contains - try both at once
return agency.Placements
.Where(p => agencySupervisors.Any(supervisor => p.Supervisors.Contains(supervisor))
.Where(p => p.Agency != agency);
I think you can do it with .Intersect also:
return agency.Placements
.Where(p => agencySupervisors.Intersect(p.Supervisors).Any()
&& p.Agency != agency);
Thanks everyone for the help - Because the objects were of different types I ended up having to do something a little different - but then found I was able to use their Ids for the comparison so the result was:
var agencySupervisors = (from ap in agency.AgencyPersonnel
where ap != null
select ap.AgencyPersonnelId).ToList();
return
(from p in m_PlacementRepository.Linq orderby p.PlacementId select p)
.Where(p => p.Agency != agency)
.Where(p => p.Supervisors != null && p.Supervisors.Any(s => agencySupervisors.Contains(s.SupervisorId)));
Plus as Mikael rightly pointed out I was starting with the wrong collection in the first place :)

Categories

Resources