编译时具有未知密钥的LINQ复合密钥组Join

本文关键字:密钥 LINQ 复合 Join 编译 未知 | 更新日期: 2023-09-27 18:22:11

我正试图让GroupJoin使用LINQ处理多个未知键。

我见过匿名类型的解决方案,但密钥总是预定义的。在我的情况下,它们是用户定义的,所以我在编译时不会知道这些信息。我尝试使用一个键值列表和一个键值数组,但它们永远不会匹配。

所以。。。这就像一个魅力:

Func<Component, string> getKeyValue = x => x.Attributes                            //from attributes
                                            .Single(a => a.Name == _keyAttribute) //selects the key attribute
                                            .Value;                              //gets attribute value
var leftJoin = source.GroupJoin(target,                  //join
                                getKeyValue,            //on the same
                                getKeyValue,           //condition
                                (src, corresp) => new
                                {
                                   src,
                                   corresp
                                })
                    .SelectMany(z => z.corresp.DefaultIfEmpty()
                                              .Select(tgt => new { z.src, tgt })) //selects matching
                    .ToList();                                                   //source and target

但这不是:

Func<Component, List<string>> getKeyValues = x => x.Attributes                 //from attributes
                                 .Where(a => _keyAttributes.Contains(a.Name)) //selects key attributes
                                 .OrderBy(a => a.Name)                       //order them by name
                                 .Select(a => a.Value)                      //gets attributes' values
                                 .ToList();
var leftJoin = source.GroupJoin(target,                  //join
                                getKeyValues,           //on the same
                                getKeyValues,          //condition
                                (src, corresp) => new
                                {
                                   src,
                                   corresp
                                })
                    .SelectMany(z => z.corresp.DefaultIfEmpty()
                                              .Select(tgt => new { z.src, tgt })) //selects matching
                    .ToList();                                                   //source and target

如果有帮助的话,这就是我正在研究的结构:

List<string> _keyAttributes;
List<Component> source;
List<Component> target;
[DataContract]
public class Component
{
   [DataMember]
   public List<Attribute> Attributes { get; set; }
   public Component()
   {
      new List<Attribute>();
   }
}
[DataContract]
public class Attribute
{
    [DataMember]
    public string Name { get; set;}
    [DataMember]
    public string Value { get; set;}
}   

有没有一种方法可以使用LINQ库来解决这个问题,或者我需要自己的GroupJoin扩展方法来做到这一点?

编译时具有未知密钥的LINQ复合密钥组Join

问题是,您提供的getKeyValues选择器将从每个Component返回一个List进行比较。每个返回的List将由引用进行比较,因此基本上您已经进行了以下操作:

var listA = new List<string> { "SomeString" };
var listB = new List<string> { "SomeString" };
bool areListsEqual = listA == listB;

areListsEqual将返回false,因为它们是通过引用进行比较的。本质上,您需要的是不同的EqualityComparer(可通过GroupJoin的重载添加),或者您需要一种通过比较属性的方法。

一个可行的例子(但不一定是一个好的方法)是:

Func<Component, string> getKeyValues = x =>
    string.Join(",", x.Attributes
                      .Where(a => _keyAttributes.Contains(a.Name))
                      .OrderBy(a => a.Name)
                      .Select(a => a.Value).ToArray());

这将创建一个字符串,表示每个列表中的值,并用于比较。一个更好的方法是使用EqualityComparer,它有自己的逻辑,可以根据其中包含的值使列表实际上相等。请参阅此处,了解如何比较两个列表。