根据pwilcox's answer 上留下的评论,似乎 OP 正在寻找一个解决方案,其中还包括不匹配的行。这意味着我们正在寻找左外连接,而不是使用内连接。
在 Linq 的世界中,这可以通过 GroupJoin、SelectMany 和 Select 运算符的组合来实现。
为了能够连接两个不同的列,我们必须引入一个中间类来识别GroupJoin 的类型。所以,我创建了以下类:
internal class IntermediateKey
{
public int Id { get; set; }
public string Value { get; set; }
}
我们还必须为这个类定义一个比较器才能找到匹配的数据:
internal class IntermediateKeyComparer : IEqualityComparer<IntermediateKey>
{
public bool Equals(IntermediateKey x, IntermediateKey y)
{
return x.Id == y.Id && x.Value == y.Value;
}
public int GetHashCode(IntermediateKey obj)
{
return obj.Id.GetHashCode() + obj.Value.GetHashCode();
}
}
请记住,此实现非常简单。正确的实现方式见this thread。
现在可以这样定义我们的查询:
var comparer = new IntermediateKeyComparer();
var result = userValues
.GroupJoin(
lookupMetas,
uv => new IntermediateKey { Id = uv.FieldId, Value = uv.FieldValue },
lm => new IntermediateKey { Id = lm.FieldId, Value = lm.Id.ToString() },
(uv, lm) => new { Value = uv, Lookups = lm},
comparer)
.SelectMany(
pair => pair.Lookups.DefaultIfEmpty(),
(paired, meta) => new { Value = paired.Value, Lookup = meta})
.Select(res =>
{
res.Value.FieldValue = res.Lookup?.FieldValueId.ToString() ?? res.Value.FieldValue;
return res.Value;
});
- 我们定义
userValues 应该在lookupMetas 上保持外部连接
- 如果
uv 的FieldId 与lm 的FieldId 匹配
- 如果
uv的FieldValue匹配lm的Id的字符串表示
- 对于
SelectMany,我们选择匹配的LookupMeta 实体或null
- 对于
Select,我们仅在存在相关LookupMeta 时更新UserValue 的FieldValue 属性,否则我们使用其原始值。
现在让我们看看它如何处理一些示例数据:
static void Main(string[] args)
{
var userValues = new List<UserValue>
{
new UserValue { FieldId = 1, FieldValue = "2"},
new UserValue { FieldId = 2, FieldValue = "3"},
new UserValue { FieldId = 4, FieldValue = "5"}
};
var lookupMetas = new List<LookupMeta>
{
new LookupMeta { FieldId = 1, Id = 2, FieldValueId = 20 },
new LookupMeta { FieldId = 2, Id = 3, FieldValueId = 30 },
new LookupMeta { FieldId = 3, Id = 4, FieldValueId = 40 },
};
var comparer = new IntermediateKeyComparer();
var result = userValues
.GroupJoin(
lookupMetas,
uv => new IntermediateKey { Id = uv.FieldId, Value = uv.FieldValue },
lm => new IntermediateKey { Id = lm.FieldId, Value = lm.Id.ToString() },
(uv, lm) => new { Value = uv, Lookups = lm},
comparer)
.SelectMany(
pair => pair.Lookups.DefaultIfEmpty(),
(x, meta) => new { Value = x.Value, Lookup = meta})
.Select(res =>
{
res.Value.FieldValue = res.Lookup?.FieldValueId.ToString() ?? res.Value.FieldValue;
return res.Value;
});
foreach (var maybeUpdatedUserValue in result)
{
Console.WriteLine($"{maybeUpdatedUserValue.FieldId}: {maybeUpdatedUserValue.FieldValue}");
}
}
输出将是:
1: 20
2: 30
4: 5
所以,如您所见,最后一个 UserValue 没有匹配的 LookupMeta,这就是它的 FieldValue 保持不变的原因。