Entity Framework 4+を使用して、すべてのテーブル(すべてのエンティティ)のコンテンツを削除したい。これをどのように行うことができますか?
これは、基礎となるデータベースがMSSQLであると仮定して、個々のエンティティオブジェクトを削除することよりもはるかに優れたmuchを実行します。
foreach (var tableName in listOfTableNames)
{
context.ExecuteStoreCommand("TRUNCATE TABLE [" + tableName + "]");
}
もちろん、テーブルに外部キーのリレーションシップがある場合、依存する主キーテーブルをクリアする前に外部キーテーブルをクリアするように、適切な順序でテーブル名のリストを設定する必要があります。
怠laな人のために、答えを探しているときに自分でコードを作成しました:
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制約を緩めることを気にしない場合は、次のようにすべてのFK制約を無効にすることができます。
context.Database.ExecuteSqlCommand("EXEC sp_MSforeachtable @command1 = 'ALTER TABLE ? NOCHECK CONSTRAINT all'");
さらにもう1つ:すべてのテーブルを削除するだけでなく、それらを削除する場合は、次の行を置き換えます。
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ステートメントは、テーブルを削除せずに、tables-listの最後のインデックスに到達したかどうかをチェックします。その場合、無限ループに入る代わりに、例外をスローしてforを終了します。
truncateは外部キー内で削除できませんでした。
次に、DbContextの拡張メソッドを作成しました。
使い方は簡単です。
db.Truncates(); //すべてのテーブルの削除。
db.Truncates( "Test1"、 "Test2"); //「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();
}
}