EFで奇妙なことが発生し、.Include(x=x.T)
ステートメントを評価するときに使用するフィールドを決定する方法に気づきました。
私たちのプロジェクトでは、すべてのデータベーステーブル(したがってEFのPOCO)の前に接頭辞が付いており、DB
問題の2つのテーブルはとです。各アイテムには、(オプションの)テンプレートが関連付けられています。DBTemplates
DBItems
私たちの関連する行DbContext
はこれです:
public IDbSet<DBTemplate> Templates {get;set;}
public IDbSet<DBItem> Items {get;set;}
簡単にするために、両方のテーブルにIdプロパティとNameプロパティが含まれていることと、DBTemplatesへの外部キーを持つDBItemsテーブルが含まれていることを前提としています。これは(今のところ)呼ばれTemplate_Id
ています(理由は後で説明します)が、一般的に言えば、名前を付けますDBTemplateId
。
一般的に言えば、それはdb
私たちのインスタンスであるという変数がありますDbContext
。
最初にDBItem
定義を作成した開発者は、関連するにリンクする仮想プロパティを追加しましたDBTemplate
が、外部キーを省略しました。プロパティは次のようになります。
public class DBItem
{
public System.Guid Id { get; set; }
public string Name { get; set; }
public virtual DBTemplate Template { get; set; }
}
public class DBTemplate
{
public System.Int32 Id { get; set; }
public string Name { get; set; }
}
アイテムを照会すると、次のステートメントがヒットします
db.Items.Include(i=>i.Template)
次のSQLが生成されます
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent2].[Id] AS [Id1],
[Extent2].[Name] AS [Name1],
FROM [dbo].[DBItems] AS [Extent1]
LEFT OUTER JOIN [dbo].[DBTemplates] AS [Extent2] ON [Extent1].[Template_Id] = [Extent2].[Id]
ここまでは順調ですね。Template_Id
ただし、 DBItemオブジェクトにはが必要です(必要です) 。DBItemクラスに追加すると、次のようになります。
public class DBItem
{
public System.Guid Id { get; set; }
public string Name { get; set; }
public Nullable<System.Int32> Template_Id { get; set; }
public virtual DBTemplate Template { get; set; }
}
同じ行がヒットしましたが、生成されたSQLは次のようになります
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Template_Id] AS [Template_Id],
[Extent2].[Id] AS [Id1],
[Extent2].[Name] AS [Name1],
FROM [dbo].[DBItems] AS [Extent1]
LEFT OUTER JOIN [dbo].[DBTemplates] AS [Extent2] ON [Extent1].[Template_Id1] = [Extent2].[Id]
(結合のTemplate_Id1に注意してください)
モデルビルダーに、という名前の外部キーを探すように指示できるので、これは簡単に修正できますTemplate_Id
。すべて問題ありません。
builder.Entity<DBItem>().HasOptional(x => x.Template).WithMany().HasForeignKey(x => x.Template_Id);
ただし、規則に従い、という名前のフィールドとDBTemplateId
DBItemクラスが次のようになっている場合:
public class DBItem
{
public System.Guid Id { get; set; }
public string Name { get; set; }
public Nullable<System.Int32> DBTemplateId { get; set; }
public virtual DBTemplate Template { get; set; }
}
ModelBuilderプロパティを変更せずに(EFが純粋に慣例に従って引き継ぐようにする)、DBTemplateIdプロパティを外部キーとして正しく取得し、次のようなSQLを生成します。
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[DBTemplateId] AS [DBTemplateId],
[Extent2].[Id] AS [Id1],
[Extent2].[Name] AS [Name1],
FROM [dbo].[DBItems] AS [Extent1]
LEFT OUTER JOIN [dbo].[DBTemplates] AS [Extent2] ON [Extent1].[DBTemplateId] = [Extent2].[Id]
クエリは意図したとおりに機能します(エラーが発生しやすいC#コードも少なくなります)。
明らかに、ここでのレッスンはフィールドの名前付けのパターンに従うことですが、EFは名前が付けられたときにプロパティ名を結合キーとして自動的に見つけて使用し、POCOに存在する場合DBTemplateId
は重複を探すのはなぜですか?Template_Id1
Template_Id