W jaki sposób mogę użyć Entity Framework na wykresie obiektu powyżej głębokości 2 z MySQL Connector / NET?

Oto potwierdzony raport o błędzie w Oracle:http://bugs.mysql.com/bug.php?id=67183

Sytuacja

Podczas korzystania z.Include Łańcuch wewnątrz mojego repozytorium, zauważyłem, że otrzymuję dziwne wyniki - głównie że wartości, które były zwracane były z niewłaściwych pól (na przykład nazwa kończy się opisem - ale w bazie wszystkie wartości są poprawne, po zapytaniu pojawia się tylko źle Zmieniłem nazwy, aby relacje były bardziej oczywiste, ale struktura jest taka sama. Ciągle otrzymuję błędne wartości dla powiązanego członka załogi i ich względną rangę i odległość. Wygląda na to, że istnieje nazwa pola, która jest taka sama w CrewMember jak Rank, wtedy wartość tego pola w Rank staje się wartością w CrewMember. Na przykład, jeśli Rank miałby opis, podobnie jak CrewMember, opis Rangi dla Członka Załogi byłby opisem Członka Załogi.

Entity Framework nie potrafi utworzyć dobrze uformowanych zapytań powyżej głębokości 2, gdy istnieją podobne pola zdefiniowane jako wynik niepoprawnego utworzenia dostawcy MySQL Connector / NET sqljoin sprawozdania.

Definicje

Jest to definicja klasy, która modeluje tabelę bazy danych. Korzystam z C # ASP.NET MVC 3 z Entity Framework 4.1 i MySQL Connector / NET wersja 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; }
}

Pytanie

Jest to kod, który odpytuje bazę danych i ma zapytanie i .Include wywołania.

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

Czy to.Include&nbsp;dobrze się nazywa? Przegapiłem coś?

Jest to dość skomplikowane, więc jeśli masz jakieś pytania, daj mi znać w komentarzach, a ja postaram się wyjaśnić wszystko, co mogłem pominąć.

W jaki sposób mogę użyć Entity Framework, aby uzyskać dobrze uformowane zapytanie na wykresie obiektu po głębokości 2 podczas korzystania z MySQL Connector / NET?

Edycje

Oto wygenerowane zapytanie:

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

Wyjaśnienie

Używanie dołączania do relacji 1-1 nie stwarza problemu, gdy „wierci się” w ten sposób, jak się wydaje. Wydaje się jednak, że problem pojawia się, gdy w ramach wiercenia występuje wiele relacji. Wiercenie jest konieczne w celu zwiększenia obciążenia.

Pierwsza projekcja,entity => entity.Ships.Select(s => s.CrewMembers, zwróci listę członków załogi powiązanych z każdym statkiem. Odpowiednio zwraca wykres, w którym port zawiera listę statków, z których każdy zawiera listę członków załogi.

Jednak druga projekcjaCrewMembers.Select(cm => cm.Rank, w rzeczywistości nie zwraca właściwego fragmentu wykresu. Pola zaczynają się mieszać, a wszystkie pola o tej samej nazwie będą domyślnie ustawiane z dowolnego powodu w polu nadrzędnym. Powoduje to niespójne wyniki i, co ważniejsze, złe dane. Fakt, że nie są zgłaszane żadne błędy, pogarsza sytuację, ponieważ można to określić tylko poprzez inspekcję w czasie wykonywania.

Gdyby był sposób, aby w jakiś sposób uzyskać silnie wpisaną pojedynczą odpowiedź (w przeciwieństwie do listy) z pierwszej projekcji, być może druga nie byłaby konieczna. Tak jak teraz uważam, że problem leży w pierwszej projekcji zwracającej listę. Gdy druga projekcja próbuje wyświetlić projekt na podstawie tej listy zamiast z pojedynczego obiektu, wprowadzany jest błąd logiczny.

Jeśli zamiast CrewMembers jest ICollection, był to tylko jeden członek CrewMember, to ta zagnieżdżona projekcja faktycznie zwróci poprawne dane. Jest to jednak uproszczona wersja tego problemu i niestety jest to, co prawie wszystkie testy zostały wykonane na różnych blogach, samouczkach, postach, artykułach i dokumentach, które przejrzałem, próbując rozwiązać ten problem.