【问题标题】:Lambda and Nested ObjectsLambda 和嵌套对象
【发布时间】:2011-01-26 04:33:24
【问题描述】:

我查看了this 的答案,它部分地解决了我的问题。

但是,我需要的是以下内容。

鉴于我有一个对象;

Product
  string code
  List<suitability> items

然后我就有了这个对象;

Suitability
  key
  value

每个产品都有可变数量的 [items],并且键/值对因产品而异。

搜索时,我会得到一个适合性对象的列表。我现在需要搜索包含(所有)提供的适用性对象的所有产品。

例如,一个产品可能有牙科 = true 和治疗 = true。

我可能会收到对所有牙科 = true 和治疗 = false 的产品的请求。

【问题讨论】:

    标签: linq lambda


    【解决方案1】:

    首先,我想指出,键值对(AKA EAV 模型)是表示此类数据的一个糟糕选择,而这个问题就是一个完美的例子,说明了为什么 - 要做到这一点要困难得多搜索任意属性集合比搜索特定属性。

    当然,还是可以的:

    var suitableProducts =
        from p in products
        where
            p.Items.Any(s => s.Key == "dental" && s.Value == "true") &&
            p.Items.Any(s => s.Key == "therapies" && s.Value == "false")
        select p;
    

    这并不像查询实际上具有 dentaltherapies 属性而不是嵌套属性的 Product 类那样容易编写或高效。

    如果您需要查询的项目数量可以改变,那么最简单的处理方法是将过滤器链接在一起:

    var searchItems = ...
    var result = products;
    foreach (var searchItem in searchItems)
    {
        result = result.Where(p =>
            p.Items.Any(s => s.Key == searchItem.Key &&
                             s.Value == searchItem.Value));
    }
    // Do something with result
    

    如果您正在寻找一种更“实用”的方式来做到这一点而无需链接,那么:

    var suitabilityConditions = searchItems.Select(i =>
        (Predicate<Product>)(p => p.Items.Any(s => 
            s.Key == searchItem.Key && s.Value == searchItem.Value)));
    Predicate<Product> match = p => suitabilityConditions.All(c => c(p));
    var suitableProducts = products.Where(match);
    

    【讨论】:

    • @Aaronaught,谢谢你,但我不能像你那样编码每一行。我可能会得到 2 件或 10 件物品。至于方法 (EAV),您会建议哪种模型,每个产品都附有可变数量的物品?
    • @griegs:添加了动态数量项目的版本。至于我的建议——我建议不要设计一种每个产品都可以具有任意属性集的设计。此类设计是反模式,可能有时是必要的,但通常是模糊要求和通过最终用户定制降低维护成本的愿望相结合的结果。知道由于这样的无数问题,它实际上会提高 TCO(另一个问题是属性是弱类型的 - 如果你想排序会发生什么?);更好地解决模糊的要求。
    • 我听到你的声音并讨论过这个问题,但老板指出,我们需要一种方法来更改与产品相关的项目数量,以适应​​企业每月的想法变化。
    • 祝你好运。不幸的是,他很可能会通过艰难的方式了解到维护这样一个设计的成本要比简单地投入一些开发资源来实现上述业务变化时的成本要高出多少。
    【解决方案2】:

    使用PredicateBuilder(来自 LinqPad 的作者),您可以根据一组编译时未知的条件构建查询。以下内容就足够了:

    var predicate = PredicateBuilder.True<Product>();
    
    foreach (Suitability criteria in searchCriteria)
    {
        string tempKey = criteria.Key;
        string tempValue = criteria.Value;
        predicate = predicate.And(p => 
                         p.Items.Any(s => s.Key == tempKey && s.Value == tempValue));
    }
    
    return dataContext.Products.Where(predicate.Compile());
    

    更新:这是我测试过的一些示例代码,它使用 IEnumerable 作为源,结果集 productsResult 包含 products 列表中的第一个和第二个产品:

    var searchCriteria = new List<Suitability>()
        {
            new Suitability() { Key="a", Value="b" },
            new Suitability() { Key="a", Value="c" }
        };
    
    var products = new List<Product>()
        {
            new Product()
                {
                    Items = new List<Suitability>() {
                                new Suitability() { Key="a", Value="b" },
                                new Suitability() { Key="a", Value="c" }}
                },
            new Product()
                {
                    Items = new List<Suitability>() {
                                new Suitability() { Key="a", Value="b" },
                                new Suitability() { Key="a", Value="c" },
                                new Suitability() { Key="b", Value="c" }}
                },
            new Product()
                {
                    Items = new List<Suitability>() {
                                new Suitability() { Key="c", Value="d" }}
                }
        };
    
        var predicate = PredicateBuilder.True<Product>();
    
        foreach (Suitability criteria in searchCriteria)
        {
            string tempKey = criteria.Key;
            string tempValue = criteria.Value;
            predicate = predicate.And(p => p.Items.Any(
                             s => s.Key == tempKey && s.Value == tempValue));
        }
    
        IEnumerable<Product> productsResult = products.Where(predicate.Compile());
    

    【讨论】:

    • @Simon,我尝试实现这个,但后来我记得我没有数据上下文,因为这不是 LINQ to SQL,当我将它应用到我的 IEnumerable 列表时出现错误。错误无法从用法中推断方法“System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)”的类型参数。尝试明确指定类型参数。
    • @griegs 添加 .Compile() 以将 Func 从表达式中取出 predicate...我已经更新了我的答案。
    • 我改变了主意,选择了这个作为首选答案。工作请客谢谢。我仍然必须同意@Aaronaught 的观点,即使用 EAV 模型的方法并不理想,但是当你别无选择时......
    • @griegs 谢谢!!我没有争论 EAV 模型不是最优的,但是当你被锁定并且别无选择时,我绝对更喜欢使用 PredicateBuilder 获得的简单性
    猜你喜欢
    • 2019-12-09
    • 2013-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-01
    • 1970-01-01
    • 2023-04-01
    相关资源
    最近更新 更多