我如何才能快速删除使用实体框架表中的所有行?
我目前使用:
var rows = from o in dataDb.Table
select o;
foreach (var row in rows)
{
dataDb.Table.Remove(row);
}
dataDb.SaveChanges();
但是,执行起来需要很长时间。
还有其他选择吗?
我如何才能快速删除使用实体框架表中的所有行?
我目前使用:
var rows = from o in dataDb.Table
select o;
foreach (var row in rows)
{
dataDb.Table.Remove(row);
}
dataDb.SaveChanges();
但是,执行起来需要很长时间。
还有其他选择吗?
当前回答
我的解决方案,混合了我的想法,一些答案(Ron来自这个帖子,但也有这个和这个反思),并试图涵盖一些不同的条件。
它基于EF6,但它应该工作得很好,只是修复了一些扩展,如GetTableName<TEntity>。
我的解决方案:
使用扩展,所以你只需要DbSet启动 有一个行数阈值,以决定RemoveRange或SQL执行,以避免性能问题 SQL版本基于DELETE而不是TRUNCATE,以避免外键问题(当然,它必须符合您的要求) 有一个参数以内联保存更改
private const int RangeLimit = 100;
private static void ClearTable<TEntity>(this DbSet<TEntity> dataSet, bool saveChanges = true) where TEntity : class
{
DbContext context = null;
if (dataSet.Count() > RangeLimit)
{
context = dataSet.GetContext();
context.Database.ExecuteSqlCommand($"DELETE FROM [{context.GetTableName<TEntity>()}]");
}
else
{
dataSet.RemoveRange(dataSet);
}
if (!saveChanges)
{
return;
}
if (context == null)
{
context = dataSet.GetContext();
}
context.SaveChanges();
}
private static DbContext GetContext<TEntity>(this DbSet<TEntity> dbSet)
where TEntity : class
{
var internalSet = dbSet
.GetType()
.GetField("_internalSet", BindingFlags.NonPublic | BindingFlags.Instance)
?.GetValue(dbSet);
var internalContext = internalSet?.GetType().BaseType
?.GetField("_internalContext", BindingFlags.NonPublic | BindingFlags.Instance)
?.GetValue(internalSet);
return (DbContext)internalContext?.GetType()
.GetProperty("Owner", BindingFlags.Instance | BindingFlags.Public)
?.GetValue(internalContext, null);
}
public static string GetTableName<TEntity>(this DbContext context) where TEntity : class
{
return (context as IObjectContextAdapter).ObjectContext.CreateObjectSet<TEntity>().EntitySet.Name;
}
对于一个名为Entries的数据库表,你所要做的就是:
databaseContext.Entries.ClearTable();
如果你想保存更改,或者不想保存更改:
databaseContext.Entries.ClearTable(false);
它基于反射,简化代码。当然,它有一些性能上的权衡,但是对于每个表反射一次,因此在这些条件下应该是完全可以接受的。
其他回答
var all = from c in dataDb.Table select c;
dataDb.Table.RemoveRange(all);
dataDb.SaveChanges();
对于那些像我一样在谷歌上搜索并最终来到这里的人来说,这是你目前在EF5和EF6中所做的:
context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
假设context是System.Data.Entity.DbContext
编辑:
目前在net6.0 (dotnet 6核心)你可以做以下事情:
context.Database.ExecuteSqlRaw("TRUNCATE TABLE [TableName]");
或者使用Async重载:
await context.Database.ExecuteSqlRawAsync("TRUNCATE TABLE [TableName]");
这些是来自Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions的扩展方法
如果你有外键的问题(在MySql中),你可能不得不做以下事情(执行SET FOREIGN_KEY_CHECKS = 0;单独打电话的部分似乎对我不起作用)
context.Database.ExecuteSqlRaw("SET FOREIGN_KEY_CHECKS = 0; TRUNCATE TABLE [TableName];");
所以如果你想要截断你的整个数据库(可能是单元测试的原因),你可以这样做:
var tableNames = context.Model.GetEntityTypes()
.Select(t => t.GetTableName())
.Distinct()
.ToList();
foreach (var tableName in tableNames)
{
context.Database.ExecuteSqlRaw($"SET FOREIGN_KEY_CHECKS = 0; TRUNCATE TABLE `{tableName}`;");
}
这里有几个问题,几乎所有的答案:
1]硬编码的sql。括号是否适用于所有数据库引擎? 2]实体框架Remove和RemoveRange调用。这会将所有实体加载到受操作影响的内存中。呵。 3]截表。与外键引用中断,可能无法跨所有数据库引擎工作。
使用https://entityframework-plus.net/,他们处理跨数据库平台的事情,将删除转换为正确的sql语句,并且不将实体加载到内存中,并且该库是免费的开源的。
免责声明:我不隶属于nuget包。他们确实提供了付费版本,功能甚至更多。
警告:以下仅适用于小表(考虑< 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;}
}
为了让代码更整洁,你可以声明下面的扩展方法:
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();
我最近使用这种方法为每次测试用例运行清理我的测试数据库(它显然比每次从头创建数据库快,尽管我没有检查生成的删除命令的形式)。
为什么会变慢?
EF将得到所有的行(VotingContext.Votes) 然后会使用他们的id(不知道具体怎么做,没关系)删除他们。
因此,如果你正在处理大量的数据,你将杀死SQL server进程(它将消耗所有的内存),同样的事情对于IIS进程,因为EF将缓存所有数据与SQL server相同的方式。如果您的表包含大量数据,请不要使用此方法。
如果您希望清除整个数据库。
由于外键的限制,截断表的顺序很重要。这是一种强行执行这个序列的方法。
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。