Reputation: 2687
I have the following tables mapped with Entity Framework:
Invoices (InvoiceID, InvoiceNumber, IsPaid)
Parts (PartID, PartNumber, OrganisationID, IsActive)
Invoices_Parts (InvoiceID, PartID, Quantity, IsClaimed)
I'm trying to get a List
of InvoiceIDs from the Invoices_Parts
table with a number of conditions. The condition I'm having a problem with is "where any Part
from Invoices_Parts
is active".
The code so far:
IQueryable<int> partIds = db.Parts.Where(x =>
x.OrganisationID == loggedInUser.OrganisationID).Select(y => y.PartID);
// && x.IsActive
List<string> invoiceIds = db.Invoices_Parts.Where(x =>
!x.IsClaimed && x.Invoice.IsPaid && partIds.Contains(x.PartID)
.DistinctBy(y => y.InvoiceID)
.Select(z => z.InvoiceID.ToString()).ToList();
I've commented out "&& x.IsActive" because I want the list of InvoiceIDs to be such that at least one part must meet the IsActive condition - I don't want to filter out all the parts where IsActive is false. How do I achieve this in LINQ without manually looping through collections and adding/removing items?
Note: In case anyone is wondering, I'm using the following helper method for DistinctBy
:
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
Edit: I have the following properties on each entity:
Invoice:
public virtual ICollection<Invoices_Parts> Invoices_Parts { get; set; }
Part:
public virtual ICollection<Invoices_Parts> Invoices_Parts { get; set; }
Invoices_Parts:
public virtual Invoice Invoice { get; set; }
public virtual Part Part { get; set; }
Upvotes: 2
Views: 215
Reputation: 156524
People often overcomplicate LINQ queries because they're so used to thinking in terms of SQL tables, joins, and such. Usually if you put your actual requirement in plain english, you can come up with a simpler query that reads almost exactly the same.
I want a list of invoice IDs from the paid invoices where any part on that invoice is active, and ... etc.
How about this:
from invoice in db.Invoices
where invoice.IsPaid
where invoice.Invoices_Parts.Any(ip => !ip.IsClaimed &&
ip.Part.IsActive &&
ip.OrganisationID == loggedInUser.OrganisationID)
select invoice.InvoiceId
Or if you prefer method syntax:
db.Invoices.Where(i => i.IsPaid)
.Where(i => i.Invoices_Parts.Any(ip => !ip.IsClaimed &&
ip.Part.IsActive &&
ip.OrganisationID == loggedInUser.OrganisationID)
.Select(i => i.InvoiceId)
.ToList();
PS--you can do a .ToString()
if you want to, but in my experience it's wiser to leave IDs strongly typed.
PPS--you can make a DistinctBy method that will play nicely with Entity Framework (not forcing premature evaluation) like so:
public static IQueryable<TSource> DistinctBy<TSource, TKey>(this IQueryable<TSource> source, Excpression<Func<TSource, TKey>> keySelector) {
return source.GroupBy(keySelector).Select(i => i.FirstOrDefault());
}
Upvotes: 5
Reputation: 13965
OK, assuming that you want all the invoices where at least one part ON THAT INVOICE is active, I'd do it like this:
IQueryable<int> partIds = db.Parts
.Where(x => x.OrganisationID == loggedInUser.OrganisationID && x.IsActive)
.Select(y => y.PartID);
List<string> invoiceIds = db.Invoices_Parts
.Where(x => !x.IsClaimed && x.Invoice.IsPaid && partIds.Contains(x.PartID))
.Select(y => y.InvoiceID.ToString())
.Distinct()
.ToList();
If that looks a lot like what you have, it's because it is. As I understood the question, you were already 99.99% there.
Upvotes: 1
Reputation: 203829
What you can do here is join the invoices with the parts to create a collection of all of the parts for each invoice. Once you have that collection determining if any item in it is active is easy enough:
List<string> invoiceIds =
(from invoice in db.Invoices_Parts
where !invoice.IsClaimed && invoice.Invoice.IsPaid
join part in partIds
on invoice.PartId equals part into parts
where parts.Any(part => part.IsActive)
select invoice.InvoiceID)
.Distinct()
.ToList();
Upvotes: 0