简体   繁体   中英

LINQ query - join and where clause not being obeyed?

I have a link query with a simple join to a couple of tables, and a where clause, for some odd reason the query doesn't seem to be obeying the where clause.

My query is as below:

from p in Platforms
 join c in Compartments on p.Id equals c.PlatformId
 join ci in CompartmentItems on c.Id equals ci.CompartmentId     
 where p.Id == 4042 && !ci.Archived && !c.Archived 
 select c

And a simple linqpad screnshot shows that the CompartmentItems are returned attached to the Compartments but irrespective of whether they are archived or not.

在此处输入图片说明

It feels like I'm being dense here, can anyone help me identify the issue with the query?

Any thoughts greatly appreciated.

EDIT: So I went back and looked at this in more detail and established why I had only returned the compartments. It's due to a repository which only typically returns domain types for consumption. As a result, I can't return more than one entity type without introducing a single random DTO type object (none of the other repos return anything other than domain types).

So with this in mind I have 4 options:

  1. Create a random DTO just for this one purpose
  2. Query the db twice and get the compartment items as a second query
  3. Return an IQueryable from the repository (again, this is not done anywhere else)
  4. Return the compartments irrespective of archived compartmentitems and filter them out in the consuming method in memory.

Any thoughts on the best approach here?

You're taking your compartment, joining it with the compartment items, filtering out some compartment items, then ignoring all of the compartment items you joined the table with entirely by only selecting out the compartments, and then using an entirely different mechanism to get all compartment items for each compartment, namely using the built in relationship property.

To get only the compartment items matching your given filter you need to actually select out the compartment items you filtered using your select clause, and then not use Include to include them all. This might look something like:

from p in Platforms
join c in Compartments on p.Id equals c.PlatformId
join ci in CompartmentItems.Where(ci => !ci.Archived) 
on c.Id equals ci.CompartmentId into compItems     
where p.Id == 4042 && !c.Archived 
select new 
{
    Compartment = c,
    CompartmentItems = compItems,
}

Look at the SQL query itself. I'm sure it obeys your filters. I bet you have lazy loading on, that's why you're seeing all the CompartmentItems.

Or if your not using SQL backend, you must have a relationship between Compartment and CompartmentItems. When you use the navigation property it'll return all the compartmentitems.

There is no bug here.

Try create a Compartment with all CompartmentItems.Archived = 1. You'll see this compartment is not going to be returned by your statement.

Try doing it like this.

    from p in Platforms
 join c in Compartments on p.Id equals c.PlatformId
 join ci in CompartmentItems on c.Id equals ci.CompartmentId     
 where p.Id == 4042
 where !ci.Archived
 where !c.Archived
 select c

It gets you around the issue of link trying to query the same record for the property. When the logic is separated by && it seems like it is read to the machine as

p.Id == 4042 && !p.Archived && !p.Archived

Which is the result of LINQ applying the property filters using only the first item in the list regardless of the difference of the remaining items.

Linq Join with into clause and where condition

var li = (from cert in db.tbl_ISOCetificate
            join comCert in db.tbl_ComGroupCertificate.Where(x=>x.GroupID ==GroupID) on cert.Cert_id equals comCert.CerID  into jo 
             from b in jo.DefaultIfEmpty()
            select new { cert.Cert_id, cert.Cert_Name}).ToList();  

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM