2013-06-10 1 views
0

У меня есть таблица в базе данных SQL Server, которая представляет файл журнала некоторых действий, вставленных из службы запущенных окон. Все работает хорошо.SqlDependency using BackgroundWorker

Но у меня есть приложение Windows, которое получает последние строки, которые были вставлены в таблицу журналов, и просматривает их в DataGridView. При разработке этого приложения я зависел от Using SqlDependency in a Windows Application от MSDN. Он работает хорошо, но когда таблица журналов получает большое количество данных журнала, приложение Windows зависает, и основной пул потоков становится слишком занятым.

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

Вы можете увидеть скриншот интерфейса по этой ссылке "UI"

No. (1): Это GroupBox представляет инструменты пользовательского интерфейса, которые пользователи могут использовать его во время мониторинга.

№ (2): Пуск button отвечает за то, что он начал слушать и получать обновления из базы данных и пополнять DataGridView.

№ (3): Эта сетка представляет новые журналы, которые были вставлены в базу данных.

№ (4): Это число (38 изменений) представляет собой счетчик прослушивания зависимости sql к изменениям базы данных.

Мой код: общественный частичный класс frmMain: Форма { SqlConnection сопп;

То, что я хочу, а именно: когда пользователь нажимает кнопкуStart, программа запуска кода в отдельном пуле потоков.

+0

Смешения зависимости, например, с BackgroundWorker собирается усложнять. Возможно, подумайте об использовании только одного. –

+0

Интересным упражнением для этого приложения будет прослушиватель приложений, подписавшийся на службу выигрыша и сигнал отправки услуги, обновляемый журнальными таблицами. В это время приложение win будет искать новые данные. –

+0

Спасибо за поддержку, но я не могу разделить приложение на два раздела, как вы сказали. Я должен разработать его в зависимости от предыдущего объяснения, которое в сообщении. Мне просто нужно запустить тот же код в отдельный ** пул потоков ** или запустить его с помощью ** BackgroundWorker **. –

ответ

1

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

Действительно, причиной того, что приложение зависает, является обновление пользовательского интерфейса в потоке пользовательского интерфейса.

Не могли бы вы показать код, отвечающий за это обновление, пожалуйста?

Если есть много уведомлений визуальное обновление будет больше, и вы не можете сделать многое:

  • изменение сетки на куски, чтобы сгладить обновление: вместо вставки 1000 новых записей, запустить 10 обновлений 100 записей, но вы берете на себя риск быть перегружены данными, если вы не обрабатывать их достаточно быстро

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

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

UPDATE:

Таким образом, если первая часть функции GetData является узким местом, то на самом деле вы можете использовать другой поток, например, с резьбовым бассейна:

private void GetData() 
{ 
    // Start the retrieval of data on another thread to let the UI thread free 
    ThreadPool.QueueUserWorkItem(o => 
    { 
     // Empty the dataset so that there is only 
     // one batch of data displayed. 
     dataToWatch.Clear(); 

     // Make sure the command object does not already have 
     // a notification object associated with it. 
     command.Notification = null; 

     // Create and bind the SqlDependency object 
     // to the command object. 
     SqlDependency dependency = new SqlDependency(command); 

     dependency.OnChange += new OnChangeEventHandler(dependency_OnChange); 

     using (SqlDataAdapter adapter = new SqlDataAdapter(command)) 
     { 
      adapter.Fill(dataToWatch, tableName); 

      // Update the UI 
      dgv.Invoke(() => 
       { 
        dgv.DataSource = dataToWatch; 
        dgv.DataMember = tableName; 
        dgv.FirstDisplayedScrollingRowIndex = dgv.Rows.Count - 1; 
       }); 
     } 
    }); 
} 

Так единственная часть, которая будет работать в потоке пользовательского интерфейса является обновление DataGrid.

Не тестировался, но надеюсь, что это помогает ...

ПОСЛЕДНИЙ? UPDATE:

С некоторой синхронизацией, чтобы избежать одновременного выполнения:

AutoResetEvent running = new AutoResetEvent(true); 

private void GetData() 
{ 
    // Start the retrieval of data on another thread to let the UI thread free 
    ThreadPool.QueueUserWorkItem(o => 
    { 
     running.WaitOne(); 

     // Empty the dataset so that there is only 
     // one batch of data displayed. 
     dataToWatch.Clear(); 

     // Make sure the command object does not already have 
     // a notification object associated with it. 
     command.Notification = null; 

     // Create and bind the SqlDependency object 
     // to the command object. 
     SqlDependency dependency = new SqlDependency(command); 

     dependency.OnChange += new OnChangeEventHandler(dependency_OnChange); 

     using (SqlDataAdapter adapter = new SqlDataAdapter(command)) 
     { 
      adapter.Fill(dataToWatch, tableName); 

      running.Set(); 

      // Update the UI 
      dgv.Invoke(() => 
       { 
       dgv.DataSource = dataToWatch; 
       dgv.DataMember = tableName; 
       dgv.FirstDisplayedScrollingRowIndex = dgv.Rows.Count - 1; 
       }); 
     } 
    }); 
} 
+0

Большое спасибо за вашу поддержку. Позвольте мне подробно объяснить эту идею. Забудьте, что есть служба Windows, что-то делать, но помните следующее: У меня есть datagrid, и зависимость sql всегда получает строки из базы данных в зависимости от sql-запроса. Таким образом, я не могу использовать элементы управления пользовательского интерфейса при заполнении datagrid, это похоже на использование цикла бесконечности в вашем приложении. Итак, я хочу запустить код «http://msdn.microsoft.com/en-us/library/a52dhwx7%28v=vs.80%29.aspx» в разделенном пуле потоков или BackgroundWorker для позволяют пользователям легко использовать пользовательский интерфейс. Вы можете перейти к ссылке, чтобы увидеть код. –

+0

Я обновил свой вопрос, вы можете увидеть сценарий кода и скриншот пользовательского интерфейса. Большое спасибо. –

+0

Еще раз спасибо за вашу любезную помощь :), но когда я использовал ваш код, я получил эту ошибку ** «Уже есть открытый DataReader, связанный с этой командой, который должен быть закрыт первым». **, хотя у меня нет считывателя данных и Я использую ** «MultipleActiveResultSets = True» ** в строке соединения, чтобы избежать этих ошибок. –