【问题标题】:How to execute DataAdapter.Fill() simultaneously如何同时执行 DataAdapter.Fill()
【发布时间】: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

标签: c# asp.net multithreading stored-procedures dataadapter


【解决方案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 方法是同步的。因此,代码中的主要操作 - 加载数据 - 同步发生。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-28
  • 1970-01-01
  • 2011-10-29
  • 2012-02-02
  • 1970-01-01
  • 2021-12-11
  • 2018-01-30
相关资源
最近更新 更多