Показывать ход выполнения через индикатор выполнения

У меня есть глупая проблема, но я застрял. Я выполняю хранимую процедуру из моего кода процедура занимает время, поэтому для этого я показываю индикатор выполнения, который показывает ход выполнения, но хранимая процедура выполняется, и нет ничего, к чему я увеличиваю значение индикатора выполнения.

Это мой код

void btnYes_Click(object sender, EventArgs e)
{
   if (DialogResult.Yes == MessageBox.Show("Are you sure", "", MessageBoxButtons.YesNo))
    {
        try
        {
         dbDataEntities db = new dbDataEntities();
        string myquery = "DECLARE @return_value int EXEC    @return_value = [dbo].[ssspUpdateMarksOfStudent] SELECT 'Return Value' = @return_value";
         //progressbar1.Maximum = 5000; 
         //progressbar1.value = ?; // how could i increment it
         //
         db.Database.ExecuteSqlCommand("myquery");



        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
}

Также я пытаюсь сделать это с помощью секундомера, установив текущее значение, которое не увеличилось при выполнении процедуры

Stopwatch st = new Stopwatch();
st.Start();
progressbar1.Maximum = 5000; 
progressbar1.Value = Convert.ToInt16(st.Elapsed.Seconds);
//My stored procedure call 
st.Stop(); 

так это можно сделать только с помощью background worker или есть ли другой способ сделать это?

Я новичок в программировании, поэтому не использовал background worker, поэтому я пытаюсь найти альтернативу.

Прошу предложить. Спасибо заранее.

Ответ 1

Одна смешная идея SqlConnection предоставляет событие InfoMessage, которое запускается, когда сервер выполняет команду PRINT. Вы можете печатать в некоторых частях хранимой процедуры и слушать это событие.

-- do something
PRINT '10 PERCENT COMPLETED';
-- do another thing
PRINT '20 PERCENT COMPLETED';
...
PRINT '100 PERCENT COMPLETED';

кроме этого, используйте решение @hamlet-hakobyan. просто покажите бесконечный индикатор выполнения.


Обновление: обновлено, чтобы включить полное решение

Прежде всего, я ненавижу давать полные ответы. Это предотвращает ум умение находить решение. Вместо этого я люблю подталкивать людей к правильному пути, чтобы они могли ходить пешком. Но здесь все равно. Протестировано с использованием VS2012, NET4, MSIL под W7x64SP1 и SQL2012.

Мой очень трудоемкий SP. Используется RaisError вместо PRINT для немедленной отправки сообщений.

Create Procedure usp_LongProcess As Begin

    Declare @i Int;
    Declare @msg VarChar(50);
    Set @i = 0;
    while (@i < 100) Begin
        WaitFor Delay '00:00:02';

        Set @i = @i + 10;
        Set @msg = Convert(VarChar(10), @i) + ' PERCENT COMPLETE';
        RaisError(@msg, 1, 1) With NoWait
    End
End

И моя форма с

  • кнопка (CallSpButton)
  • индикатор выполнения (progress)
  • метка (statusLabel) и
  • фоновый рабочий (SpCaller) с WorkerReportsProgress установите true.

Screenshot

И наконец код, который делает вызов

private void CallSpButton_Click(object sender, EventArgs e)
{
    CallSpButton.Enabled = false;
    SpCaller.RunWorkerAsync();
}

private void SpCaller_DoWork(object sender, DoWorkEventArgs e)
{
    var self = (BackgroundWorker) sender;

    var cb = new SqlConnectionStringBuilder
    {
        DataSource = ".", 
        InitialCatalog = "Sandbox", 
        IntegratedSecurity = true
    };

    using (var cn = new SqlConnection(cb.ToString()))
    {
        cn.FireInfoMessageEventOnUserErrors = true;
        cn.Open();
        cn.InfoMessage += (o, args) => self.ReportProgress(0, args.Message);

        using (var cmd = cn.CreateCommand())
        {
            cmd.CommandText = "usp_LongProcess";
            cmd.CommandType = CommandType.StoredProcedure;

            cmd.ExecuteNonQuery();
        }
    }
}

private void SpCaller_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    CallSpButton.Enabled = true;
}

private void SpCaller_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    var message = Convert.ToString(e.UserState);
    Debug.WriteLine(message);
    statusLabel.Text = message;

    if (message.EndsWith(" PERCENT COMPLETE"))
    {
        int percent;
        if (int.TryParse(message.Split(' ')[0], out percent))
            progress.Value = percent;
    }
}

