Wie kann ich mit MySQL Connector / NET Entity Framework für ein Objektdiagramm mit einer Tiefe von mehr als 2 verwenden?

Hier ist ein bestätigter Fehlerbericht mit Oracle:http://bugs.mysql.com/bug.php?id=67183

Situation

Bei Verwendung eines.Include Kette in meinem Repository bemerkte ich, dass ich seltsame Ergebnisse bekam - meistens, dass die abgefragten Werte, die zurückgegeben wurden, aus den falschen Feldern stammten (Name würde zum Beispiel in Beschreibung enden - aber in der Datenbank sind alle Werte korrekt, sie nur nach der Abfrage falsch angezeigt). Ich habe die Namen geändert, damit die Beziehungen offensichtlicher werden, aber die Struktur ist dieselbe. Ich erhalte immer wieder die falschen Werte für das zugehörige CrewMember und deren relativen Rang und Abstand. Wenn es einen Feldnamen gibt, der in CrewMember mit Rank identisch ist, entspricht der Wert dieses Felds in Rank dem Wert in CrewMember. Wenn beispielsweise Rank und CrewMember eine Beschreibung haben, ist die Beschreibung von Rank für das CrewMember die Beschreibung des CrewMembers.

Entity Framework kann keine wohlgeformten Abfragen mit einer Tiefe von mehr als 2 erstellen, wenn ähnliche Felder definiert sind, da der MySQL Connector / NET-SQL-Anbieter keine ordnungsgemäße Form aufweistjoin Aussagen.

Definitionen

Dies ist eine Klassendefinition, die eine Datenbanktabelle modelliert. Ich verwende C # ASP.NET MVC 3 mit dem Entity Framework 4.1 und dem MySQL Connector / NET Version 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; }
}

Abfrage

Dies ist der Code, der die Datenbank abfragt und über die Aufrufe query und .Include verfügt.

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)));

Sind diese.Include nennt wohlgeformt? Habe ich etwas verpasst?

Dies ist ziemlich komplex. Wenn Sie Fragen haben, teilen Sie mir diese bitte in Kommentaren mit. Ich werde versuchen, alles zu klären, was ich möglicherweise ausgelassen habe.

Wie kann ich Entity Framework verwenden, um bei Verwendung von MySQL Connector / NET eine wohlgeformte Abfrage für ein Objektdiagramm mit einer Tiefe von mehr als 2 zu erhalten?

Bearbeitungen

Hier ist die generierte Abfrage:

{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}

Klärung

Die Verwendung von Include-on-1-1-Beziehungen ist kein Problem, wenn auf diese Weise ein Drilldown durchgeführt wird. Das Problem scheint sich jedoch zu ergeben, wenn im Rahmen der Bohrungen 1-viele Beziehungen bestehen. Das Bohren ist notwendig, um eifrig zu belasten.

Die erste Projektion,entity => entity.Ships.Select(s => s.CrewMembers, gibt eine Liste der Crewmitglieder zurück, die sich auf jedes Schiff beziehen. Dies gibt die Grafik korrekt zurück, in der ein Hafen eine Liste von Schiffen mit jeweils einer Liste von Besatzungsmitgliedern enthält.

Allerdings die zweite ProjektionCrewMembers.Select(cm => cm.Rankgibt in der Tat nicht das richtige Stück des Graphen zurück. Die Felder werden gemischt, und alle Felder mit demselben Namen werden aus irgendeinem Grund als Standard für das übergeordnete Feld verwendet. Dies führt zu inkonsistenten Ergebnissen und vor allem zu schlechten Daten. Die Tatsache, dass keine Fehler geworfen werden, macht es noch schlimmer, da dies nur durch Laufzeitüberprüfung festgestellt werden kann.

Wenn es eine Möglichkeit gäbe, eine stark typisierte Einzelantwort (im Gegensatz zu einer Liste) von der ersten Projektion zu erhalten, wäre die zweite möglicherweise nicht erforderlich. Nach meinem derzeitigen Kenntnisstand liegt das Problem in der ersten Projektion, die eine Liste zurückgibt. Wenn die zweite Projektion versucht, basierend auf dieser Liste anstatt von einem einzelnen Objekt zu projizieren, wird der logische Fehler eingeführt.

Wenn es sich bei CrewMembers nicht um eine ICollection, sondern nur um ein CrewMember handelt, gibt diese verschachtelte Projektion tatsächlich die korrekten Daten zurück. Dies ist jedoch eine vereinfachte Version dieses Problems, und leider wurden fast alle Tests in den verschiedenen Blogs, Tutorials, Posts, Artikeln und Dokumenten durchgeführt, die ich bei dem Versuch, dieses Problem zu lösen, durchgesehen habe.

Antworten auf die Frage(3)

Ihre Antwort auf die Frage