调用ToList()时内部LINQ查询的exception

昨天我正在研究代码重构,并遇到了一个exception,我真的找不到太多的信息。 这是情况。

我们有一对EF实体,它们通过关系表具有多对多的关系。 有问题的对象看起来像这样,省去了不必要的位。

public partial class MasterCode { public int MasterCodeId { get; set; } ... public virtual ICollection MasterCodeToSubCodes { get; set; } } public partial class MasterCodeToSubCodes { public int MasterCodeToSubCodeId { get; set; } public int MasterCodeId { get; set; } public int SubCodeId { get; set; } ... } 

现在,我尝试对这些实体运行LINQ查询。 我们在DTO中使用了很多LINQ投影。 DTO和查询如下。 masterCodeId是传入的参数。

 public class MasterCodeDto { public int MasterCodeId { get; set; } ... public ICollection SubCodeIds { get; set; } } (from m in MasterCodes where m.MasterCodeId == masterCodeId select new MasterCodeDto { ... SubCodeIds = (from s in m.MasterCodeToSubCodes select s.SubCodeId).ToList(), ... }).SingleOrDefaultAsync(); 

内部查询引发以下exception

 Expression of type 'System.Data.Entity.Infrastructure.ObjectReferenceEqualityComparer' cannot be used for constructor parameter of type 'System.Collections.Generic.IEqualityComparer`1[System.Int32]' 

我们之前在代码的其他地方做过这样的内部查询,没有任何问题。 这一点的不同之处在于,我们不会新建一个对象并投射到它中,而是返回一组我们想要放入列表的整数。

我找到了一个解决方法,通过将MasterCodeDto上的ICollection更改为IEnumerable并删除ToList(),但我无法找到为什么我不能只选择id并将它们作为列表返回。

有没有人对此问题有任何见解? 通常只返回一个id字段并且当它不是内部查询的一部分时调用ToList()可以正常工作。 我是否错过了对内部查询的限制,以防止此类操作发生?

谢谢。

编辑:为了举例说明此模式的工作原理,我将向您展示一个有效的查询示例。

  (from p in Persons where p.PersonId == personId select new PersonDto { ... ContactInformation = (from pc in p.PersonContacts select new ContactInformationDto { ContactInformationId = pc.PatientContactId, ... }).ToList(), ... }).SingleOrDefaultAsync(); 

在这个例子中,我们选择一个新的Dto,而不是只选择一个值。 它工作正常。 问题似乎源于仅选择单一价值。

编辑2:在另一个有趣的转折,如果不是选择进入MasterCodeDto我选择一个匿名类型,也不会抛出ToList()到位的exception。

我认为你偶然发现了Entity Framework中的一个错误。 EF有一些逻辑可以选择适当的具体类型来实现集合。 HashSet是它的最爱之一。 显然(我不能完全遵循EF的源代码),它为ICollections选择HashSet,为IEnumerable选择List。

看起来EF试图通过使用接受IEqualityComparer的构造函数来创建HashSet。 (这发生在EF的DelegateFactory类,方法GetNewExpressionForCollectionType 。)错误是它使用自己的ObjectReferenceEqualityComparer 。 但这是一个IEqualityComparer ,它无法转换为IEqualityComparer

一般来说,我认为最好不要在LINQ查询中使用ToList ,并在DTO类型的集合中使用IEnumerable 。 因此,EF将有完全的自由选择合适的混凝土类型。