如何同时执行 DataAdapter.Fill()

Posted

技术标签:

【中文标题】如何同时执行 DataAdapter.Fill()【英文标题】:How to execute DataAdapter.Fill() simultaneously 【发布时间】:2020-03-05 07:15:50 【问题描述】:

我已经在一个 ASP.Net 应用程序上工作了很长时间,并且有超过 10 个客户端在使用该应用程序。但是现在我在应用程序中发现了一个问题,就是我有一个存储过程调用,大约需要30秒才能执行。这不是问题,因为 SQL 代码高度复杂且循环多次。问题是 : 每当执行该存储过程调用时,我都无法使用任何其他函数或存储过程调用。 当我尝试调试时,问题是“DataAdapter.Fill()”函数正在等待第一个存储过程调用完成。

我执行存储过程调用并返回数据的代码是:

public static DataSet ExecuteQuery_SP(string ProcedureName, object[,] ParamArray)
    
        SqlDataAdapter DataAdapter = new SqlDataAdapter();       
        DataSet DS = new DataSet();
        try
        
            if (CON.State != ConnectionState.Open)
                OpenConnection();
            SqlCommand cmd = new SqlCommand();
            cmd.CommandTimeout = 0;
            cmd.CommandText = ProcedureName;
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Connection = CON;
            cmd.Transaction = SqlTrans;
            string ParamName;
            object ParamValue;
            for (int i = 0; i < ParamArray.Length / 2; i++)
            
                ParamName = ParamArray[i, 0].ToString();
                ParamValue = ParamArray[i, 1];
                cmd.Parameters.AddWithValue(ParamName, ParamValue);
            
            DataAdapter = new SqlDataAdapter(cmd);
            DataAdapter.Fill(DS);
            cmd.CommandText = "";
        
           catch (Exception ea)
        
        
        return DS;
    

所有存储过程调用都通过此函数进行。因此,当我的第一个存储过程调用“A”正在运行时,存储过程调用“B”在“A”完成之前不会执行。

这会降低应用程序的整体性能并导致数据检索出现问题。 我浏览了谷歌,发现“线程”可能会有所帮助,但我无法正确执行线程。我对这类事情不是很熟悉。如果您可以纠正问题,这将很有帮助。 我的第一个存储过程调用是:

 ds = DB.ExecuteQuery_SP("SelectOutstandingReportDetailed", parArray);

其中 ds 是 DataSet 对象。 第二个存储过程调用是:

ds = DB.ExecuteQuery_SP("[SelectAccLedgersDetailsByID]", ParamArray);

我当前的数据库连接打开功能是:

 public static bool OpenConnection() 
        
            try
            

                    Server = (String)HttpContext.GetGlobalResourceObject("Resource", "Server");
                    DBName = (String)HttpContext.GetGlobalResourceObject("Resource", "DBName");
                    UserName = (String)HttpContext.GetGlobalResourceObject("Resource", "UserName");
                    PassWord = (String)HttpContext.GetGlobalResourceObject("Resource", "PassWord");

                    string ConnectionString;
                    ConnectionString = "server=" + Server + "; database=" + DBName + "; uid=" + UserName + "; pwd=" + PassWord + "; Pooling='true';Max Pool Size=100;MultipleActiveResultSets=true;Asynchronous Processing=true";

                    CON.ConnectionString = ConnectionString;
                    if (CON.State != ConnectionState.Open)
                    
                        CON.Close();
                        CON.Open();
                    

            
            catch (Exception ea)
            
            
            return false;
        

其中 'CON' 是一个公共 SqlConnection 变量

static SqlConnection CON = new SqlConnection();

我发现了问题,就是所有的存储过程调用都是通过这个'CON'对象来执行的。如果每个存储过程调用都有单独的 SqlConnection 对象,则没有问题。 那么是否可以为每个 ExecuteQuery_SP 调用创建单独的 SqlConnection 。 如果有任何疑问,请发表评论。 谢谢你

【问题讨论】:

"所有存储过程调用都通过这个函数工作。"那不要用同一种方法来执行不同的存储过程? ExecuteQuery_SP 函数是打开数据库连接的常用函数。我不能为每个存储过程调用提供单独的函数,因为将有大约一百个存储过程调用@IrishChieftain docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/… -- 你试过这个吗?抱歉,一开始语言错误。让我看看 C# 是否有类似的东西。 这就是所谓的数据层。您有不同的数据访问方法,每种方法都可能调用存储过程。试一试。 您是否使用单个SqlConnection 进行所有数据访问?你不应该那样做。为每个请求创建一个新的SqlConnection。 ADO.NET 在内部管理 connection pool。 【参考方案1】:

默认情况下,SQL Server 将同时允许thousands of connections。这不是您问题的根源。您已强制对存储过程的每次调用都通过一个方法进行汇集。排除您对存储过程的调用 - 换句话说,丢失 ExecuteQuery_SP 方法,这是一个瓶颈。然后再次测试。

这里是data layers的介绍。

【讨论】:

