엔터티 프레임 워크. 테이블에서 모든 행 삭제


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]이식성이 없습니다.
Mark Amery

답변:


293

인터넷 검색을하고 나와 같은 결과를 얻은 사람들에게는 현재 EF5 및 EF6에서 수행하는 방법입니다.

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

컨텍스트가 System.Data.Entity.DbContext


20
참고로 TRUNCATE를 사용하려면 사용자에게 테이블에 대한 ALTER 권한이 있어야합니다. ( stackoverflow.com/questions/4735038/… )
Alex

7
@Alex "MyTable 개체가 없거나 권한이 없기 때문에 MyTable 개체를 찾을 수 없습니다."오류로 인해 많은 시간을 낭비했습니다. 정확한 이유로-ALTER 권한은 EF 앱에 거의 부여되지 않으며 오류 메시지는 실제로 거위 추적에 따라 전송됩니다.
Chris Moschini

8
테이블이 외래 키 관계의 일부이기 때문에 문제가 있었지만 그 관계의 리프 테이블 이었지만 말입니다. context.Database.ExecuteSqlCommand ( "DELETE FROM [Interests]");를 사용하여 상처를 입었습니다. 대신
Dan Csharpster

2
[여기서 이스케이프는 SQL Server에만 적용 되지만 TRUNCATE명령은 ANSI SQL의 일부가 아니므로 SQLite가 아닌 대부분의 SQL 방언에서 작동합니다.
Mark Amery

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를 사용하여 (정확한 방법은 중요하지 않음) ID를 삭제합니다.

따라서 많은 양의 데이터로 작업하는 경우 EF가 SQL Server와 동일한 방식으로 모든 데이터를 캐시하므로 IIS 서버에 대해 SQL 서버 프로세스 (모든 메모리를 소비 함)를 종료시킵니다. 테이블에 많은 양의 데이터가 포함되어 있으면 이것을 사용하지 마십시오.


1
좋은 대답, 모든 행 코드 삭제 속도를 10 배로 높였습니다! 내 프로젝트의 다른 곳에 다른 Clear () 정적 확장 메서드가 이미 정의되어 있으므로 Clear () 정적 확장 메서드의 이름을 ClearDbSet ()과 같은 것으로 바꾸어야합니다.
dodgy_coder

1
@dodgy_coder 이름 바꾸기는 확장 방법이 DbSet, IDbSet 및 IEnumerable, IList, ICollection, ICache 또는 "Clear"가 아닌 다른 인터페이스를위한 것이기 때문에 사용자가 지정한 이유로 필요하지 않습니다. 확장 방법에 대한 기본 설정은 정의 된 유형입니다. 그러나 그것이 더 명확하게 읽히고 중복 소리가 들리지 않으면 Great !. 나는 그것이 현명한 성능을 돕는 것이 기쁘다! 건배!
Ahmed Alejo

작은 테이블에 대해서만 말씀해 주셔서 감사합니다. 나는 사람들이 당신의 설명의 중요성을 얻길 바랍니다. 이 방법은 구문 설탕이기 때문에 올바른 방법은 Ron Sijm이 제안한 방법입니다. 제거하기 전에 데이터를로드하지 않기 때문입니다. 이 방법을 보여주고 설명하기 위해 건배.
yedevtxt

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는 이것들이 다른 작업이지만, 거의 동일한 순 효과를 가질 것이라는 점에 유의하십시오.
Rudi Visser

3
그러나 DELETE는 IDENTITY 시드를 재설정하지 않습니다. 특정 상황에서는 문제가 될 수 있습니다.
스티브

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

11
삭제 대신 전체 선택 및 삭제 후에 실행하도록해서는 안됩니다. 시간 성능 측면에서 볼 때 이것은 대단하지 않습니다!
HellBaby

2
@HellBaby 드물지 않는 한 성능이 크게 관련이 없습니다.
Alex

6
거의 호출되지 않더라도 나쁘다. EF 변경 추적 속도가 느리기 때문에 3000 개의 항목 만있는 테이블은 30 초 이상 걸릴 수 있습니다.
약간

1
작은 테이블 (<1000 행)에만 적합
Amir Touitou

내 단위 내에서 메모리 데이터베이스에 대한 완벽 :) 테스트
SimonGates

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

지금까지 내가 확장 방법이없는 알고 DeleteIQueryable- 나는 마니가 EntityFramework.Extended 같은 것을 사용하고 있었다 같은데요 : github.com/loresoft/EntityFramework.Extended

답을 잘못 수정 했으므로 답변을 수정했습니다. @ null, 당신 말이 맞습니다. 이것은 .Delete사용자 정의 확장이며 먼저 답변을 게시하는 열기 에서이 사용자 정의의 정의를 언급하는 것을 완전히 잊었습니다 .Delete. :)
Manish Mishra 12

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

"리프"테이블의 내용을 완전히 업데이트하는 (FK가 가리 키지 않는) 특정 사례를 처리해야 할 때이 질문에 부딪 쳤습니다. 이것은 모든 행을 제거하고 새로운 행 정보를 넣는 것과 관련이 있으며 트랜잭션으로 수행해야합니다 (삽입이 어떤 이유로 든 실패하면 빈 테이블로 끝나고 싶지 않습니다).

public static void Clear<T>(this DbSet<T> dbSet)접근 방식 을 시도 했지만 새 행이 삽입되지 않았습니다. 또 다른 단점은 행이 하나씩 삭제되므로 전체 프로세스가 느리다는 것입니다.

그래서 TRUNCATE훨씬 빠르며 롤백 가능 하기 때문에 접근 방식으로 전환했습니다 . 또한 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 실패).


두 개의 설명 : 1. 테이블 이름은 [...]로 묶어야합니다. 내 클래스 / 테이블 중 하나를 "트랜잭션"이라고하며 SQL 키워드입니다. 2. 단위 테스트를 위해 DB의 모든 테이블을 지우는 것이 목표라면, 외래 키 제약 조건을 가진 문제는 부모 테이블보다 자식 테이블이 잘리는 방식으로 테이블을 처리하도록 명령함으로써 쉽게 해결할 수 있습니다.
Christoph

@Christoph-1. 네, 맞습니다. 키워드를 피하기 위해 항상 테이블 이름을 지정하여 문제를 일으킬 수 있기 때문에 나는 그것을 놓쳤다. 내 기억이 맞다 경우 2. FKS에 의해 참조 테이블 (SQL Server가 발생 잘라 내기 할 수 없다 Cannot truncate table because it is being referenced by a FOREIGN KEY constraintFKS가되어야합니다 그래서 그들은 비어있는 경우에도) 삭제하고 다시 사용하기 위해 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 

중요한 점 중 하나는 테이블에서 외래 키를 사용하는 경우 먼저 부모 테이블보다 먼저 자식 테이블을 삭제해야하므로 삭제 중 테이블의 시퀀스 (계층 구조)가 중요하므로 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;
}

왜 모든 레코드를 꺼내서 삭제 하시겠습니까? 매우 비효율적
암말

성능이 나의 우선 순위가 아니기 때문입니다. 모듈성을 기반으로하므로 위치 조건을 추가하거나 제거하기 전에 데이터를 확인하려는 경우 할 수 있습니다. EF6은 SQL I / O에 대해 가장 느린 도구이므로 성능이 최우선 인 경우 EF6을 사용하는 이유가 문제가됩니다.
Roberto Mutti

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

부모를 삭제하려고 할 때 모든 자식이 삭제되면 계단식으로 배열됩니다. 또는 아이들이 nullable 외래 키를 가지고 있습니다.


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.