简体   繁体   English

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

[英]How to run a LINQ query on a collection of objects and a collection inside of each object

I have a collection of objects where each object also has a collection. 我有一个对象集合,其中每个对象也都有一个集合。 Like so: 像这样:

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

I want to run a LINQ query to check if a Product entity exists and, if it does exist, check it's Sales collection to see if a specific string value (from the Tuple) also exists. 我想运行LINQ查询来检查Product实体是否存在,如果确实存在,则检查它的Sales集合以查看是否还存在特定的字符串值(来自Tuple)。 If it does, I want to return the corresponding double (also from the Tuple). 如果是这样,我想返回相应的double(也从Tuple中)。

I know I can do this in a few lines of code, like so: 我知道我可以用几行代码来做到这一点,就像这样:

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

Is it possible to do this in one line? 可以一行完成此操作吗?

The key here is to not actually materialize your query through the use of SingleOrDefault until you're actually done defining the entirety of it. 关键是要在定义完整体之后才真正通过SingleOrDefault查询。 Use Where instead and then use SingleOrDefault at the very end. 改为使用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();

Is it possible to do it in one line. 是否可以在一行中完成。

I believe you can distill your code to less lines by combining the check into the second sales array such as 我相信您可以通过将支票合并到第二个销售数组中来将代码提炼到更少的行,例如

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;

Using a Default Value 使用默认值

This solution uses the help from a default faux pre-created Product to be used when one is not found. 此解决方案使用默认的人造预创建Product的帮助,如果找不到该Product ,则使用该Product Using it in the extension method DefaultIfEmpty , that method determines if a empty projection has been returned and in that case it will instead return the faux instance. 在扩展方法DefaultIfEmpty中使用它,该方法确定是否已返回空投影,在这种情况下,它将代替返回人造实例。 After that we can safely extract a the value which would be string.empty and assign it to the final string productSale . 之后,我们可以安全地提取一个值为string.empty的值,并将其分配给最终的字符串productSale

Below I use a hardcoded 1.5 as the sale price for easier reading of the example. 下面,为了便于阅读示例,我使用了硬编码的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 is string.Empty if no value found or has the actual value to use. productSale是string.Empty,如果找不到值或具有要使用的实际值。


Whole test project in LinqPad which simulates a fail by using 1.5. LinqPad中的整个测试项目,使用1.5模拟失败。 Use 1.6 to show success. 使用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; }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM