对Join()
Nullable<int>
{1}}的LINQ TKey
方法跳过空键匹配。我在文档中遗漏了什么?我知道我可以切换到SelectMany()
,我只是好奇为什么这个平等操作像SQL一样工作而不像C#,因为尽管我可以说,EqualityComparer<int?>.Default
的工作方式与我期望的完全相同为空值。
http://msdn.microsoft.com/en-us/library/bb534675.aspx
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
public class dt
{
public int? Id;
public string Data;
}
public class JoinTest
{
public static int Main(string [] args)
{
var a = new List<dt>
{
new dt { Id = null, Data = "null" },
new dt { Id = 1, Data = "1" },
new dt { Id = 2, Data = "2" }
};
var b = new List<dt>
{
new dt { Id = null, Data = "NULL" },
new dt { Id = 2, Data = "two" },
new dt { Id = 3, Data = "three" }
};
//Join with null elements
var c = a.Join( b,
dtA => dtA.Id,
dtB => dtB.Id,
(dtA, dtB) => new { aData = dtA.Data, bData = dtB.Data } ).ToList();
// Output:
// 2 two
foreach ( var aC in c )
Console.WriteLine( aC.aData + " " + aC.bData );
Console.WriteLine( " " );
//Join with null elements converted to zero
c = a.Join( b,
dtA => dtA.Id.GetValueOrDefault(),
dtB => dtB.Id.GetValueOrDefault(),
(dtA, dtB) => new { aData = dtA.Data, bData = dtB.Data } ).ToList();
// Output:
// null NULL
// 2 two
foreach ( var aC in c )
Console.WriteLine( aC.aData + " " + aC.bData );
Console.WriteLine( EqualityComparer<int?>.Default.Equals( a[0].Id, b[0].Id ) );
Console.WriteLine( EqualityComparer<object>.Default.Equals( a[0].Id, b[0].Id ) );
Console.WriteLine( a[0].Id.Equals( b[0].Id ) );
return 0;
}
}
答案 0 :(得分:4)
Enumerable.Join
使用JoinIterator
(私有类)迭代匹配元素。 JoinIterator
使用Lookup<TKey, TElement>
创建序列键的查找:
internal static Lookup<TKey, TElement> CreateForJoin(
IEnumerable<TElement> source,
Func<TElement, TKey> keySelector,
IEqualityComparer<TKey> comparer)
{
Lookup<TKey, TElement> lookup = new Lookup<TKey, TElement>(comparer);
foreach (TElement local in source)
{
TKey key = keySelector(local);
if (key != null) // <--- Here
{
lookup.GetGrouping(key, true).Add(local);
}
}
return lookup;
}
这里有趣的部分是跳过null
的密钥。这就是为什么没有提供默认值你只有一个匹配。
看起来我找到了这种行为的原因。 Lookup使用默认的EqualityComparer,它将返回0
的密钥null
和密钥0
:
int? keyA = 0;
var comparer = EqualityComparer<int?>.Default;
int hashA = comparer.GetHashCode(keyA) & 0x7fffffff; // from Lookup class
int? keyB = null;
int hashB = comparer.GetHashCode(keyB) & 0x7fffffff;
Console.WriteLine(hashA); // 0
Console.WriteLine(hashB); // 0
可能会跳过空值以避免匹配null
和0
个键。
答案 1 :(得分:0)
我认为这样做是为了匹配you can't join on null keys, they are just ignored数据库的行为。 There are workarounds超越了这个限制,遗憾的是无法用LINQ编写。
您必须以这样的方式编写查询:没有任何键实际上为null。您可以通过将值包装在另一个可以进行相等性比较的对象(例如,元组或匿名对象)中来实现。
//Join with null elements
var c = a.Join( b,
dtA => Tuple.Create(dtA.Id),
dtB => Tuple.Create(dtB.Id),
(dtA, dtB) => new { aData = dtA.Data, bData = dtB.Data } ).ToList();