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
FYI, щоб використовувати TRUNCATE, користувач повинен мати дозвіл ALTER на столі. ( stackoverflow.com/questions/4735038/… )
— Олексій

7
@Alex Щойно витрачав багато часу на помилку "Неможливо знайти об'єкт MyTable, оскільки його не існує або у вас немає дозволів." саме з цієї причини - дозволу ALTER рідко надаються додаткам EF, і повідомлення про помилку дійсно надсилає вас на погоню за дикими гусками.
— Кріс Москіні

8
У мене виникли проблеми, оскільки мій стіл був частиною закордонних ключових відносин, хоча це була таблиця листів у цих відносинах. Я завершився використанням контексту.Database.ExecuteSqlCommand ("ВИДАЛИТИ З [Інтереси]"); натомість
— Дан Чшарпстер

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;}
}

Для коду Tidier ви можете оголосити наступний метод розширення:

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 s, а не IEnumerable, IList, ICollection, ICache або будь-якого іншого інтерфейсу, який потрібен "Clear". перевагою методу розширення є тип, за яким визначаються. але якщо це чіткіше для вас і не звучить зайвим, чудово! я радий, що це допомагає мудрістю! Ура!
— Ахмед Алехо

Я радий, що ви заявили лише для невеликих таблиць. Я сподіваюся, що люди отримають важливість вашого пояснення. оскільки цей спосіб є синтаксичним цукром, правильним є спосіб, який запропонував Рон Сійм. тому що ви не завантажуєте дані перед їх видаленням. привіт, хоча за показ та пояснення цього способу.
— yedevtxt

3
Це не скидає ідентифікаційний ключ. Тож якщо ви очистите 10 записів, наступний все одно буде 11.
— MDave,

89

Використання TRUNCATE TABLEкоманди SQL буде найшвидшим, оскільки воно працює на столі, а не на окремих рядках.

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

Якщо припустити dataDb, що це DbContext(не an ObjectContext), ви можете обернути його та використовувати такий метод:

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

Якщо ви отримаєте помилку дозволів при спробі цього, просто змініть TRUNCATE TABLEнаDELETE FROM
— codeMonkey

1
@codeMonkey просто зауважте, що це різні операції, але вони матимуть (майже) той самий чистий ефект 👍
— Руді Віссер

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 and EntityFrameWork у поточному проекті ur
— Manish Mishra

Який метод розширення - Видалити?
— Ахмед Алехо

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

Я редагував свою відповідь, раніше вона була оманливою. @null, ви маєте рацію, це .Deleteбуло власне розширення, і в запалі опублікування відповіді спочатку зовсім забув згадати визначення цього звичаю .Delete. :)
— Manish Mishra

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підхід, оскільки це набагато швидше, і це також ROLLBACK . Він також скидає ідентичність.

Приклад з використанням шаблону репозиторію:

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. Якщо метою є очищення всіх таблиць у БД для тестування одиниць, проблеми з обмеженнями зовнішніх ключів можна легко вирішити, наказавши таблиці обробляти їх таким чином, щоб дочірні таблиці обрізалися перед батьківськими таблицями.
— Крістоф

@Christoph - 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) Це не скине лічильник ідентифікаторів, якщо він встановлений для автоматичного збільшення (урізає).
— 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 ("деякий 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 

Важливим моментом є те, що якщо ви використовуєте сторонні ключі у своїх таблицях, спочатку потрібно видалити дочірню таблицю перед батьківською таблицею, тому послідовність (ієрархія) таблиць під час видалення є важливою, інакше може статися виняток SQLite.

Примітка: var контекст = новий 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;
}

навіщо ви витягували всі записи, щоб видалити їх? вкрай неефективно
— кобила

Тому що виконання не було моїм пріоритетом. Він заснований на модульності, тому якщо ви хочете додати умову де або перевірити дані, перш ніж видаляти їх, ви можете. EF6 - найповільніший інструмент вводу / виводу SQL, тому чому питання використання EF6, якщо продуктивність була пріоритетним питанням, має бути питанням ..
— Роберто Мутті

0

У моєму коді я не мав приємного доступу до об’єкта Бази даних, тому ви можете це зробити на 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.