エンティティフレームワーク。テーブルのすべての行を削除


280

Entity Frameworkを使用してテーブルのすべての行をすばやく削除するにはどうすればよいですか?

私は現在使用しています:

var rows = from o in dataDb.Table
           select o;
foreach (var row in rows)
{
    dataDb.Table.Remove(row);
}
dataDb.SaveChanges();

ただし、実行に時間がかかります。

代替案はありますか?


22
答えを読むと、これらのTRUNCATE熟練者の誰もが外部キーの制約について心配しないのはなぜでしょうか。
Gert Arnold、

2
Entity Frameworkの他のデータベースのサポートは、この質問に関する情報を見つけることができ、数年前に確かに先行している限り、誰もがMicrosoft SQL Serverを使用していることは当然ですが、 。ヒント:回答がSQLステートメントのテーブル名を角かっこで囲んでいる場合(例:)[TableName]、移植性はありません。
マークアメリー2018

回答:


293

これをググって、私のようにここで終わった人のために、これはあなたが現在EF5とEF6でそれをしている方法です:

context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");

コンテキストが System.Data.Entity.DbContext


20
参考までに、TRUNCATEを使用するには、ユーザーはテーブルに対するALTER権限を持っている必要があります。(stackoverflow.com/questions/4735038/...
アレックス

7
@Alexは、「オブジェクトMyTableが存在しないか、アクセス権がないため、オブジェクトを見つけることができません。」というエラーで膨大な時間を無駄にしました。その正確な理由で-EFアプリにALTER権限が付与されることはほとんどなく、エラーメッセージは実際に野生のガチョウ追跡を送信します。
Chris Moschini、2015年

8
テーブルが外部キーリレーションシップの一部であるため、そのリレーションシップのリーフテーブルであるにもかかわらず、問題が発生しました。context.Database.ExecuteSqlCommand( "DELETE FROM [Interests]");を使用して終了しました。代わりに
Dan Csharpster

2
ここでの[エスケープはSQL Serverに固有のものですが、TRUNCATEコマンドはANSI SQLの一部であるため、ほとんどのSQL方言(SQLiteではありません)で機能します。
マークアメリー2018

207

警告:以下は小さなテーブルにのみ適しています(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を最初から再作成するよりも明らかに高速です)。


なぜ遅いのでしょうか?

  1. EFはすべての行を取得します(VotingContext.Votes)
  2. 次に、それらのIDを使用して(正確にはわからない、重要ではありません)、それらを削除します。

したがって、EFがすべてのデータをSQLサーバーと同じ方法でキャッシュするため、深刻な量のデータを処理している場合は、SQLサーバープロセスを強制終了し(すべてのメモリを消費します)、IISプロセスと同じことになります。テーブルに大量のデータが含まれている場合は、これを使用しないでください。


1
すばらしい回答です。すべての行の削除コードを10倍高速化しました。Clear()静的拡張メソッドの名前をClearDbSet()などに変更する必要があることに注意してください。プロジェクトの他の場所で別のClear()静的拡張メソッドが既に定義されているためです。
dodgy_coder 2015年

1
@dodgy_coderの名前の変更は、拡張メソッドがDbSet、IDbSetであり、IEnumerable、IList、ICollection、ICache、または「クリア」が必要なその他のインターフェイスではないため、指定した理由で不要です。拡張メソッドの設定は、が定義されているタイプです。しかし、それが読みやすく、冗長に聞こえない場合は、すばらしいです。私は、それがパフォーマンスに役立つのを嬉しく思います!乾杯!
Ahmed Alejo 2015年

小さなテーブルだけに言ってよかったです。あなたの説明の重要性を人々に理解してもらいたい。この方法は構文糖なので、適切な方法はRon Sijmが提案した方法です。削除する前にデータをロードしないからです。ただし、この方法を示して説明したことを乾杯します。
yedevtxt 2018

3
これはIDキーをリセットしません。あなたは10件のレコードをクリアするのであれば、次はまだ11になります
MDave

89

SQLのTRUNCATE TABLEコマンドを使用すると、個々の行ではなくテーブルを操作するので最速になります。

dataDb.ExecuteStoreCommand("TRUNCATE TABLE [Table]");

dataDbDbContext(ではなくObjectContext)であると仮定すると、それをラップして次のようなメソッドを使用できます。

var objCtx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)dataDb).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [Table]");

これを実行するときに権限エラーが発生する場合は、次のように変更TRUNCATE TABLEしてくださいDELETE FROM
codeMonkey

1
@codeMonkeyは、これらの操作が異なることに注意してください。ただし、(ほぼ)同じ正味の効果がありますV
Rudi Visser '22

3
ただし、DELETEはIDENTITYシードをリセットしません。これは特定の状況で問題となる可能性があります。
スティーブ

43
var all = from c in dataDb.Table select c;
dataDb.Table.RemoveRange(all);
dataDb.SaveChanges();

11
これは使用しないでください。削除するだけでなく、完全選択と削除を実行します。時間パフォーマンスの観点から、これは大きな違いです!
HellBaby 2014年

2
@HellBabyまれにしか呼び出されず、パフォーマンスがまったく関係ない場合を除きます。
Alex

6
めったに呼ばれなくてもこれは悪いです。EF変更追跡の速度が遅いため、3000エントリしかないテーブルでは30秒以上かかる場合があります。
2016年

1
これは小さなテーブル(<1000行)にのみ適しています
Amir Touitou

単体テストでのインメモリデータベースに
最適

38
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]");
}

1
しかし、私がそれを書いたとき。 "query.Delete();" -「削除」が認識されない
Zhenia

1
現在のプロジェクトにSystem.Data.EntityおよびEntityFrameWorkの参照を追加
Manish Mishra

