这是 Oracle 确认的错误报告:http ://bugs.mysql.com/bug.php?id=67183
情况
在我的存储库中使用.Include
链时,我注意到我得到了奇怪的结果 - 主要是返回的查询值来自错误的字段(例如,名称最终会出现在描述中 - 但在数据库中所有值都是正确,它们仅在查询后显示错误)。我更改了名称,因此关系更加明显,但结构是相同的。我一直在为相关的 CrewMember 及其相对等级和权限获得错误的值。似乎如果 CrewMember 中存在与 Rank 相同的字段名称,则 Rank 中该字段的值将变为 CrewMember 中的值。例如,如果 Rank 有描述,CrewMember 也有,那么 CrewMember 的 Rank 描述就是 CrewMember 的描述。
当由于 MySQL 连接器/NET sql 提供程序未能正确形成join
语句而定义了类似的字段时,Entity Framework 无法进行超过深度 2 的格式良好的查询。
定义
这是一个模拟数据库表的类定义。我将 C# ASP.NET MVC 3 与 Entity Framework 4.1 和 MySQL Connector/NET 6.5 版一起使用
public class Harbor
{
public int HarborId { get; set; }
public virtual ICollection<Ship> Ships { get; set; }
public string Description { get; set; }
}
public class Ship
{
public int ShipId { get; set; }
public int HarborId { get; set; }
public virtual Harbor Harbor { get; set; }
public virtual ICollection<CrewMember> CrewMembers { get; set; }
public string Description { get; set; }
}
public class CrewMember
{
public int CrewMemberId { get; set; }
public int ShipId { get; set; }
public virtual Ship Ship { get; set; }
public int RankId { get; set; }
public virtual Rank Rank { get; set; }
public int ClearanceId { get; set; }
public virtual Clearance Clearance { get; set; }
public string Description { get; set; }
}
public class Rank
{
public int RankId { get; set; }
public virtual ICollection<CrewMember> CrewMembers { get; set; }
public string Description { get; set; }
}
public class Clearance
{
public int ClearanceId { get; set; }
public virtual ICollection<CrewMember> CrewMembers { get; set; }
public string Description { get; set; }
}
询问
这是查询数据库并具有查询和 .Include 调用的代码。
DbSet<Harbor> dbSet = context.Set<Harbor>();
IQueryable<Harbor> query = dbSet;
query = query.Include(entity => entity.Ships);
query = query.Include(entity => entity.Ships.Select(s => s.CrewMembers));
query = query.Include(entity => entity.Ships.Select(s => s.CrewMembers.Select(cm => cm.Rank)));
query = query.Include(entity => entity.Ships.Select(s => s.CrewMembers.Select(cm => cm.Clearance)));
这些.Include
调用格式正确吗?我错过了什么?
这相当复杂,所以如果您有任何问题,请在评论中告诉我,我会尽力澄清我可能遗漏的任何内容。
使用 MySQL Connector / NET 时,如何使用 Entity Framework 在深度超过 2 的对象图上获得格式良好的查询?
编辑
这是生成的查询:
{SELECT
[Project1].[HarborId],
[Project1].[Description],
[Project1].[C2] AS [C1],
[Project1].[ShipId],
[Project1].[HarborId1],
[Project1].[Description1],
[Project1].[C1] AS [C2],
[Project1].[CrewMemberId],
[Project1].[ShipId1],
[Project1].[ClearanceId],
[Project1].[RankId],
[Project1].[Description2],
[Project1].[RankId1],
[Project1].[Description3],
[Project1].[ClearanceId1],
[Project1].[Description4],
FROM (SELECT
[Extent1].[HarborId],
[Extent1].[Description],
[Join3].[ShipId],
[Join3].[HarborId] AS [HarborId1],
[Join3].[Description]AS [Description1],
[Join3].[CrewMemberId],
[Join3].[ShipId]AS [ShipId1],
[Join3].[ClearanceId],
[Join3].[RankId],
[Join3].[Description] AS [Description2],
[Join3].[RankId] AS [RankId1],
[Join3].[Description] AS [Description3],
[Join3].[ClearanceId] AS [ClearanceId1],
[Join3].[Description] AS [Description4],
CASE WHEN ([Join3].[ShipId] IS NULL) THEN (NULL) WHEN ([Join3].[CrewMemberId] IS NULL) THEN (NULL) ELSE (1) END AS [C1],
CASE WHEN ([Join3].[ShipId] IS NULL) THEN (NULL) ELSE (1) END AS [C2]
FROM [Harbor] AS [Extent1] LEFT OUTER JOIN (SELECT
[Extent2].[ShipId],
[Extent2].[HarborId],
[Extent2].[Description],
[Join2].[CrewMemberId],
[Join2].[ShipId] AS [ShipID1],
[Join2].[ClearanceId],
[Join2].[RankId],
[Join2].[Description] AS [DESCRIPTION1],
[Join2].[RankID1],
[Join2].[DESCRIPTION1] AS [DESCRIPTION11],
[Join2].[ClearanceID1],
[Join2].[DESCRIPTION2],
FROM [Ship] AS [Extent2] LEFT OUTER JOIN (SELECT
[Extent3].[CrewMemberId],
[Extent3].[ShipId],
[Extent3].[ClearanceId],
[Extent3].[RankId],
[Extent3].[Description],
[Extent4].[RankId] AS [RankID1],
[Extent4].[Description] AS [DESCRIPTION1],
[Extent5].[ClearanceId] AS [ClearanceID1],
[Extent5].[Description] AS [DESCRIPTION2],
FROM [CrewMember] AS [Extent3] INNER JOIN [Rank] AS [Extent4] ON [Extent3].[RankId] = [Extent4].[RankId] LEFT OUTER JOIN [Clearance] AS [Extent5] ON [Extent3].[ClearanceId] = [Extent5].[ClearanceId]) AS [Join2] ON [Extent2].[ShipId] = [Join2].[ShipId]) AS [Join3] ON [Extent1].[HarborId] = [Join3].[HarborId]
WHERE [Extent1].[HarborId] = @p__linq__0) AS [Project1]
ORDER BY
[Project1].[HarborId] ASC,
[Project1].[C2] ASC,
[Project1].[ShipId] ASC,
[Project1].[C1] ASC}
澄清
当以这种方式“向下钻取”时,在 1-1 关系上使用 include 似乎没有问题。但是,当钻探过程中存在一对多关系时,问题似乎就出现了。为了急切加载,钻孔是必要的。
第一个投影entity => entity.Ships.Select(s => s.CrewMembers
将返回与每艘船相关的 CrewMembers 列表。这会正确返回一个港口包含船只列表的图表,每艘船都有一个船员名单。
但是,第二个投影CrewMembers.Select(cm => cm.Rank
实际上并没有返回正确的图形部分。字段开始混合,任何共享相同名称的字段无论出于何种原因都将默认为父字段。这会导致结果不一致,更重要的是数据不正确。没有抛出错误的事实使情况变得更糟,因为这只能通过运行时检查来确定。
如果有办法以某种方式从第一个投影中获得强类型的单一响应(而不是列表),那么第二个可能就没有必要了。就像现在一样,我认为问题在于第一个投影返回一个列表。当第二个投影尝试基于该列表而不是从单个对象进行投影时,就会引入逻辑错误。
如果 CrewMembers 不是一个 ICollection,而只是一个 CrewMember,那么这个嵌套投影实际上将返回正确的数据。然而,这是这个问题的简化版本,不幸的是,几乎所有的测试似乎都是从我为解决这个问题而审查的各种博客、教程、帖子、文章和文档中完成的。