Показывать ход выполнения через индикатор выполнения
У меня есть глупая проблема, но я застрял. Я выполняю хранимую процедуру из моего кода
процедура занимает время, поэтому для этого я показываю индикатор выполнения, который показывает ход выполнения, но хранимая процедура выполняется, и нет ничего, к чему я увеличиваю значение индикатора выполнения.
Это мой код
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());
Ответ 3
Хранимые процедуры не предоставляют информацию о ходе. Вы можете использовать стиль ProgressBar
в Marquee
свойство ProgressBar.Style для ProgressBarStyle.Marquee
Ответ 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;
}