Как выполнить DataAdapter.Fill () одновременно - PullRequest
0 голосов
/ 08 ноября 2019

Я давно работаю над приложением 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».

Это снижает общую производительность приложения и вызывает проблемы при извлечении данных. Я просмотрел Google и обнаружил, что «Threading» может быть полезным, но я не могу правильно выполнить Threading. Я не очень знаком с такими вещами. Будет полезно, если вы сможете исправить проблему. Мой первый вызов хранимой процедуры:

 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, то проблем нет. Таким образом, возможно сделать отдельное SqlConnection для каждого вызова ExecuteQuery_SP. Если есть сомнения, пожалуйста, прокомментируйте. Спасибо

Ответы [ 2 ]

3 голосов
/ 08 ноября 2019

SQL Server разрешит тысячи соединений одновременно, по умолчанию. Это НЕ источник вашей проблемы. Вы принудительно направляли каждый вызов хранимой процедуры одним методом. Факторизовать ваши обращения к хранимым процедурам - другими словами, потерять метод ExecuteQuery_SP, который является узким местом. Затем проверьте снова.

Вот введение в слои данных .

0 голосов
/ 08 ноября 2019

Вот самая простая версия, которую я могу создать для вас. Важно: чтобы понять, следует прочитать об 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();
    }
...