web-dev-qa-db-ja.com

Entity Framework:外部キーを設定し、SaveChangesでナビゲーションプロパティにアクセスしますが、関連するエンティティをロードしません。何故なの?

Entity Framework 5 Code FirstでこのEntityクラスを使用しています:

public class Survey
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

    public string SurveyName { get; set; }

    [Required]
    public int ClientID { get; set; }

    [ForeignKey("ClientID")]
    public virtual Client Client { get; set; }
}

そして、コントローラーのCreateメソッドでこれを行います:

    Survey entity = new Survey()
    {
        SurveyName = "Test Name",
        ClientID = 4
    };
    db.Surveys.Add(entity);
    db.SaveChanges();
    Client c1 = entity.Client;                    //Why is this null?
    Client c2 = db.Clients.Find(entity.ClientID); //But this isn't?

    string s2 = c2.ClientName;
    string s1 = c1.ClientName;   //null reference thrown here

SaveChangesの後、クライアントナビゲーションプロパティはnullのままです。外部キーが存在するため、データベースからクライアントをロードする呼び出しを予期していました。なぜそうしなかったのですか?

[〜#〜] edit [〜#〜]ここでのコードは、コントローラーがDbContextに依存していたときのものです。これが機能するようになってからまもなく、リポジトリと作業単位を使用するようにコードをリファクタリングしました。その動きの一部は、Createを使用したいときにnewを使用するのは間違っていると感じたという事実に基づいています。そのとき起こったことは、 リポジトリパターンを使用するときにプロキシが作成されるようにする方法 で問題が発生したことです。

35
Colin

親を作成した後にナビゲーションプロパティの遅延読み込みが機能するようにするには、Survey演算子でnewを作成するのではなく、コンテキストインスタンスを使用して作成する必要があります。関連するClientを遅延ロードできる動的プロキシをインスタンス化します。それがDbSet<T>.Create()メソッドの目的です:

_Survey entity = db.Surveys.Create();

entity.SurveyName = "Test Name";
entity.ClientID = 4;

db.Surveys.Add(entity);
db.SaveChanges();

Client c1 = entity.Client;
string s1 = c1.ClientName;
// will work now if a Client with ID 4 exists in the DB
_

強調するのは、DBからクライアントをロードするのは_entity.ClientID = 4;_行またはdb.Surveys.Add(entity);または_db.SaveChanges_行ではなく、_Client c1 = entity.Client;_行(遅延ロード)です。

56
Slauma

@NicholasButlerが言ったように、SaveChangesを呼び出すと、スズで言うことを行います-コードをデバッグすると、これを見ることができます:Intellitrace出力は、挿入/更新のために生成したSQLを表示しますが、その後の選択はありません。

Includeメソッドを使用して)積極的なロードを行わない限り、関連するエンティティは取得時にロードされないため、それらを作成または更新することもできないのは当然です。

Entity Framework(バージョン4.1以降)は遅延読み込みをサポートしています。これが意味することは、それが有効になっている場合、Client c1 = entity.Client;shouldClientオブジェクトをロードします。明確にするために、この操作はSaveChanges呼び出しに直接関連していません。

db.Configuration.LazyLoadingEnabledtrueに設定されます。そうでない場合は、trueに設定してみて、Client c1 = entity.Client;はまだnullです。

つまり、SaveChangesを呼び出してもロードはトリガーされませんが、遅延ロードが有効になっている場合は、entity.Clientは、まだ読み込まれていない場合、エンティティの読み込みをトリガーする必要があります。

編集:

これについては以前に知っておくべきでしたが、あなたはSurvey entityオブジェクト。その理由は、EFは、自分のクラスから派生したクラスを作成することで遅延ロードマジックを機能させるが、遅延ロードをサポートするためにvirtualとマークされたプロパティをオーバーライドするからです。取得を実行するときにこれを行うため、entityオブジェクトは、現状のまま遅延ロードを行いません。

SaveChangesへの呼び出しの直後にこれを試してください:

Survey entity2 = db.Surveys.Find(entity.ID);
Client c1 = entity2.Client;

これは、後の動作を示すはずです。

10
nick_w

遅延読み込みを有効にするには、Surveyクラスのすべてのプロパティを仮想として定義する必要があります。

詳細については、 http://msdn.Microsoft.com/en-us/library/vstudio/dd468057(v = vs.100).aspx を参照してください。

0
w.brian