切片 Entity Framework 的事务以减少内存使用
Slicing Entity Framework's transaction in order to reduce memory usage
所以我的问题是我必须 运行 32 位 ASP .NET 应用程序 运行ning。但是我必须处理一堆记录,这会在数据库中产生巨大的变化。万一出现问题,我必须将整个过程放入
一笔交易。
我想如果我在每个 f.e 之后使用 DBContext.SaveChanges()
。 10000 条已处理的记录并删除引用等,因此 GC
可以完成它的工作可以解决我的问题,但事实并非如此。
问题是每个使用数据库的进程(我的意思是 CRUD 操作)都在显着变慢(甚至慢 10 倍左右)。
所以我的代码是这样的,为了更好的理解:
public void StartProcess()
{
localScopeCtx = cont.Resolve<IApplicationDbContext>();
IDbContextTransaction trans = localScopeCtx.BeginTransaction();
int take = 10000;
int index = 1;
while (index <= needToProcess)
{
var records = GetRecords(take)
List<obj> recordsToDelete;
ProcessRecords(take, out recordsToDelete);
localScopeCtx.Records.RemoveRange(recordsToDelete);
localScopeCtx.SaveChanges();
DetachAllEntities(localScopeCtx);
}
trans.Commit();
}
public void DetachAllEntities(IApplicationDbContext dbContext)
{
var changedEntriesCopy = dbContext.ChangeTracker.Entries()
.Where(e => e.State == EntityState.Added ||
e.State == EntityState.Modified ||
e.State == EntityState.Deleted ||
e.State == EntityState.Unchanged)
.ToList();
foreach (var entry in changedEntriesCopy)
entry.State = EntityState.Detached;
}
在第一个循环中一切都很好。它速度很快,而且它的工作就像魅力一样。但是在 localScopeCtx.SaveChanges()
之后一切都变慢了,就像我说的那样。我认为这是因为 EF 并没有真正将修改发送到数据库,而是将其存储在内存中(根据内存使用情况),但以一种更聪明的方式,它永远不会 运行 离开它(也许虚拟内存使用率左右)。
有什么解决方案的建议吗?
编辑:
在进程中添加DetachAllEntities
方法;
好的,所以我找到了解决方案,如果有人在这里寻找类似的东西,那就是:
我已经认识到,如果不创建新的 DbContext,它永远不会 "let go" 之前加载的任何对象。因此解决方案是更改事务创建和 DbContext 创建的顺序并创建一个新的每个 "slice".
后的上下文
我的代码看起来像这样:
public void StartProcess()
{
/*This part isn't exacly the same in my program but the point is the same: create a SqlConnection*/
string providerName = "System.Data.SqlClient";
string serverName = ".";
string databaseName = "SchoolDB";
// Initialize the connection string builder for the SQL Server provider.
SqlConnectionStringBuilder sqlBuilder =
new SqlConnectionStringBuilder();
// Set the properties for the data source.
sqlBuilder.DataSource = serverName;
sqlBuilder.InitialCatalog = databaseName;
sqlBuilder.IntegratedSecurity = true;
using (SqlConnection con = new SqlConnection(sqlBuilder.ToString()))
{
/*From this point my tested code has exactly the same construction*/
con.Open();
using (SqlTransaction transaction = con.BeginTransaction())
{
int take = 10000;
int index = 1;
while (index <= needToProcess)
{
localScopeCtx = new ApplicationDbContext(trans, false);
var records = GetRecords(take)
List<obj> recordsToDelete;
ProcessRecords(take, out recordsToDelete);
localScopeCtx.Records.RemoveRange(recordsToDelete);
localScopeCtx.SaveChanges();
localScopeCtx.Dispose()
}
trans.Commit();
}
}
}
所以我的问题是我必须 运行 32 位 ASP .NET 应用程序 运行ning。但是我必须处理一堆记录,这会在数据库中产生巨大的变化。万一出现问题,我必须将整个过程放入
一笔交易。
我想如果我在每个 f.e 之后使用 DBContext.SaveChanges()
。 10000 条已处理的记录并删除引用等,因此 GC
可以完成它的工作可以解决我的问题,但事实并非如此。
问题是每个使用数据库的进程(我的意思是 CRUD 操作)都在显着变慢(甚至慢 10 倍左右)。
所以我的代码是这样的,为了更好的理解:
public void StartProcess()
{
localScopeCtx = cont.Resolve<IApplicationDbContext>();
IDbContextTransaction trans = localScopeCtx.BeginTransaction();
int take = 10000;
int index = 1;
while (index <= needToProcess)
{
var records = GetRecords(take)
List<obj> recordsToDelete;
ProcessRecords(take, out recordsToDelete);
localScopeCtx.Records.RemoveRange(recordsToDelete);
localScopeCtx.SaveChanges();
DetachAllEntities(localScopeCtx);
}
trans.Commit();
}
public void DetachAllEntities(IApplicationDbContext dbContext)
{
var changedEntriesCopy = dbContext.ChangeTracker.Entries()
.Where(e => e.State == EntityState.Added ||
e.State == EntityState.Modified ||
e.State == EntityState.Deleted ||
e.State == EntityState.Unchanged)
.ToList();
foreach (var entry in changedEntriesCopy)
entry.State = EntityState.Detached;
}
在第一个循环中一切都很好。它速度很快,而且它的工作就像魅力一样。但是在 localScopeCtx.SaveChanges()
之后一切都变慢了,就像我说的那样。我认为这是因为 EF 并没有真正将修改发送到数据库,而是将其存储在内存中(根据内存使用情况),但以一种更聪明的方式,它永远不会 运行 离开它(也许虚拟内存使用率左右)。
有什么解决方案的建议吗?
编辑:
在进程中添加DetachAllEntities
方法;
好的,所以我找到了解决方案,如果有人在这里寻找类似的东西,那就是:
我已经认识到,如果不创建新的 DbContext,它永远不会 "let go" 之前加载的任何对象。因此解决方案是更改事务创建和 DbContext 创建的顺序并创建一个新的每个 "slice".
后的上下文我的代码看起来像这样:
public void StartProcess()
{
/*This part isn't exacly the same in my program but the point is the same: create a SqlConnection*/
string providerName = "System.Data.SqlClient";
string serverName = ".";
string databaseName = "SchoolDB";
// Initialize the connection string builder for the SQL Server provider.
SqlConnectionStringBuilder sqlBuilder =
new SqlConnectionStringBuilder();
// Set the properties for the data source.
sqlBuilder.DataSource = serverName;
sqlBuilder.InitialCatalog = databaseName;
sqlBuilder.IntegratedSecurity = true;
using (SqlConnection con = new SqlConnection(sqlBuilder.ToString()))
{
/*From this point my tested code has exactly the same construction*/
con.Open();
using (SqlTransaction transaction = con.BeginTransaction())
{
int take = 10000;
int index = 1;
while (index <= needToProcess)
{
localScopeCtx = new ApplicationDbContext(trans, false);
var records = GetRecords(take)
List<obj> recordsToDelete;
ProcessRecords(take, out recordsToDelete);
localScopeCtx.Records.RemoveRange(recordsToDelete);
localScopeCtx.SaveChanges();
localScopeCtx.Dispose()
}
trans.Commit();
}
}
}