ちょっと更新処理とかのことが気になったので試した結果をメモメモ。
こんなデータコンテキストを用意しておきます。
// 簡単データコンテキスト public class SampleContext : DbContext { public DbSet<Person> People { get; set; } } public class Person { public int Id { get; set; } public string Name { get; set; } }
適当にデータを突っ込む場合
// 適当にデータ突っ込む using (var ctx = new SampleContext()) { ctx.People.Add(new Person { Name = "田中1" }); ctx.People.Add(new Person { Name = "田中2" }); ctx.People.Add(new Person { Name = "田中3" }); ctx.People.Add(new Person { Name = "田中4" }); ctx.People.Add(new Person { Name = "田中5" }); ctx.People.Add(new Person { Name = "田中6" }); ctx.SaveChanges(); }
このデータがあるという前提で、DBのデータを書き換える一番一般的な方法
Console.WriteLine("DB更新------------------"); using (var ctx = new SampleContext()) { // DB書き換え foreach (var p in ctx.People) { p.Name += " * "; } Console.WriteLine("{0}件更新", ctx.SaveChanges()); // ローカルのデータを確認 foreach (var p in ctx.People.Local) { Console.WriteLine("{0} {1}", p.Id, p.Name); } }
プロパティを書き換えてSaveChangesを呼びます。DbSetのLocalメソッドでDBに再度アクセスすることなく、ローカルに存在するデータを取得できます。実行結果はこんな感じ。
6件更新 145 田中1 * 146 田中2 * 147 田中3 * 148 田中4 * 149 田中5 * 150 田中6 *
続いて、クエリにAsNoTrackingを指定した場合。Localにデータは入りません。さらにAsNoTrackingで取得したデータを書き換えてSaveChangesしてもDBは更新されません。
using (var ctx = new SampleContext()) { // AsNoTracking foreach (var p in ctx.People.AsNoTracking()) { Console.WriteLine("{0} {1}", p.Id, p.Name); } Console.WriteLine("--"); // Localにデータ入らない foreach (var p in ctx.People.Local) { Console.WriteLine("{0} {1}", p.Id, p.Name); } // データ書き換えても無駄 foreach (var p in ctx.People.AsNoTracking()) { p.Name = "NoTracking太郎"; } Console.WriteLine("{0}件更新", ctx.SaveChanges()); }
こんな感じの実行結果になります。Localにデータが入ってないことがわかります。あとデータ書き換えても更新されません。
163 田中1 164 田中2 165 田中3 166 田中4 167 田中5 168 田中6 -- 0件更新
次に、DbContextのConfigurationから変更を自動で検知しないという状態にしたときの動き。
Console.WriteLine("全部トラッキングしない------------------"); using (var ctx = new SampleContext()) { // 全体的にトラッキングしない ctx.Configuration.AutoDetectChangesEnabled = false; foreach (var p in ctx.People) { Console.WriteLine("{0} {1}", p.Id, p.Name); p.Name += " * "; } Console.WriteLine("{0}件更新", ctx.SaveChanges()); // Localにはデータ入ってる foreach (var p in ctx.People.Local) { Console.WriteLine("{0} {1}", p.Id, p.Name); } // 変更チェックを有効にしてSaveChanges ctx.Configuration.AutoDetectChangesEnabled = true; Console.WriteLine("{0}件更新", ctx.SaveChanges()); }
実行結果を見ると、Localにデータは保持されてるけど変更はDBまでいかない感じです。でも、再度AutoDetectChangesEnabledをtrueにしてSaveChangesを呼ぶと変更箇所を認識してくれるみたいです。
何度もローカルのデータを再利用しつつ、読み取り専用のデータ向きなのかな…?いまいちメリットが把握できない。
193 田中1 194 田中2 195 田中3 196 田中4 197 田中5 198 田中6 0件更新 193 田中1 * 194 田中2 * 195 田中3 * 196 田中4 * 197 田中5 * 198 田中6 * 6件更新
最後はどうにでもな〜れ!な方法。
using (var ctx = new SampleContext()) { // 一括更新みたいなのをトランザクションはってやりたいときの最後の手段 using (var tx = new TransactionScope()) { Console.WriteLine( "{0}件削除", ctx.Database.ExecuteSqlCommand("DELETE FROM PEOPLE")); tx.Complete(); } }
SQL直実行なのでなんでもできます。特定の条件にマッチするものを一括更新とかの場合は、こっちのほうが効率よさそうですね。実行結果は以下のような感じです。
6件削除