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.