Tra*_*s J 8 c# mysql linq eager-loading entity-framework-4.1
以下是Oracle确认的错误报告:http://bugs.mysql.com/bug.php?id = 67183
情况
当.Include我在我的存储库中使用链时,我注意到我得到了奇怪的结果 - 主要是被查询的值是从错误的字段返回的(例如,名称最终会出现在描述中 - 但在数据库中所有的值都是正确,他们只在查询后显示错误).我更改了名称,因此关系更加明显,但结构是一样的.我一直在为相关的CrewMember及其相对排名和清除得到错误的值.看起来如果CrewMember中的字段名称与Rank相同,则Rank中该字段的值将变为CrewMember中的值.例如,如果Rank有描述,CrewMember也是如此,那么CrewMember的Rank描述将是CrewMember的描述.
当由于MySQL Connector/NET sql提供程序无法正确形成join语句而导致类似字段定义时,实体框架无法在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; }
}
Run Code Online (Sandbox Code Playgroud)
询问
这是查询数据库并具有查询和.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)));
Run Code Online (Sandbox Code Playgroud)
这些.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}
Run Code Online (Sandbox Code Playgroud)
澄清
使用包含在1-1关系时,以这种方式"向下钻取"似乎没有任何问题.然而,当钻探中存在一对多的关系时,似乎会出现这个问题.钻孔是必要的,以便加载.
第一个投影entity => entity.Ships.Select(s => s.CrewMembers将返回与每艘船相关的船员记录列表.这样可以正确地返回港口包含船舶清单的图表,每个船舶都有一个船员清单.
但是,第二个投影CrewMembers.Select(cm => cm.Rank实际上并不返回图形的正确部分.字段开始混合,并且任何共享相同名称的字段将出于任何原因而默认为父字段.这导致不一致的结果,更重要的是坏数据.没有错误抛出的事实会使情况变得更糟,因为这只能通过运行时检查来确定.
如果有办法以某种方式从第一个投影获得强类型的单个响应(而不是列表),则可能第二个没有必要.就像现在一样,我认为问题在于第一次投射返回一个清单.当第二个投影尝试基于该列表而不是单个对象进行投影时,会引入逻辑错误.
如果,而不是CrewMembers是ICollection,它只有一个CrewMember,那么这个嵌套投影实际上将返回正确的数据.然而,这是这个问题的简化版本,不幸的是,几乎所有测试似乎都是从我审查过的各种博客,教程,帖子,文章和文档中完成的,试图解决这个问题.
query.Include(entity => entity.Ships);
query.Include(entity => entity.Ships.Select(s => s.CrewMembers));
query.Include(entity => entity.Ships.Select(s => s.CrewMembers.Select(cm => cm.Rank)));
query.Include(entity => entity.Ships.Select(s => s.CrewMembers.Select(cm => cm.Clearance)));
Run Code Online (Sandbox Code Playgroud)
首先,你知道它必须是query = query.Include(...).Include(...),对吧?
只要您执行最后2个,就不需要前2个.船只和船员都将从第二个装载2.你试过这个吗?
//query.Include(entity => entity.Ships);
//query.Include(entity => entity.Ships.Select(s => s.CrewMembers));
query = query.Include(entity => entity.Ships.Select(s => s.CrewMembers.Select(cm => cm.Rank)))
.Include(entity => entity.Ships.Select(s => s.CrewMembers.Select(cm => cm.Clearance)));
Run Code Online (Sandbox Code Playgroud)
此外,您始终可以启动sql profiler以查看发送到db的确切查询.如果你只运行第3和第4个包含,我不会指望会在图表中交换不同对象的属性值的错误.
编辑
下面的测试是使用SQL Server和SqlClient提供程序进行的.如果MySql您使用的提供程序有一个错误,那么问题无法通过SQL Server重现这一问题会引发一个问题,即为您的LINQ查询创建了错误的SQL.它看起来像在这个问题中同样的问题,其中问题发生在MySql提供者身上,并且无法用SqlClient/ SQL Server 重现.
我一直在为相关的CrewMember及其相对排名和清除得到错误的值.看起来如果CrewMember中的字段名称与Rank相同,则Rank中该字段的值将变为CrewMember中的值.例如,如果Rank有描述,CrewMember也是如此,那么CrewMember的Rank描述将是CrewMember的描述.
我用粗体测试了这个例子(使用EF 4.3.1)并且无法重现问题:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace EFInclude
{
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; }
}
public class MyContext : DbContext
{
public DbSet<Harbor> Harbors { get; set; }
public DbSet<Ship> Ships { get; set; }
public DbSet<CrewMember> CrewMembers { get; set; }
public DbSet<Rank> Ranks { get; set; }
public DbSet<Clearance> Clearances { get; set; }
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
using (var context = new MyContext())
{
context.Database.Initialize(true);
var harbor = new Harbor
{
Ships = new HashSet<Ship>
{
new Ship
{
CrewMembers = new HashSet<CrewMember>
{
new CrewMember
{
Rank = new Rank { Description = "Rank A" },
Clearance = new Clearance { Description = "Clearance A" },
Description = "CrewMember A"
},
new CrewMember
{
Rank = new Rank { Description = "Rank B" },
Clearance = new Clearance { Description = "Clearance B" },
Description = "CrewMember B"
}
},
Description = "Ship AB"
},
new Ship
{
CrewMembers = new HashSet<CrewMember>
{
new CrewMember
{
Rank = new Rank { Description = "Rank C" },
Clearance = new Clearance { Description = "Clearance C" },
Description = "CrewMember C"
},
new CrewMember
{
Rank = new Rank { Description = "Rank D" },
Clearance = new Clearance { Description = "Clearance D" },
Description = "CrewMember D"
}
},
Description = "Ship CD"
}
},
Description = "Harbor ABCD"
};
context.Harbors.Add(harbor);
context.SaveChanges();
}
using (var context = new MyContext())
{
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)));
var sqlString = query.ToString();
// see below for the generated SQL query
var harbor = query.Single();
Console.WriteLine("Harbor {0} Description = \"{1}\"",
harbor.HarborId, harbor.Description);
foreach (var ship in harbor.Ships)
{
Console.WriteLine("- Ship {0} Description = \"{1}\"",
ship.ShipId, ship.Description);
foreach (var crewMember in ship.CrewMembers)
{
Console.WriteLine("-- CrewMember {0} Description = \"{1}\"",
crewMember.CrewMemberId, crewMember.Description);
Console.WriteLine("-- CrewMember {0} Rank Description = \"{1}\"",
crewMember.CrewMemberId, crewMember.Rank.Description);
Console.WriteLine("-- CrewMember {0} Clearance Description = \"{1}\"",
crewMember.CrewMemberId, crewMember.Clearance.Description);
}
}
Console.ReadLine();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出是:

