0

EFで奇妙なことが発生し、.Include(x=x.T)ステートメントを評価するときに使用するフィールドを決定する方法に気づきました。

私たちのプロジェクトでは、すべてのデータベーステーブル(したがってEFのPOCO)の前に接頭辞が付いており、DB問題の2つのテーブルはとです。各アイテムには、(オプションの)テンプレートが関連付けられています。DBTemplatesDBItems

私たちの関連する行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);

ただし、規則に従い、という名前のフィールドとDBTemplateIdDBItemクラスが次のようになっている場合:

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_Id1Template_Id

4

1 に答える 1

2

Entity Framework Codeが最初に関係を検出すると、いくつかの規則に従って、外部キーとして使用する必要があるプロパティを検出しようとします。

次の3つのルールのいずれかに一致する名前のプロパティを検索します。

  • [Targettype key property name]
  • [Targettype name]+[Targettype key property name]
  • [Foreignkey navigation property name]+[Targettype key property name]

したがってDBTemplateId、2番目のルールと呼ばれるプロパティを追加すると一致し、Code Firstはそのプロパティを外部キーとして正しく使用し、その名前でデータベースに外部キー列を生成します。

ただし、代わりにプロパティを呼び出すと、どのTemplate_Idルールも一致せず、CodeFirstは外部キーの名前を自動的に生成します。

慣例により、生成される名前はになり[Navigation property name]_[Targettype key property name]ます。この場合、結果はになりますTemplate_Id。ただし、その名前のプロパティがすでに存在するため、番号1が追加されます。

コードファーストが、それ自体が自動的に作成するのと同じ名前に一致する4番目のルールを持つことは理にかなっているように私には思えます。その場合Template_Id、正式な名前になり、メソッドを使用して構成する必要はありませんHasForeignKey

于 2013-02-01T21:48:55.997 に答える