【问题标题】:How to run a LINQ query on a collection of objects and a collection inside of each object如何对对象集合和每个对象内部的集合运行 LINQ 查询
【发布时间】:2014-11-16 03:42:55
【问题描述】:

我有一个对象集合,其中每个对象也有一个集合。像这样:

public class Product
{
    public int Id { get; set; }
    public List<Tuple<string, double>> Sales { get; set; }
}

我想运行 LINQ 查询来检查 Product 实体是否存在,如果确实存在,请检查它的 Sales 集合以查看特定字符串值(来自元组)是否也存在。如果是这样,我想返回相应的双精度(也来自元组)。

我知道我可以用几行代码做到这一点,如下所示:

saleAmount = String.Empty;                      
product = Model.Products.SingleOrDefault(p => p.Id == product.Id);
if(product != null)
{
    productSale = product.Sales.SingleOrDefault(i => i.Item1 == sale.Id);
    if(productSale != null)
    {
        saleAmount = productSale.Item2.ToString();
    }
}

是否可以在一行中做到这一点?

【问题讨论】:

  • 您将看到NullReferenceExceptions,我怀疑您的代码会更清晰。
  • 您意识到,我希望,如果列表中的多个项目具有该生产 id,SingleOrDefault 将引发异常。您确定不要在这里FirstOrDefault
  • @JimMischel 鉴于他正在处理主键,它们不应该被复制。如果它们以某种方式存在,那将是数据中的错误,应该抛出。
  • @Servy 如果它们是主键,那么(在这里挑剔)FirstOrDefault 在性能方面更快(因为它不需要检查是否有重复项来抛出异常)。
  • @Servy:我没有看到任何说他正在使用主键。但如果是这样的话,那当然了。

标签: c# linq


【解决方案1】:

这里的关键是在您真正完成对整个查询的定义之前,不要通过使用SingleOrDefault 实际实现您的查询。请改用Where,然后在最后使用SingleOrDefault

var query = (from product in Model.Products
                where product.Id == someProductId
                let sale = product.Sales.SingleOrDefault(i => i.Item1 == sale.Id)
                where sale != null
                select new
                {
                    product,
                    saleAmount = sale.Item2,
                })
            .SingleOrDefault();

【讨论】:

  • 这是一个非常有趣的方法。这可以使用 lambda 表达式吗?
  • @ShaiCohen 这使用 lambda 表达式。
  • @ShaiCohen lambda 表达式是条件表达式,它通过“转到”=&gt; 语法推断特定检查语法。您正在考虑的lambdalinq 表达式,它可以是Servy 所做的query 语法(从技术上讲,他有一个混合)或method 语法我在我的例子中使用过。我们俩都使用lambdas 来表示一个检查操作,比如i =&gt; i.Item1 == sale.Id。请参阅Query Syntax and Method Syntax in LINQ (C#) 了解更多信息。
  • @OmegaMan:感谢您的澄清。就在你认为你知道一些事情的时候.... :)
  • @ShaiCohen 我们都去过那里。很高兴为您提供帮助。
【解决方案2】:

是否可以一行完成。

我相信您可以通过将检查组合到第二个销售数组中来将代码简化为更少的行,例如

var products = Model.Products.Where(p => p.Id == product.Id
                                              &&
                                         p.Sales.Any(i => i.Item1 == sale.Id) );

var saleAmount = (products != null && products.Any()) 
                                   ? products.First().Sales.First().Item2.ToString()
                                   : string.Empty;

使用默认值

此解决方案使用来自默认 faux 的帮助,该帮助来自预先创建的 Product,以便在找不到时使用。在扩展方法DefaultIfEmpty 中使用它,该方法确定是否返回了空投影,在这种情况下,它将返回虚假实例。之后,我们可以安全地提取 string.empty 的值并将其分配给最终字符串 productSale

下面我使用硬编码的1.5 作为销售价格,以便于阅读示例。

// Our default will set saleAmount to string.Empty if nothing is found in Products.
var defProduct = new Product() 
                      { Id    = -1, 
                        Sales = new List<Tuple<string, double>>()
                                  { new Tuple<string,double>(string.Empty, 0.0) }};

var productSale =

Products.Where(p => p.Id == product.Id && p.Sales.Any (s => s.Item2 == 1.5 ) )
        .DefaultIfEmpty( defProduct )
        .First ()
        .Sales.First()
        .Item1;

productSale 为字符串。如果未找到值或具有要使用的实际值,则为空。


使用 1.5 模拟失败的 LinqPad 中的整个测试项目。使用 1.6 显示成功。

void Main()
{

    var targetSalePrice = 1.5;
    var targetProductId = 2;

    var Products = new List<Product>() { new Product()
                                           { Id = 2,
                                             Sales = new List<Tuple<string, double>>()
                                            { new Tuple<string,double>("actual", 1.6) } }
                                        };


// Our default will set saleAmount to string.Empty if nothing is found in Products.
var defProduct = new Product() { Id = -1, Sales = new List<Tuple<string, double>>()
                                  { new Tuple<string,double>("faux string.Empty", 0.0) }};

var productSale =

Products.Where(p => p.Id == targetProductId 
                   && p.Sales.Any (s => s.Item2 == targetSalePrice ) )
        .DefaultIfEmpty( defProduct )
        .First ()
        .Sales.First ()
        .Item1;

    productSale.Dump(); // outputs the string "faux string.Empty" from the faux default.

}

// Define other methods and classes here


public class Product
{
    public int Id { get; set; }
    public List<Tuple<string, double>> Sales { get; set; }
}

【讨论】:

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