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.