根据你的粗体描述,我应该有:CrewMember 1 Description ="Rank A"和其他3名船员一样混乱.但我没有这个.
与您遇到错误的代码相比,我的测试程序有什么不同吗?
编辑
生成的查询SQL(参见var sqlString = query.ToString();上面的源代码行,以下是内容sqlString)是:
SELECT
[Project1].[HarborId] AS [HarborId],
[Project1].[Description] AS [Description],
[Project1].[C2] AS [C1],
[Project1].[ShipId] AS [ShipId],
[Project1].[HarborId1] AS [HarborId1],
[Project1].[Description1] AS [Description1],
[Project1].[C1] AS [C2],
[Project1].[CrewMemberId] AS [CrewMemberId],
[Project1].[ShipId1] AS [ShipId1],
[Project1].[RankId] AS [RankId],
[Project1].[ClearanceId] AS [ClearanceId],
[Project1].[Description2] AS [Description2],
[Project1].[RankId1] AS [RankId1],
[Project1].[Description3] AS [Description3],
[Project1].[ClearanceId1] AS [ClearanceId1],
[Project1].[Description4] AS [Description4]
FROM ( SELECT
[Extent1].[HarborId] AS [HarborId],
[Extent1].[Description] AS [Description],
[Join3].[ShipId1] AS [ShipId],
[Join3].[HarborId] AS [HarborId1],
[Join3].[Description1] AS [Description1],
[Join3].[CrewMemberId] AS [CrewMemberId],
[Join3].[ShipId2] AS [ShipId1],
[Join3].[RankId1] AS [RankId],
[Join3].[ClearanceId1] AS [ClearanceId],
[Join3].[Description2] AS [Description2],
[Join3].[RankId2] AS [RankId1],
[Join3].[Description3] AS [Description3],
[Join3].[ClearanceId2] AS [ClearanceId1],
[Join3].[Description4] AS [Description4],
CASE WHEN ([Join3].[ShipId1] IS NULL) THEN CAST(NULL AS int) WHEN ([Join3].[CrewMemberId] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1],
CASE WHEN ([Join3].[ShipId1] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
FROM [dbo].[Harbors] AS [Extent1]
LEFT OUTER JOIN (SELECT [Extent2].[ShipId] AS [ShipId1], [Extent2].[HarborId] AS [HarborId], [Extent2].[Description] AS [Description1], [Join2].[CrewMemberId], [Join2].[ShipId2], [Join2].[RankId1], [Join2].[ClearanceId1], [Join2].[Description2], [Join2].[RankId2], [Join2].[Description3], [Join2].[ClearanceId2], [Join2].[Description4]
FROM [dbo].[Ships] AS [Extent2]
LEFT OUTER JOIN (SELECT [Extent3].[CrewMemberId] AS [CrewMemberId], [Extent3].[ShipId] AS [ShipId2], [Extent3].[RankId] AS [RankId1], [Extent3].[ClearanceId] AS [ClearanceId1], [Extent3].[Description] AS [Description2], [Extent4].[RankId] AS [RankId2], [Extent4].[Description] AS [Description3], [Extent5].[ClearanceId] AS [ClearanceId2], [Extent5].[Description] AS [Description4]
FROM [dbo].[CrewMembers] AS [Extent3]
INNER JOIN [dbo].[Ranks] AS [Extent4] ON [Extent3].[RankId] = [Extent4].[RankId]
LEFT OUTER JOIN [dbo].[Clearances] AS [Extent5] ON [Extent3].[ClearanceId] = [Extent5].[ClearanceId] ) AS [Join2] ON [Extent2].[ShipId] = [Join2].[ShipId2] ) AS [Join3] ON [Extent1].[HarborId] = [Join3].[HarborId]
) AS [Project1]
ORDER BY [Project1].[HarborId] ASC, [Project1].[C2] ASC, [Project1].[ShipId] ASC, [Project1].[C1] ASC
Run Code Online (Sandbox Code Playgroud)