复杂的LINQ“任何”查询

时间:2014-01-30 18:04:22

标签: c# linq entity-framework

我有以下表格与实体框架映射:

发票(InvoiceID,InvoiceNumber,IsPaid)
零件(PartID,PartNumber,OrganisationID,IsActive)
Invoices_Parts(InvoiceID,PartID,Quantity,IsClaimed)

我正试图从List表中获取Invoices_Parts个InvoiceID,其中包含许多条件。我遇到问题的条件是“{em>来自Part的任何 Invoices_Parts处于有效状态”。

到目前为止的代码:

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();

我已经注释掉了“&amp;&amp; x.IsActive”,因为我希望InvoiceID列表至少有一部分必须满足IsActive条件 - 我不想过滤掉所有部分IsActive是假的。如何在LINQ中实现此功能而无需手动循环收集和添加/删除项目?

注意:如果有人想知道,我正在使用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;
        }
    }
}

修改:我在每个实体上都有以下属性:

发票:

public virtual ICollection<Invoices_Parts> Invoices_Parts { get; set; }

部分:

public virtual ICollection<Invoices_Parts> Invoices_Parts { get; set; }

Invoices_Parts:

public virtual Invoice Invoice { get; set; }
public virtual Part Part { get; set; }

3 个答案:

答案 0 :(得分:5)

人们经常使LINQ查询过于复杂,因为他们习惯于在SQL表,连接等方面进行思考。通常,如果您将实际需求放在简​​明的英语中,您可以提出一个更简单的查询,它几乎完全相同。

  

我想要一张付款发票中的发票ID列表,其中该发票上的任何部分都是有效的,等等。

这个怎么样:

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

或者如果您更喜欢方法语法:

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 - 如果您愿意,可以.ToString(),但根据我的经验,保留ID强类型更明智。

PPS - 您可以创建一个与Entity Framework完美匹配的DistinctBy方法(不强制过早评估),如下所示:

public static IQueryable<TSource> DistinctBy<TSource, TKey>(this IQueryable<TSource> source, Excpression<Func<TSource, TKey>> keySelector) {
    return source.GroupBy(keySelector).Select(i => i.FirstOrDefault());
}

答案 1 :(得分:1)

好的,假设您想要至少有一部分ON THAT INVOICE活动的所有发票,我会这样做:

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();

如果它看起来很像你拥有的那样,那是因为它是。据我所知,你已经99.99%了。

答案 2 :(得分:0)

您可以在此处将发票与零件合并,以便为每张发票创建所有零件的集合。一旦你有了该集合,确定其中的任何项目是否有效是很容易的:

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();