我没有正确理解你。您的意思是为每个存储过程调用编写单独的类似 ExecuteQuery_SP 的函数吗? 是的。您正在不必要地制造瓶颈。 对不起,在我的情况下这是不可能的。因为有大量的存储过程调用。 我尝试为“SelectOutstandingReportDetailed”添加单独的函数,但问题仍然存在。 您只是调用了“SelectOutstandingReportDetailed”而不是其他任何存储过程吗?有很多连接的查询有问题吗?也许缺少数据库索引?您需要缩小范围。【参考方案2】:

这是我可以为您创建的最简单的版本。 重要提示:要了解您应该了解 async-await。

您可以从Microsoft C# Async-Await Docs开始

// TODO set up your connection string
    private string connectionString = "<your connection string>";

    // Gets data assyncronously
    public static async Task<DataTable> GetDataAsync(string procedureName, object[,] ParamArray)
    
        try
        
            var asyncConnectionString = new SqlConnectionStringBuilder(connectionString)
            
                AsynchronousProcessing = true
            .ToString();

            using (var conn = new SqlConnection(asyncConnectionString))
            
                using (var SqlCommand = new SqlCommand())
                
                    SqlCommand.Connection = conn;
                    SqlCommand.CommandText = procedureName;
                    SqlCommand.CommandType = CommandType.StoredProcedure;

                    string ParamName;
                    object ParamValue;
                    for (int i = 0; i < ParamArray.Length / 2; i++)
                    
                        ParamName = ParamArray[i, 0].ToString();
                        ParamValue = ParamArray[i, 1];
                        SqlCommand.Parameters.AddWithValue(ParamName, ParamValue);
                    

                    conn.Open();
                    var data = new DataTable();
                    data.BeginLoadData();
                    using (var reader = await SqlCommand.ExecuteReaderAsync().ConfigureAwait(true))
                    
                        if (reader.HasRows)
                            data.Load(reader);
                    
                    data.EndLoadData();
                    return data;
                
            
        
        catch (Exception Ex)
        
            // Log error or something else
            throw;
        
    

    public static async Task<DataTable> GetData(object General, object Type, string FromDate, string ToDate)
    
        object[,] parArray = new object[,]
        "@BranchID",General.BranchID,
        "@FinancialYearID",General.FinancialYearID,
        "@Type",Type,
        "@FromDate",DateTime.ParseExact(FromDate, "dd/MM/yyyy", System.Globalization.CultureInfo.InvariantCulture),
        "@ToDate",DateTime.ParseExact(ToDate, "dd/MM/yyyy", System.Globalization.CultureInfo.InvariantCulture)
        ;

        return await DataBaseHelper.GetDataAsync("SelectOutstandingReportDetailed", parArray);
    

    // Calls database assyncronously
    private async Task ConsumeData()
    
        DataTable dt = null;

        try
        
            // TODO configure your parameters here
            object general = null;
            object type = null;
            string fromDate = "";
            string toDate = "";

            dt = await GetData(general, type, fromDate, toDate);
        
        catch (Exception Ex)
        
            // do something if an error occurs
            System.Diagnostics.Debug.WriteLine("Error occurred: " + Ex.ToString());
            return;
        

        foreach (DataRow dr in dt.Rows)
        
            System.Diagnostics.Debug.WriteLine(dr.ToString());
        
    

    // Fired when some button is clicked. Get and use the data assyncronously, i.e. without blocking the UI.
    private async void button1_Click(object sender, EventArgs e)
    
        await ConsumeData();
    

【讨论】:

我会尝试这个,但对我来说它看起来非常复杂,因为我对此一点也不熟悉。我会尽力做到这一点。如果可以的话请帮我写代码 “SelectOutstandingReportDetailed”的参数是什么?如果你给我,我可以告诉你我的方法。 而且,我很好奇。如何创建 ParamArray 变量?它像 [ ["@varname",value], ["@varname2",value] ] 吗?我不明白。 object[,] parArray = new object[,] "@BranchID",General.BranchID, "@FinancialYearID",General.FinancialYearID, "@Type",Type , "@FromDate",DateTime.ParseExact(FromDate, "dd/MM/yyyy", System.Globalization.CultureInfo.InvariantCulture), "@ToDate",DateTime.ParseExact(ToDate, "dd/MM/yyyy" , System.Globalization.CultureInfo.InvariantCulture) ;这是 SelectOutstandingReportDetailed 的 paramarray 对象 DataTable.Load 方法是同步的。因此,代码中的主要操作 - 加载数据 - 同步发生。

以上是关于如何同时执行 DataAdapter.Fill()的主要内容,如果未能解决你的问题,请参考以下文章

DataAdapter.Fill(数据集)

向 dataAdapter.fill() 添加参数

DataAdapter.Fill()分页

使用 C# dataAdapter.Fill() 和 dataAdapter.Update() 将表的数据从一个数据库传输到另一个数据库的同一个表

在不使用 DataAdapter.Fill 的情况下更新数据表 2 次

DataAdapter.Fill 给出错误“在 sys.servers 中找不到服务器‘系统’。” [关闭]