IT이야기

Entity Framework에서 모든 엔터티 삭제

cyworld 2021. 10. 28. 19:19
반응형

Entity Framework에서 모든 엔터티 삭제


Entity Framework 4+를 사용하여 모든 테이블(모든 엔터티)의 내용을 삭제하고 싶습니다. 어떻게 할 수 있습니까?


이것은 기본 데이터베이스가 MSSQL이라고 가정할 때 개별 엔터티 개체를 삭제하는 것보다 훨씬 더 나은 성능을 제공합니다.

foreach (var tableName in listOfTableNames)
{
    context.ExecuteStoreCommand("TRUNCATE TABLE [" + tableName + "]");
}

물론 테이블에 외래 키 관계가 있는 경우 테이블 이름 목록을 올바른 순서로 설정하여 해당 테이블이 의존할 수 있는 기본 키 테이블을 지우기 전에 외래 키 테이블을 지워야 합니다.


게으른 사람들을 위해 답을 찾을 때 스스로 생각해낸 코드:

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)
            {
                context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", tableName));
            }

            context.SaveChanges();
        }
    }

간단한 설명: 권한이 없기 때문에 테이블을 자르지 않습니다. 문제가 되지 않는다면 자유롭게 하십시오. __MigrationHistory 테이블은 where 문에서 무시됩니다.

업데이트: 몇 가지 연구 끝에 더 나은 솔루션을 찾았습니다(좋지는 않지만 필요한 열만 삭제).

public static void ClearDatabase(DbContext context)
    {
        var objectContext = ((IObjectContextAdapter)context).ObjectContext;
        var entities = objectContext.MetadataWorkspace.GetEntityContainer(objectContext.DefaultContainerName, DataSpace.CSpace).BaseEntitySets;
        var method = objectContext.GetType().GetMethods().First(x => x.Name == "CreateObjectSet");
        var objectSets = entities.Select(x => method.MakeGenericMethod(Type.GetType(x.ElementType.FullName))).Select(x => x.Invoke(objectContext, null));
        var tableNames = objectSets.Select(objectSet => (objectSet.GetType().GetProperty("EntitySet").GetValue(objectSet, null) as EntitySet).Name).ToList();

        foreach (var tableName in tableNames)
        {
            context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", tableName));
        }

        context.SaveChanges();
    }

EF 6의 경우:

DbSet<Entity>.RemoveRange(DbSet<Entity>);

다음과 같은 코드를 사용하여 테이블을 반복합니다.

context.GetType().GetProperties()
.Where(propertyInfo => propertyInfo.PropertyType == typeof(Table<>))
.Select(propertyInfo => propertyInfo.GetValue(context, null) as ITable).ToList()
.Foreach(table =>
{
    //code that deletes the actual tables records.
}
);

@Wojciech Markowski의 훌륭한 답변을 개선하려고 노력하고 싶습니다.

나처럼 게으르고 외래 키 제약 조건을 확인하지 않으려면 다음 방법을 사용할 수 있습니다.

        private void ClearDatabase(TContext context)
    {
            // disable all foreign keys
            //context.Database.ExecuteSqlCommand("EXEC sp_MSforeachtable @command1 = 'ALTER TABLE ? NOCHECK CONSTRAINT all'");

            List<string> tableNames = context.Database.SqlQuery<string>("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME NOT LIKE '%Migration%'").ToList();

            for (int i = 0; tableNames.Count>0; i++)
            {
                try
                {
                    context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", tableNames.ElementAt(i % tableNames.Count)));
                    tableNames.RemoveAt(i % tableNames.Count);
                    i = 0;
                }
                catch { } // ignore errors as these are expected due to linked foreign key data             
            }


            context.SaveChanges();
    }

ClearDatabase 메서드는 테이블 목록을 살펴보고 정리합니다. FK 제약 조건이 발견되면 예외를 포착하고 다음 테이블로 이동합니다. 결국 모든 테이블이 삭제됩니다.

또한 모든 FK 제약 조건을 느슨하게 하고 싶지 않다면 다음 줄에서 모든 제약 조건을 비활성화할 수 있습니다.

context.Database.ExecuteSqlCommand("EXEC sp_MSforeachtable @command1 = 'ALTER TABLE ? NOCHECK CONSTRAINT all'");

한 가지 더: 모든 테이블을 지우고 지우지 않으려면 다음 줄을 바꾸십시오.

context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", tableNames.ElementAt(i % tableNames.Count)));

와 함께:

context.Database.ExecuteSqlCommand(string.Format("DROP TABLE {0}", tableNames.ElementAt(i % tableNames.Count)));

개인적으로 코드 우선 마이그레이션을 사용하여 Entity Framework 6에서 이 답변을 확인했습니다.

편집: 더 나은 버전:

        private void ClearDatabase(MrSaleDbContext context)
    {
        //Optional: disable all foreign keys (db-schema will be loosed).
        //context.Database.ExecuteSqlCommand("EXEC sp_MSforeachtable @command1 = 'ALTER TABLE ? NOCHECK CONSTRAINT all'");

        List<string> tableNames = context.Database.SqlQuery<string>("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME NOT LIKE '%Migration%' AND TABLE_NAME NOT LIKE 'AspNet%'").ToList();

        for (int i = 0; tableNames.Count > 0; i++)
        {
            try
            {
                //To delete all tables and not just clean them from data, replace "DELETE FROM {0}" in "DROP TABLE {0}":
                context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", tableNames.ElementAt(i % tableNames.Count)));
                tableNames.RemoveAt(i % tableNames.Count);
                i = -1; //flag: a table was removed. in the next iteration i++ will be the 0 index.
            }
            catch (System.Data.SqlClient.SqlException e)   // ignore errors as these are expected due to linked foreign key data    
            {                    
                if ((i % tableNames.Count) == (tableNames.Count - 1))
                {
                    //end of tables-list without any success to delete any table, then exit with exception:
                    throw new System.Data.DataException("Unable to clear all relevant tables in database (foriegn key constraint ?). See inner-exception for more details.", e);
                }

            }

        }

catch 블록의 if 문은 테이블을 삭제하지 않고 테이블 목록의 마지막 인덱스에 도달했는지 여부를 확인합니다. 이 경우 무한 루프에 들어가는 대신 예외를 throw하고 for를 종료합니다.


truncate는 외래 키 내에서 삭제할 수 없습니다.

그런 다음 DbContext에 대한 확장 메서드를 만들었습니다.

사용법은 간단합니다.

db.truncates(); // 모든 테이블 삭제.

db.Truncates("테스트1", "테스트2"); // "Test1, Test2" 테이블만 삭제

public static class DbContextExtension
{
    public static int Truncates(this DbContext db, params string[] tables)
    {
        List<string> target = new List<string>();
        int result = 0;

        if (tables == null || tables.Length == 0)
        {
            target = db.GetTableList();
        }
        else
        {
            target.AddRange(tables);
        }

        using (TransactionScope scope = new TransactionScope())
        {
            foreach (var table in target)
            {
                result += db.Database.ExecuteSqlCommand(string.Format("DELETE FROM  [{0}]", table));
                db.Database.ExecuteSqlCommand(string.Format("DBCC CHECKIDENT ([{0}], RESEED, 0)", table));
            }

            scope.Complete();
        }

        return result;
    }

    public static List<string> GetTableList(this DbContext db)
    {
        var type = db.GetType();

        return db.GetType().GetProperties()
            .Where(x => x.PropertyType.Name == "DbSet`1")
            .Select(x => x.Name).ToList();
    }
}

ReferenceURL : https://stackoverflow.com/questions/6089403/delete-all-entities-in-entity-framework

반응형