I am shifting a C# ASP.NET Core 7 project from using SqlClient
with regular SQL queries to using Entity Framework instead. I have a particular place when the application runs multiple long-running tasks, it's kind of a simulation with a big for loop where the user can follow progress, and for that reason, each task writes into the database dozens of times in its own task. The old SqlClient
solution worked smoothly with minimal CPU and memory usage, but with EF, once the threads are beginning to work, everything halts and freezes.
I know that DbContext
is not thread-safe, therefore each task creates its own DbContext
, and they create it, particularly where the database inserts occur, and I dispose them right away once they are not needed, and yet, in the for loop it completely freezes the computer and everything stops. The web application is not even responding anymore.
The simplified controller:
public SmContext db { get; set; }
public SimulateRoundModel(SmContext db)
{
this.db = db;
}
public async Task<IActionResult> OnPost()
{
List<Match> matches = new CollectorClass(db).Collect();
MyClass.Wrapper(matches);
return Page();
}
The simplified code:
public static void Wrapper(List<Match> matches)
{
Parallel.For(0, matches.Count,
index =>
{
matches[index].LongSim();
});
}
Match class:
private SmContext db { get; set; }
public Match(db)
{
this.db = db;
}
public void longSim()
{
db.Dispose(); // disposing the main dbcontext that the constructor receives, we don't want to use that
using (SmContext db = new SmContext())
{
// some initial query and insert
}
for (int i = 0; i < 100; i++)
{
Thread.Sleep(5000);
// some simulation
db = new SmContext();
SomeInsert(); // these are using the db for the insert
SomeInsert();
SomeInsert();
db.Dispose();
}
}
We are talking about 5-50 matches and Parallel.For
optimized them very well with the old SqlClient
solutions, I have seen running it with 200 matches without an issue before. These are not intensive tasks, only simple stuff, and some queries, but they are running long. Ideally, I would like to continue saving the progress to the database without a major rewrite.
The ultimate question is, is there a conceptual issue here, that I am too newbie to recognize, or this solution should work fine and there is something fuzzy going on in the black spots of the code?