Ответ 2

Похоже, вы используете хранимую процедуру в потоке gui.

Это означает, что ваше приложение будет не отвечать на запросы, пока вызов будет запущен, поскольку насос сообщения заблокирован. Это, наверное, плохая идея. Я бы рекомендовал переместить все длинные вызовы в другой поток.

Фоновый работник - это класс, предназначенный для этого, чтобы вы могли сообщать о том, как работает длинная работа, поэтому вы можете обновлять интерфейс, но поскольку ваша задача - только один элемент, не так много отчетов, поэтому он может быть не лучшим выбором для вас.

Возможно, вам нужно что-то вроде формы (угадывание синтаксиса, когда у меня нет компилятора) и заимствования из Windows Forms ProgressBar: Самый простой способ запустить/остановить выделение?

progressbar1.Style = ProgressBarStyle.Marquee; //thanks Hamlet
progressbar1.MarqueeAnimationSpeed = 30;

//run the long running thing on a background thread.
var bgTask = Task.Factory.StartNew(() => db.Database.ExecuteSqlCommand("myquery"));

//when it done, back on this thread, let me know and we'll turn the progress bar off
bgTask.ContinueWith(resultTask => {
    progressBar1.Style = ProgressBarStyle.Continuous;
    progressBar1.MarqueeAnimationSpeed = 0;
 }, TaskScheduler.FromCurrentSynchronizationContext());

Ответ 4

Пожалуйста, следуйте этому методу.

  • Сначала вычислите время выполнения хранимой процедуры.  db.Database.ExecuteSqlCommand("myquery"); введите здесь код

  • Выделите отдельный поток/(часть процесса) для выполнения этого запроса. используя элемент управления рабочего стола в С# (в do_work Event)

  • аналогичным образом назначить отдельный поток процесса, используя фонового работника, для отображения статуса индикатора выполнения. (в событии с улучшенным ходом) просто увеличивайте значение на 10%. дайте небольшой сон (в зависимости от времени, затраченного на ваш запрос, разделите его на десять частей). По завершении make Progressbar.value = 100 (в Run-Worker-CompletedEvent)

Пожалуйста, вызовите оба потока в среднее время в backgroundWorker1.RunWorkerAsync();

Я думаю, что это решает вашу проблему.


Ответ 5

Я изучил фоновый рабочий, и я делаю это так Я поместил выполнение моей процедуры на другой поток и индикатор выполнения в потоке графического интерфейса пользователя

 BackgroundWorker bg = new BackgroundWorker();
 Boolean stopwork = true;
 Private void btnYes_Click(object sender, EventArgs e)
        {
            if (DialogResult.Yes == MessageBox.Show(clsGlobalObjectRefrances.OMessageString.SureWant2UpdateMarks, "", MessageBoxButtons.YesNo))
            {
                try
                {

                    bg.DoWork += bg_DoWork;
                    bg.RunWorkerCompleted += bg_RunWorkerCompleted;
                    bg.RunWorkerAsync();
                    pgbUpdateMarks.Maximum = 60;
                    Stopwatch st = new Stopwatch();
                    st.Start();
                    while(stopwork)
                    {
                        pgbUpdateMarks.Value = st.Elapsed.Seconds;
                    }
                    pgbUpdateMarks.Value = 0;
                    MessageBox.Show("Executed sucessfully");
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }

Где DoWork

 void bg_DoWork(object sender, DoWorkEventArgs e)
        {

        dbDataEntities db = new dbDataEntities();
        string myquery = "DECLARE @return_value int EXEC    @return_value = [dbo].[ssspUpdateMarksOfStudent] SELECT 'Return Value' = @return_value";
        db.Database.ExecuteSqlCommand("myquery");
        stopwork = false;
        }