我有一个复杂的LINQ查询(使用LINQ 2 EF)可以返回重复的结果,因此我使用.Distinct()方法来避免重复.这是骨架:
var subQuery1 = // one query... var subQuery2 = // another query... var result = subQuery1.Distinct().Union( subQuery2.Distinct() ).ToArray();
每个子查询将公共用户表与另一个表连接并执行“where”查询,稍后将结果组合在.Union(…)中.这个工作正常,直到表被修改为包含XML列,这导致此异常:
the xml data type cannot be selected as distinct because it is not comparable
在这种情况下,我不关心XML列是否在结果中是等效的.实际上我只需要确保主键UserId在结果中是不同的.
有没有办法使用Distinct()但忽略XML列或更简单的方法来确保我以有效的方式使用相同的UserId从结果中删除记录?理想情况下,这不会从数据库中检索重复记录,也不需要进行后处理来删除重复记录.
更新:
我发现如果我提前将查询序列化到数组,则不需要任何类型的比较器,因为Linq2Objects没有XML不同的选择问题.例如,我可以这样做:
var subQuery1 = // one query... var subQuery2 = // another query... var result = subQuery1.Distinct().ToArray().Union( subQuery2.Distinct().ToArray() ) .ToArray();
所以我真正想要的是一种避免序列化中间查询并直接执行Linq2Entities调用的方法,该调用不会获取具有重复UserIds的记录.感谢迄今为止的所有答案.
解决方法
写IEqualityComparer< T>包含XML类型的对象的实现,并将其传递给Distinct.在Equals方法中,您可以根据需要实现相等语义.
这是我自己编写的一个方便的T4代码生成模板,用于生成IEqualityComparer< T>我团队的域模型的实现:
<#@ template language="C#v3.5" debug="True" #> <#@ output extension=".generated.cs" #> <# var modelNames = new string[] { "ClassName1","ClassName2","ClassName3",}; var namespaceName = "MyNamespace"; #> using System; using System.Collections.Generic; namespace <#= namespaceName #> { <# for (int i = 0; i < modelNames.Length; ++i) { string modelName = modelNames[i]; string eqcmpClassName = modelName + "ByIDEqualityComparer"; #> #region <#= eqcmpClassName #> /// <summary> /// Use this EqualityComparer class to determine uniqueness among <#= modelName #> instances /// by using only checking the ID property. /// </summary> [System.Diagnostics.DebuggerNonUserCode] public sealed partial class <#= eqcmpClassName #> : IEqualityComparer<<#= modelName #>> { public bool Equals(<#= modelName #> x,<#= modelName #> y) { if ((x == null) && (y == null)) return true; if ((x == null) || (y == null)) return false; return x.ID.Equals(y.ID); } public int GetHashCode(<#= modelName #> obj) { if (obj == null) return 0; return obj.ID.GetHashCode(); } } #endregion <# if (i < modelNames.Length - 1) WriteLine(String.Empty); } // for (int i = 0; i < modelNames.Length; ++i) #> }
它假设您的每个模型类都有一个名为“ID”的属性,它是主键,存储为实现Equals的东西.我们的会议迫使我们所有的模型都拥有这个属性.如果您的模型都具有不同名称的ID属性,请考虑修改此T4模板以满足您的需求或更好,让您自己更轻松(不仅仅是为了使用此T4)并更改模型以使用“ID” “ 名称.