[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();

}

 

posted @ 2023-03-16 17:03  WikiChen  阅读(153)  评论(0编辑  收藏  举报