Entity Framework. Удалить все строки в таблице


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адептов не беспокоится об ограничениях внешнего ключа.
— Герт Арнольд

2
Я немного удивлен тем, что ответы здесь просто принимают как должное то, что все используют Microsoft SQL Server, хотя поддержка других баз данных в Entity Framework восходит настолько далеко, насколько я могу найти информацию, и определенно предшествует этому вопросу на несколько лет. , Совет: если ответ заключает в кавычки имена таблиц в операторах SQL (например:) [TableName], он не переносим.
— Марк Эмери

Ответы:


293

Для тех, кто гуглит это и попал сюда, как я, вот как вы в настоящее время делаете это в EF5 и EF6:

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

Предполагая, что контекст System.Data.Entity.DbContext


20
К вашему сведению, чтобы использовать TRUNCATE, пользователь должен иметь разрешение ALTER для таблицы. ( stackoverflow.com/questions/4735038/… )
— Алекс

7
@Alex Просто потратил кучу времени на ошибку «Не удается найти объект MyTable, потому что он не существует или у вас нет прав». именно по этой причине - разрешения ALTER редко предоставляются приложениям EF, и сообщение об ошибке действительно отправляет вас в погоню.
— Крис Москини

8
У меня были проблемы, так как моя таблица была частью отношения внешнего ключа, хотя это была конечная таблица в этих отношениях. Я завелся с использованием context.Database.ExecuteSqlCommand ("DELETE FROM [Interests]"); вместо этого
— Дэн Чарпстер

2
Обратите внимание, что хотя [экранирование здесь специфично для SQL Server, TRUNCATEкоманда не является - она ​​является частью ANSI SQL и поэтому будет работать на большинстве диалектов SQL (хотя и не на SQLite).
— Марк Амери

207

Предупреждение: следующее подходит только для небольших таблиц (например, <1000 строк)

Вот решение, которое использует структуру сущностей (не SQL) для удаления строк, поэтому оно не является специфичным для SQL Engine (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();

Недавно я использовал этот подход для очистки своей тестовой базы данных при каждом запуске тестового набора (это, очевидно, быстрее, чем воссоздание БД с нуля каждый раз, хотя я не проверял форму сгенерированных команд удаления).


Почему это может быть медленным?

  1. EF получит ВСЕ строки (VotingContext.Votes)
  2. а затем будет использовать их идентификаторы (не знаю точно, как, не имеет значения), чтобы удалить их.

Поэтому, если вы работаете с серьезным объемом данных, вы убьете процесс сервера SQL (он будет занимать всю память) и то же самое для процесса IIS, поскольку EF будет кэшировать все данные так же, как сервер SQL. Не используйте этот, если ваша таблица содержит серьезный объем данных.


1
Отличный ответ, ускорил мой код удаления всех строк в 10 раз! Обратите внимание, что мне пришлось переименовать метод статического расширения Clear () в нечто вроде ClearDbSet (), поскольку у меня уже был другой метод статического расширения Clear (), определенный в другом месте моего проекта.
— dodgy_coder

1
Переименование @dodgy_coder не требуется по указанной вами причине, поскольку метод расширения предназначен для DbSet, IDbSet, а не для IEnumerable, IList, ICollection, ICache или любого другого интерфейса, для которого требуется «Очистить». предпочтение отдается методу расширения типа, для которого определены. но если вам это кажется понятнее и не звучит излишне, отлично! Я рад, что это помогает в исполнении! Ура!
— Ахмед Алехо

Я рад, что вы указали только для небольших столов. Я надеюсь, что люди понимают важность вашего объяснения. потому что этот путь - синтаксический сахар, правильный путь - это способ, предложенный Роном Сиймом. потому что вы не загружаете данные перед их удалением. ура, хотя для показа и объяснения этого способа сделать это.
— yedevtxt

3
Это не сбрасывает ключ идентификации. Итак, если вы очистите 10 записей, следующая будет 11.
— MDave

89

Использование команды 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]");

Если вы получаете ошибку прав доступа при попытке этого, просто измените TRUNCATE TABLEнаDELETE FROM
— codeMonkey

1
@codeMonkey просто обратите внимание, что это разные операции, но они будут иметь (почти) одинаковый чистый эффект net
— Руди Виссер

3
Но DELETE не сбрасывает семя IDENTITY. Это может быть проблематично в определенных ситуациях.
— Стив

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

11
Это не должно использоваться, потому что вы выполняете полный выбор и удаление после, а не просто удаление. С точки зрения времени и производительности это большое НЕ!
— HellBaby

2
@HellBaby Если это редко называется, и, следовательно, производительность не имеет значения.
— Алекс

6
Даже если это редко называют это плохо. Таблица, содержащая всего 3000 записей, может занять до 30 секунд из-за медленного отслеживания изменений EF.
— Незначительное

1
Это подходит только для небольших столов (<1000 строк)
— Амир Туиту

Идеально
— подходит

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 ();" - «Удалить» не распознается
— Женя

1
добавить ссылку на System.Data.Entity и EntityFrameWork в вашем текущем проекте
— Маниш Мишра

Какой метод расширения Удалить?
— Ахмед Алехо

Насколько я знаю , что это не метод расширения Deleteна IQueryable- я предполагаю , Manish использовал что - то вроде EntityFramework.Extended: github.com/loresoft/EntityFramework.Extended
— нуль

Я отредактировал свой ответ, раньше он вводил в заблуждение. @null, вы правы, это .Deleteбыло пользовательское расширение, и в пылу публикации ответа сначала, совершенно забыл упомянуть определение этого обычая .Delete. :)
— Маниш Мишра

