【问题标题】:Complex LINQ 'Any' query复杂的 LINQ“任何”查询
【发布时间】:2014-02-23 05:00:24
【问题描述】:

我用实体框架映射了以下表格:

发票(InvoiceID、InvoiceNumber、IsPaid)
部件(PartID、PartNumber、OrganisationID、IsActive)
Invoices_Parts(InvoiceID、PartID、Quantity、IsClaimed)

我正在尝试从具有多个条件的 Invoices_Parts 表中获取 List 的 InvoiceID。我遇到问题的条件是“Invoices_Parts 中的 any Part 处于活动状态”。

到目前为止的代码:

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

我已注释掉“&& 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; }

【问题讨论】:

  • 您的实体中有一些导航属性吗?
  • 您可能知道这一点,但我应该指出,当您的 IQueryable 到达您的 DistinctBy 扩展中的 foreach 时,它会触发。我想说,为了清楚起见,您应该在链中的 DistinctBy() 调用之前放置一个 ToList(),以明确这是查询评估的位置。
  • @GeorgeMauer 添加ToList 不必要地立即评估整个查询,强制评估整个结果集,并具有创建/填充将被完全忽略的列表的开销。 AsEnumerable 无需任何额外开销即可实现所需的可读性语义。
  • 实际上,在我看来,如果你把IsActive 测试放回去,你就会得到你想要的。您正在寻找 InvoiceID 值,因此将 InvoiceID 从所有 Invoice_Parts 记录中拉出,其中 IsActive 为真,其他情况为真,您应该会得到正确的结果。
  • 是的,除了DistinctBy 扩展将已经强制触发查询。我特别反对的是它看起来不会(因为Distinct() 是在LINQ 提供程序中实现的),但它实际上会(因为DistinctBy 不是)。所以我想也许更好的建议是将其重命名为EvaluateAndDistinctBy

标签: c# linq entity-framework


【解决方案1】:

人们经常使 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——你可以创建一个 DistinctBy 方法,它可以很好地与 Entity Framework 配合使用(不强制过早评估),如下所示:

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

【讨论】:

  • 那么假设 EF,嵌套的 .Any 会在这里生成内部连接还是应用交叉?
  • @GeorgeMauer:它通常会执行 WHERE EXISTS 查询,这允许它在找到匹配项后立即短路。
【解决方案2】:

好的,假设您希望所有发票中至少有一部分在该发票上处于活动状态,我会这样做:

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% 在那里。

【讨论】:

  • 我可能没有在问题中正确解释自己。我还使用了任意字段名称,因为实际数据集有更多字段。
【解决方案3】:

您可以在此处将发票与部件连接起来,为每张发票创建所有部件的集合。一旦您拥有该集合,确定其中的任何项目是否处于活动状态就很容易了:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多