[EF Core] 多线程执行SQL Command
public class XXXRepository { //数据库上下文 private XXXDBContext context; //作用域服务工厂 private readonly IServiceScopeFactory _serviceScopeFactory; public XXXRepository(XXXDBContext context, IServiceScopeFactory serviceScopeFactory) { this.context = context; _serviceScopeFactory = serviceScopeFactory; } }
public void XXXMethod() { // 开启数据库连接 context.Database.OpenConnection(); // 多线程任务列表 List<Task> tasks = new List<Task>(); // 线程安全先进先出集合 ConcurrentQueue<XXX> queue = new ConcurrentQueue<XXX>(); // SQL参数列表 -- 任务公有参数 List<SqlParameter> sqlParameters = new List<SqlParameter>(){ (new SqlParameter("@参数1", "参数值"), (new SqlParameter("@参数2", "参数值"), (new SqlParameter("@参数3", "参数值") }; // 循环创建任务 XXXStringList.ForEach(paramater => { // 任务私有参数 // 因为任务将在ForEach执行完后才开始运行,这意味着所有任务将会使用同一个参数值 // 所以需要使用一个变量来存储参数值,保证每个任务都使用独立的变量 string param = paramater; // 添加任务到任务列表 tasks.Add( // 创建并运行任务 Task.Run(async () => { try { // 创建作用域服务 using (var scope = _serviceScopeFactory.CreateScope()) { using (var _dbcontext = scope.ServiceProvider.GetService<XXXContext>()) { using (var command = context.Database.GetDbConnection().CreateCommand()) { command.Parameters.Clear(); // 添加任务私有参数 command.Parameters.Add(new SqlParameter("@PARAM", param)); // 添加任务公有参数 // 如果前面使用了SQL Command并且也使用了command.Parameters.Add()或command.Parameters.AddRange() // 再次添加参数时可能会出现报错 -- The SqlParameter is already contained by another SqlParameterCollection // 使用command.Parameters.Clear()似乎无法解决此问题 // 因此需要深度拷贝 command.Parameters.AddRange(sqlParameters.Select(s => ((ICloneable)s).Clone()).ToArray()); command.CommandText = "SELECT * FROM TABLE WHERE COLUMN = @PARAM AND ... "; // 有些查询语句在SQL Management Studio运行很快,但在代码中会出现超时 // 网上说是因为程序也占用了执行时间 // 这种情况可以添加CommandTimeout command.CommandTimeout = 30000; using (var res = await command.ExecuteReaderAsync()) { while (res.Read()) { // 多任务执行时,插入数据可能会出现数据不一致性问题,就是每次的结果行数不一致 // 使用线程安全先进先出集合,保证数据一致性 queue.Enqueue( new XXX { Column1 = res[0].ToString(), Column2 = res[1].ToString(), Column3 = res[2].ToString(), ... }; ); } } } } } } catch (Exception ex) { throw ex; } }) ); }); // 阻塞主线程,等待所有任务完成 Task.WaitAll(tasks.ToArray()); // 关闭数据库连接 context.Database.CloseConnection(); }