23

Вы можете сделать это без Foreach

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

Это удалит все строки


Будет ли усекать элементы свойства навигации?
— Джон Дир

19

Это позволяет избежать использования любого SQL

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

9

Я сталкивался с этим вопросом, когда мне приходилось иметь дело с конкретным случаем: полное обновление контента в «листовой» таблице (без ФК, указывающих на это). Это включало удаление всех строк и добавление информации о новых строках, и это должно быть сделано транзакционно (я не хочу в конечном итоге получить пустую таблицу, если вставка не удалась по какой-либо причине).

Я попробовал public static void Clear<T>(this DbSet<T> dbSet)подход, но новые строки не вставляются. Другим недостатком является то, что весь процесс идет медленно, так как строки удаляются одна за другой.

Итак, я перешел к TRUNCATEподходу, так как это намного быстрее, и это также ROLLBACKable . Это также сбрасывает личность.

Пример использования шаблона хранилища:

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


Два комментария: 1. Имя таблицы должно быть заключено в [...]. Один из моих классов / таблиц называется «Транзакция», который является ключевым словом SQL. 2. Если цель состоит в том, чтобы очистить все таблицы в БД для модульного тестирования, проблемы с ограничениями внешнего ключа можно легко обойти, упорядочив таблицы для их обработки таким образом, чтобы дочерние таблицы усекались перед родительскими таблицами.
— Кристоф

@ Кристоф - 1. Да, это правда. Я пропустил это, потому что я всегда называю таблицы, чтобы избежать ключевых слов, поскольку это может привести к проблемам. 2. Если я правильно помню, таблицы, на которые ссылаются FK, не могут быть усечены (бросает SQL Server Cannot truncate table because it is being referenced by a FOREIGN KEY constraint), даже если они пусты, поэтому FK должны быть отброшены и созданы заново для использования в TRUNCATEлюбом случае.
— Алексей

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 можно упростить до «УДАЛИТЬ ИЗ 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)

Похоже, что самый быстрый способ очистить все таблицы БД - это использовать context.Database.ExecuteSqlCommand («некоторый SQL»), как отмечалось выше в некоторых комментариях. Здесь я собираюсь показать, как сбросить счетчик 'index' таблиц тоже.

            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 

Одним из важных моментов является то, что если вы используете внешние ключи в своих таблицах, вы должны сначала удалить дочернюю таблицу перед родительской таблицей, поэтому важна последовательность (иерархия) таблиц во время удаления, в противном случае может возникнуть исключение 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

Удалить все записи. Не сбрасывайте основной индекс, как «усекать».

/// <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 - самый медленный инструмент для ввода / вывода SQL, поэтому вопрос об использовании EF6, если производительность была приоритетом, должен быть под вопросом ..
— Роберто Мутти,

0

В моем коде у меня не было хорошего доступа к объекту Database, поэтому вы можете сделать это в DbSet, где вам также разрешено использовать любой тип sql. Это будет примерно так:

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

Убедитесь, что при попытке удалить родителя все дочерние элементы будут каскадно удаляться. Или дети имеют обнуляемый внешний ключ.


0
var list = db.Discounts.ToList().Select(x => x as Discount);
foreach (var item in list)
{
    db.Discounts.Remove(item);
}
db.SaveChanges();
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.