削除とはどのような拡張方法ですか?
Ahmed Alejo 2014年

私の知る限りは、拡張メソッドがありません知っているようDeleteIQueryable-私はマニッシュがEntityFramework.Extendedような何か使用していた推測している:github.com/loresoft/EntityFramework.Extended
nullの

私は私の回答を編集しましたが、以前は誤解を招くものでした。@null、あなたは正しい、これ.Deleteはカスタム拡張であり、最初に回答を投稿するという熱意の中で、このカスタムの定義について言及することを完全に忘れていました.Delete。:)
Manish Mishra 2014年

23

あなたはForeachなしでそれを行うことができます

dataDB.Table.RemoveRange(dataDB.Table);
dataDB.SaveChanges();

すべての行が削除されます


ナビゲーションプロパティアイテムは切り捨てられますか?
John Deer

19

これは、SQLの使用を回避します

using (var context = new MyDbContext())
{
    var itemsToDelete = context.Set<MyTable>();
    context.MyTables.RemoveRange(itemsToDelete);
    context.SaveChanges();
}

9

特定のケースに対処しなければならないときにこの質問に出くわしました:「リーフ」テーブルのコンテンツの完全な更新(それを指す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は失敗します)。


2つのコメント:1.テーブル名は[...]で囲む必要があります。私のクラス/テーブルの1つは「トランザクション」と呼ばれ、SQLキーワードです。2.ユニットテストのためにDB内のすべてのテーブルをクリアすることが目的である場合、外部キー制約の問題は、親テーブルの前に子テーブルが切り捨てられるようにテーブルを順序付けすることで簡単に回避できます。
クリストフ

@Christoph-1.はい、そうです。それはトラブルにつながる可能性があるので、キーワードを避けるために常にテーブルに名前を付けるので、それを逃しました。2.私が正しく覚えている場合、FKによって参照されるテーブルCannot truncate table because it is being referenced by a FOREIGN KEY constraintは空であっても切り捨てることができない(SQL Serverがスローする)ため、とにかく使用するには、 FKを削除して再作成する必要がありますTRUNCATE
Alexei

5

もし

      using(var db = new MyDbContext())
            {
               await db.Database.ExecuteSqlCommandAsync(@"TRUNCATE TABLE MyTable"););
            }

原因

テーブル 'MyTable'はFOREIGN KEY制約によって参照されているため、切り捨てることはできません。

私はこれを使います:

      using(var db = new MyDbContext())
               {
                   await db.Database.ExecuteSqlCommandAsync(@"DELETE FROM MyTable WHERE ID != -1");
               }

1
外部キーに制約がある場合:(1)SQLを "DELETE FROM MyTable"に簡略化できます。(2)自動インクリメントに設定している場合、これはIdカウンターをリセットしません(切り捨ては行います)。
RGH

5
var data = (from n in db.users select n);
db.users.RemoveRange(data);
db.SaveChanges();

4

データベース全体をクリアしたい場合。

外部キーの制約のため、テーブルが切り捨てられる順序が重要です。これは、このシーケンスをブルートフォースする方法です。

    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を再インスタンス化することを忘れないでください。


2

以下はSQLiteデータベースで動作します(Entity Frameworkを使用)

上記のいくつかのコメントも強調されているように、すべてのdbテーブルをクリアする最も速い方法は 'context.Database.ExecuteSqlCommand( "some SQL")'を使用することです。ここでは、テーブルの「インデックス」数もリセットする方法を示します。

            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()


1

これはEF 5で正しく動作します。

YourEntityModel myEntities = new YourEntityModel();

var objCtx = ((IObjectContextAdapter)myEntities).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [TableName]");

1

EFCore(私が使用しているバージョンは3.1)では、次のコマンドを使用してすべての行を削除できます-

context.Database.ExecuteSqlRaw("TRUNCATE TABLE [TableName]");

0

すべてのレコードを削除します。「truncate」のようにプライマリインデックスをリセットしないでください。

/// <summary>
/// SET - DELETE all record by table - no truncate - return deleted records
/// </summary>
public static int setListDelAllMYTABLE()
{
    // INIT
    int retObj = 0;
    using (MYDBEntities ctx = new MYDBEntities())
    {
        // GET - all record
        var tempAllRecord = ctx.MYTABLE.ToList();
        // RESET
        ctx.MYTABLE.RemoveRange(tempAllRecord);
        // SET - final save
        retObj += ctx.SaveChanges();
    }
    // RET
    return retObj;
}

なぜすべてのレコードを引き出して削除するのですか?非常に非効率的

パフォーマンスは私の優先事項ではなかったので。これはモジュール性に基づいているため、where条件を追加したり、データを削除する前にデータを確認したりすることができます。EF6が...パフォーマンスであった場合、なぜEF6を使用すると、優先度が問題である必要があり、SQL I / Oについての最も遅いのツールです
ロベルトMutti

0

私のコードでは、データベースオブジェクトに適切にアクセスできなかったので、任意の種類のSQLを使用することも許可されているDbSetでアクセスできます。次のように終了します。

var p = await _db.Persons.FromSql("truncate table Persons;select top 0 * from Persons").ToListAsync();

0

MVCの場合、次のことができます。

public async Task<IActionResult> DeleteAll()
{
    var list = await _context.YourClass.ToListAsync();
    _context.YourClass.RemoveRange(list);
    await _context.SaveChangesAsync();
    return RedirectToAction(nameof(Index));
}

0

親を削除しようとしているときに、すべての子が削除時にカスケードすることを確認してください。または、子供はnull可能な外部キーを持っています。


0
var list = db.Discounts.ToList().Select(x => x as Discount);
foreach (var item in list)
{
    db.Discounts.Remove(item);
}
db.SaveChanges();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.