2017-12-01 5 views
4

以下は私のコードを簡略化したものです。私はGetPerson1()GetPerson2()の唯一の違いは、選択基準を変更せずに、クエリの実行を強制的に.ToList()句であることを考えれば、等しくなるようにp1p2が等しくなるように期待し、またp1_afterp2_afterでしょう。Linq `Where`句のクエリ結果は、クエリが遅延して実行されるのか、遅く実行されるのかによって異なる可能性がありますか?

私の前提は間違っていますか?私はp1_afterp2_afterが異なる(p2_afterは年齢が26に変更されたため、期待どおりにnullになっています)、私のプログラムでバグを発見しました。しかしp1_afterにはまだp1と同じインスタンスが含まれています。

この動作は正常ですか?私がチェックしたいのは私には論理的ではないようです。 p1_afterにもかかわらずp1_after.Age戻り26は、そのAgeが25

public void OnGet() 
    { 
     Person p1 = GetPerson1(); 
     Person p2 = GetPerson2(); 

     p1.Age = 26; 
     p2.Age = 26; 

     Person p1_after = GetPerson1(); // not null, but p1_after.Age is 26 
     Person p2_after = GetPerson2(); // null 
    } 

    public Person GetPerson1() 
    { 
     return _context 
      .Persons 
      .Where(p => p.Age == 25) 
      .SingleOrDefault(); 
    } 

    public Person GetPerson2() 
    { 
     return _context 
      .Persons 
      .ToList() 
      .Where(p => p.Age == 25) 
      .SingleOrDefault(); 
    } 
+0

いずれかのefは、 '.ToList()'を使うときに何らかの形のメモリキャッシュを使うか、バグです。私は彼らのgithubに行くことをお勧めしたい、問題を作成し、devsがこれについて何を言わなければならないか見る。 –

+0

データベースからエンティティを再フェッチするときにEFがチェンジ・トラッカーのローカル変更を上書きしないことがわかっている場合は、すべて意味があります。 'p1'、' p2'、 'p1_after'は同じオブジェクトへの参照ですが、' p2_after'は何も出てこないクエリの結果に過ぎません。 –

答えて

2

であるように選択されていた。特に、これは野生の推測ですが、私はあなたのプログラムがそのように振る舞う理由を仮定しています。

年齢を変更した後は、SaveChanges/SaveChangesAsyncのいずれのメソッドも呼び出さなかったので、変更はデータベースに反映されず、ローカルのコードに反映されません。

GetPerson1メソッドを再度呼び出すと、年齢が25歳の人物をデータベースから取得するように求められます。データベースが変更によって反映されていないため、前と同じ結果になります。
GetPerson2メソッドを呼び出すと、別の結果が得られる理由は、私の推測になります。GetPerson2メソッドでは、すべての人物をメモリに入れて、メモリ内の結果をフィルタリングするToListメソッドを実行しますGetPersons1メソッドフィルタリングがデータベースレベルで行われるとき、同じコンテキストを使用しているので、GetPerson2メソッドを2回呼び出すと、そのようなものになります。EntityFrameworkは、フィルタリングするリストを作成するすべての人物を取得するキャッシュメカニズムを使用します。あなたの変更とこのリストには年齢25の人がいないので、p2_afterはnullです。私の仮定を確認するか、拒否すること

私は3つの異なるシナリオしようとします:私はp1_afterとp2_afterを推測する。この場合

public void OnGet() 
{ 
    Person p1 = GetPerson1(); 
    Person p2 = GetPerson2(); 

    p1.Age = 26; 
    p2.Age = 26; 

    _context.SaveChanges(); 

    Person p1_after = GetPerson1(); 
    Person p2_after = GetPerson2(); 
} 

:2つの呼び出し間でデータベースへの変更を保存し

  • を変更がデータベースにも反映されるため、同じ(両方ともnull)になります。

  • 呼び出しごとに新鮮なコンテキスト使用:今、あなたの変更はに反映されませんので、私はp1_afterとp2_afterが同じになると思います。この場合、

    public void OnGet() 
    { 
        Person p1 = GetPerson1(); 
        Person p2 = GetPerson2(); 
    
        p1.Age = 26; 
        p2.Age = 26; 
    
        Person p1_after = GetPerson1(); 
        Person p2_after = GetPerson2(); 
    } 
    
    public Person GetPerson1() 
    { 
        using(var context = new ...) 
        { 
         return context 
          .Persons 
          .Where(p => p.Age == 25) 
          .SingleOrDefault(); 
        } 
    } 
    
    public Person GetPerson2() 
    { 
        using(var context = new ...) 
        { 
         return context 
          .Persons 
          .ToList() 
          .Where(p => p.Age == 25) 
          .SingleOrDefault(); 
        } 
    } 
    

    を(P1およびP2の両方が前にいました)あなたが毎回新しいコンテキストを使用しているので、キャッシングの実現可能性はありません。

  • 使用AsNoTracking:この場合

    public void OnGet() 
    { 
        Person p1 = GetPerson1(); 
        Person p2 = GetPerson2(); 
    
        p1.Age = 26; 
        p2.Age = 26; 
    
        Person p1_after = GetPerson1(); 
        Person p2_after = GetPerson2(); 
    } 
    
    public Person GetPerson1() 
    { 
        return _context 
         .Persons 
         .AsNoTracking() 
         .Where(p => p.Age == 25) 
         .SingleOrDefault(); 
    } 
    
    public Person GetPerson2() 
    { 
        return _context 
         .Persons 
         .ToList() 
         .AsNoTracking() 
         .Where(p => p.Age == 25) 
         .SingleOrDefault(); 
    } 
    

    は私がp1_afterとp2_afterがなる推測同じ(両方P1、P2は前にあったように)今EFトラッキングが無効になっているため - AKAキャッシングなし。

+0

あなたは何が起こっているのか正確に説明したと思います。 (これは私の前提でしたが、この質問を書いています)。しかし、私にとっては、これはフレームワークの中では間違った/一貫性のない問題です。私が 'SaveChanges()'を呼び出すことができない理由は、コード内でさらに変更があっても、変更のセット全体を中止することができるからです。その場合、単に 'SaveChanges()'を呼び出すことはなく、何も保持されません。 – Laurent

関連する問題