Entity Framework を使用してテーブル内のすべての行をすばやく削除するにはどうすればよいですか?
私は現在使用しています:
var rows = from o in dataDb.Table
select o;
foreach (var row in rows)
{
dataDb.Table.Remove(row);
}
dataDb.SaveChanges();
ただし、実行には長い時間がかかります。
代替手段はありますか?
Entity Framework を使用してテーブル内のすべての行をすばやく削除するにはどうすればよいですか?
私は現在使用しています:
var rows = from o in dataDb.Table
select o;
foreach (var row in rows)
{
dataDb.Table.Remove(row);
}
dataDb.SaveChanges();
ただし、実行には長い時間がかかります。
代替手段はありますか?
これをグーグルで調べて、私のようにここにたどり着いた人のために、これは現在EF5とEF6でそれを行う方法です:
context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
コンテキストがSystem.Data.Entity.DbContext
警告:以下は小さなテーブルにのみ適しています (1000 行未満と考えてください)。
これはエンティティ フレームワーク (SQL ではない) を使用して行を削除するソリューションであるため、SQL エンジン (R/DBM) 固有ではありません。
これは、テストまたは同様の状況でこれを行っていることを前提としています。また
単に呼び出す:
VotingContext.Votes.RemoveRange(VotingContext.Votes);
次のコンテキストを想定します。
public class VotingContext : DbContext
{
public DbSet<Vote> Votes{get;set;}
public DbSet<Poll> Polls{get;set;}
public DbSet<Voter> Voters{get;set;}
public DbSet<Candidacy> Candidates{get;set;}
}
コードを整理するには、次の拡張メソッドを宣言できます。
public static class EntityExtensions
{
public static void Clear<T>(this DbSet<T> dbSet) where T : class
{
dbSet.RemoveRange(dbSet);
}
}
次に、上記は次のようになります。
VotingContext.Votes.Clear();
VotingContext.Voters.Clear();
VotingContext.Candidacy.Clear();
VotingContext.Polls.Clear();
await VotingTestContext.SaveChangesAsync();
私は最近、このアプローチを使用して、テストケースの実行ごとにテストデータベースをクリーンアップしました (生成された削除コマンドの形式を確認していませんが、DB を毎回ゼロから再作成するよりも明らかに高速です)。
なぜ遅くなることがありますか?
したがって、大量のデータを処理している場合は、SQL サーバー プロセスを強制終了し (すべてのメモリを消費します)、IIS プロセスについても同じことを行います。これは、EF が SQL サーバーと同じ方法ですべてのデータをキャッシュするためです。テーブルに大量のデータが含まれている場合は、これを使用しないでください。
SQL のTRUNCATE TABLE
コマンドを使用すると、個々の行ではなくテーブルで動作するため、最も高速になります。
dataDb.ExecuteStoreCommand("TRUNCATE TABLE [Table]");
dataDb
がDbContext
(ではない) であると仮定するとObjectContext
、それをラップして、次のようなメソッドを使用できます。
var objCtx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)dataDb).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [Table]");
using (var context = new DataDb())
{
var ctx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)context).ObjectContext;
ctx.ExecuteStoreCommand("DELETE FROM [TableName] WHERE Name= {0}", Name);
}
また
using (var context = new DataDb())
{
context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
}
これにより、SQLの使用が回避されます
using (var context = new MyDbContext())
{
var itemsToDelete = context.Set<MyTable>();
context.MyTables.RemoveRange(itemsToDelete);
context.SaveChanges();
}
特定のケースに対処しなければならなかったときに、この質問に出くわしました。「リーフ」テーブルのコンテンツを完全に更新します(FKがそれを指していません)。これには、すべての行を削除して新しい行情報を配置する必要があり、トランザクションで実行する必要があります (何らかの理由で挿入が失敗した場合、空のテーブルになってしまいたくありません)。
アプローチを試みましたpublic static void Clear<T>(this DbSet<T> dbSet)
が、新しい行が挿入されません。もう 1 つの欠点は、行が 1 つずつ削除されるため、プロセス全体が遅くなることです。
TRUNCATE
そのため、はるかに高速でROLLBACKableであるため、アプローチに切り替えました。また、ID をリセットします。
リポジトリ パターンを使用した例:
public class Repository<T> : IRepository<T> where T : class, new()
{
private readonly IEfDbContext _context;
public void BulkInsert(IEnumerable<T> entities)
{
_context.BulkInsert(entities);
}
public void Truncate()
{
_context.Database.ExecuteSqlCommand($"TRUNCATE TABLE {typeof(T).Name}");
}
}
// usage
DataAccess.TheRepository.Truncate();
var toAddBulk = new List<EnvironmentXImportingSystem>();
// fill toAddBulk from source system
// ...
DataAccess.TheRepository.BulkInsert(toAddBulk);
DataAccess.SaveChanges();
もちろん、すでに述べたように、このソリューションは外部キーによって参照されるテーブルでは使用できません (TRUNCATE は失敗します)。
データベース全体をクリアしたい場合。
外部キー制約のため、テーブルがどのシーケンスで切り捨てられるかが重要です。これは、このシーケンスをブルートフォースする方法です。
public static void ClearDatabase<T>() where T : DbContext, new()
{
using (var context = new T())
{
var tableNames = context.Database.SqlQuery<string>("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME NOT LIKE '%Migration%'").ToList();
foreach (var tableName in tableNames)
{
foreach (var t in tableNames)
{
try
{
if (context.Database.ExecuteSqlCommand(string.Format("TRUNCATE TABLE [{0}]", tableName)) == 1)
break;
}
catch (Exception ex)
{
}
}
}
context.SaveChanges();
}
}
利用方法:
ClearDatabase<ApplicationDbContext>();
この後、DbContext を再インスタンス化することを忘れないでください。
以下はSQLiteデータベース(Entity Frameworkを使用)で動作します。
context.Database.ExecuteSqlCommand("some SQL")
上記のいくつかのコメントでも強調されているように、すべての db テーブルをクリアする最速の方法は を使用しているようです。ここでは、テーブルの「インデックス」カウントもリセットする方法を示します。
context.Database.ExecuteSqlCommand("delete from TableA");
context.Database.ExecuteSqlCommand("delete from sqlite_sequence where name='TableA'");//resets the autoindex
context.Database.ExecuteSqlCommand("delete from TableB");
context.Database.ExecuteSqlCommand("delete from sqlite_sequence where name='TableB'");//resets the autoindex
context.Database.ExecuteSqlCommand("delete from TableC");
context.Database.ExecuteSqlCommand("delete from sqlite_sequence where name='TableC'");//resets the autoindex
重要な点の 1 つは、テーブルで外部キーを使用する場合、親テーブルの前にまず子テーブルを削除する必要があるため、削除中のテーブルの順序 (階層) が重要です。そうしないと、SQLite 例外が発生する可能性があります。
ノート:var context = new YourContext()
これは EF 5 で適切に機能します。
YourEntityModel myEntities = new YourEntityModel();
var objCtx = ((IObjectContextAdapter)myEntities).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [TableName]");