如何对对象集合和每个对象内部的集合运行LINQ查询

时间:2014-11-14 21:54:37

标签: c# linq

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

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

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

我知道我可以用几行代码来完成这个,就像这样:

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

是否可以在一行中完成此操作?

2 个答案:

答案 0 :(得分: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();

答案 1 :(得分:1)

  

是否可以在一行中完成。

我相信您可以通过将支票结合到第二个销售数组中来将您的代码提炼为更少的代码,例如

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是string.Empty,如果没有找到值或者具有要使用的实际值。


LinqPad中的整个测试项目,使用1.5模拟失败。使用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; }
}