Oracleで確認されたバグレポートは次のとおりです。http://bugs.mysql.com/bug.php?id = 67183
状況
リポジトリ内でチェーンを使用する.Include
と、奇妙な結果が得られることに気付きました。ほとんどの場合、返されるクエリの値は間違ったフィールドからのものでした(たとえば、名前は説明に表示されます)が、データベースではすべての値が正解です。クエリの後でのみ間違って表示されます)。関係がわかりやすくなるように名前を変更しましたが、構造は同じです。関連するCrewMemberとそれらの相対的なランクとクリアランスに対して間違った値を取得し続けます。CrewMemberにRankと同じフィールド名がある場合、Rankのそのフィールドの値は、CrewMemberの値になります。たとえば、ランクに説明があり、CrewMemberにも説明がある場合、CrewMemberのランクの説明はCrewMemberの説明になります。
join
MySQL Connector / NET sqlプロバイダーがステートメントを適切に形成できなかった結果として同様のフィールドが定義されている場合、EntityFrameworkは深さ2を超えて整形式のクエリを作成できません。
定義
これは、データベーステーブルをモデル化するクラス定義です。EntityFramework4.1およびMySQLConnector/NETバージョン6.5でC#ASP.NETMVC3を使用しています
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を使用しても問題はありません。ただし、この問題は、掘削の一部として1対多の関係がある場合に発生するようです。負荷を熱くするために穴あけが必要です。
最初のプロジェクションはentity => entity.Ships.Select(s => s.CrewMembers
、各船に関連するCrewMembersのリストを返します。これにより、港に船のリストが含まれ、それぞれに乗組員のリストが含まれるグラフが適切に返されます。
ただし、2番目の射影CrewMembers.Select(cm => cm.Rank
は、実際にはグラフの適切な部分を返しません。フィールドが混在し始め、同じ名前を共有するフィールドは、何らかの理由で親フィールドにデフォルト設定されます。これにより、結果に一貫性がなくなり、さらに重要なことにデータが不良になります。エラーがスローされないという事実は、実行時の検査によってのみ判断できるため、さらに悪化します。
最初の射影から(リストではなく)強く型付けされた単一の応答を何らかの方法で取得する方法があれば、おそらく2番目の射影は必要ないでしょう。今のところ、問題はリストを返す最初の予測にあると思います。2番目の投影が単一のオブジェクトからではなく、そのリストに基づいて投影しようとすると、論理エラーが発生します。
CrewMembersがICollectionである代わりに、それが1つのCrewMemberのみであった場合、このネストされたプロジェクションは実際に正しいデータを返します。ただし、これはこの問題の簡略化されたバージョンであり、残念ながら、この問題を解決するためにレビューしたさまざまなブログ、チュートリアル、投稿、記事、およびドキュメントから、ほとんどすべてのテストが行われたようです。