LINQ Composite Key Group Присоединение с ключами, неизвестными во время компиляции
Я пытаюсь заставить 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 для этого?
1 ответ
Проблема в том, что предоставленный вами селектор getKeyValues вернет List
с каждого Component
сравнивать по. Возвращенный 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
это имеет свою собственную логику для того, что делает списки фактически равными на основе значений, содержащихся внутри. Смотрите здесь, как вы можете сравнить два списка.