Como eu posso usar o Entity Framework em um gráfico de objeto após uma profundidade de 2 com o MySQL Connector / NET?

Aqui está um relatório de bug confirmado com o Oracle:http://bugs.mysql.com/bug.php?id=67183

Situação

Ao usar um.Include cadeia dentro do meu repositório, notei que estava recebendo resultados estranhos - principalmente que os valores consultados que estavam sendo retornados eram dos campos errados (o nome acabaria na descrição por exemplo - mas no banco de dados todos os valores estão corretos, eles só aparece errado após a consulta). Eu mudei os nomes para que os relacionamentos sejam mais óbvios, mas a estrutura é a mesma. Eu continuo recebendo os valores errados para o CrewMember associado e seu Rank e Clearance relativos. Parece que se existe um nome de campo que é o mesmo em CrewMember como Rank, então o valor daquele campo em Rank torna-se o que o valor estava em CrewMember. Por exemplo, se o Rank tivesse uma descrição, e também o CrewMember, então a descrição do Rank para o CrewMember seria a descrição do CrewMember.

O Entity Framework não consegue fazer consultas bem formadas após uma profundidade de 2 quando existem campos semelhantes definidos como resultado do MySQL Connector / NET sql provider não formar corretamentejoin afirmações.

Definições

Esta é uma definição de classe que modela uma tabela de banco de dados. Estou usando o C # ASP.NET MVC 3 com o Entity Framework 4.1 e o MySQL Connector / NET versão 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; }
}

Inquerir

Esse é o código que consulta o banco de dados e tem a consulta e as chamadas .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)));

São estes.Include chama bem formado? Perdi alguma coisa?

Isso é bastante complexo, então se você tiver alguma dúvida, por favor, deixe-me saber nos comentários e vou tentar esclarecer qualquer coisa que eu possa ter deixado de fora.

Como posso usar o Entity Framework para obter uma consulta bem formada em um gráfico de objeto após uma profundidade de 2 ao usar o MySQL Connector / NET?

Edições

Aqui está a consulta gerada:

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

Esclarecimento

Usar incluir nos relacionamentos de 1 a 1 não é um problema quando se faz "drill down" dessa maneira. No entanto, a questão parece surgir quando há várias relações como parte da perfuração. A perfuração é necessária para uma carga ávida.

A primeira projeção,entity => entity.Ships.Select(s => s.CrewMembers, retornará uma lista de CrewMembers que estão relacionados a cada nave. Isso retorna corretamente o gráfico onde um porto contém uma lista de navios, cada um com uma lista de membros da tripulação.

No entanto, a segunda projeçãoCrewMembers.Select(cm => cm.Rank, de fato não retorna a parte correta do gráfico. Os campos começam a ser misturados e os campos que compartilham o mesmo nome serão padronizados por qualquer motivo no campo pai. Isso resulta em resultados inconsistentes e, mais importante, em dados ruins. O fato de que nenhum erro é lançado torna isso pior, já que isso só pode ser determinado através da inspeção em tempo de execução.

Se houvesse uma maneira de obter uma resposta única fortemente tipificada (em oposição a uma lista) da primeira projeção, talvez a segunda não fosse necessária. Como é agora, acredito que a questão está na primeira projeção retornando uma lista. Quando a segunda projeção tenta projetar com base nessa lista em vez de em um único objeto, o erro lógico é introduzido.

Se, em vez de CrewMembers ser um ICollection, era apenas um CrewMember, então essa projeção aninhada retornaria os dados corretos. No entanto, essa é uma versão simplificada desse problema e, infelizmente, é o que quase todos os testes parecem ter sido feitos nos vários blogs, tutoriais, publicações, artigos e documentos que analisei tentando resolver esse problema.

questionAnswers(3)

yourAnswerToTheQuestion