Как лучше всего отображать информацию о прогрессе при использовании ADO.NET?

Я хочу показать пользователю подробную информацию о ходе выполнения потенциально длительных операций с базой данных. В частности, при вставке / обновлении данных, размер которых может составлять порядка сотен КБ или МБ.

В настоящее время я использую DataTables и DataRow в памяти, которые затем синхронизируются с базой данных с помощью вызовов TableAdapter.Update. Это работает отлично и изящно, но единственный вызов оставляет мало возможностей для сбора какой-либо информации о ходе выполнения, чтобы показать ее пользователю. Я понятия не имею, сколько данных передается по сети в удаленную БД и каков прогресс. По сути, все, что я знаю, - это когда обновление возвращается, и оно считается завершенным (за исключением любых ошибок или исключений). Но это означает, что все, что я могу показать, это 0%, затем пауза, а затем 100%.

Я могу подсчитать количество строк, даже зайдя так далеко, чтобы определить, сколько на самом деле изменено или добавлено, и я мог бы даже, возможно, рассчитать для каждого DataRow его предполагаемый размер на основе типа данных каждого столбца, используя sizeof для типов значений, таких как int и проверки length для таких вещей, как строки или байтовые массивы. При этом я, вероятно, мог бы определить перед обновлением приблизительный общий размер передачи, но я все еще застрял без какой-либо информации о ходе после того, как Update вызывается в TableAdapter.

Я застрял, просто используя неопределенный индикатор выполнения или курсор ожидания мыши? Нужно ли мне радикально изменить наш уровень доступа к данным, чтобы иметь возможность подключаться к такой информации? Даже если я не могу получить точный переданный КБ (например, индикатор загрузки файла в веб-браузере), могу ли я хотя бы знать, когда заканчивается каждый DataRow / DataTable или что-то в этом роде?

Как лучше всего показать такую ​​информацию о прогрессе с помощью ADO.NET?


person Sean Hanley    schedule 10.04.2010    source источник


Ответы (2)


Существует полурешение для части SELECT, которое заключается в том, чтобы сначала выполнить запрос COUNT, чтобы получить количество строк, которые вы ожидаете получить. Это практично только в том случае, если запрос COUNT может вернуть результат очень быстро (то есть за доли секунды) - с другой стороны, если для выполнения требуется несколько секунд, то само выполнение запроса (в отличие от перечисления результатов) может занимает больше времени, чем передача данных, и в этом случае вообще не стоит пытаться показывать дискретный индикатор выполнения.

Что касается UPDATE и INSERT - нет, для этого нет простого решения, особенно с использованием адаптеров таблиц. Если у вас есть большой объем данных для отправки, вы можете рассмотреть возможность использования SqlBulkCopy для загрузки в промежуточную таблицу и последующего фактического обновления на сервере. Этот класс предлагает NotifyAfter вместе со свойством SqlRowsCopied, которое может дать вам разумное приближение к текущему прогрессу (вы уже знаете общее количество строк в этом случае, поскольку они находятся в памяти).

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

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

Если есть реальная вероятность того, что выполнение самого запроса (или обновления) на самом сервере займет много времени, не считая времени на загрузку / скачивание записей, то я определенно использовал бы вместо этого индикатор выполнения. В противном случае ... удачи.

person Aaronaught    schedule 10.04.2010
comment
SqlBulkCopy пытается вставить все строки как новые? Под этим я подразумеваю, что если в DataTable, переданном ему при вызове WriteToServer, есть строки с RowState == Modified, то обновляет ли он эти строки или вставляет копии с новыми PK? Что, если KeepIdentity включен? Если он попытается вставить, а PK совпадают, я полагаю, это вызовет исключение SqlException. Может быть, это заслуживает отдельного вопроса. Документы MSDN не были конкретными ... - person Sean Hanley; 11.04.2010
comment
Неважно. Когда документы MSDN терпят неудачу, блоги MSDN приходят на помощь. blogs.msdn.com/irenak/archive/2006/02/ 10 / 529553.aspx - person Sean Hanley; 11.04.2010
comment
@Yadyn: SqlBulkCopy завершает операцию массовой вставки, которая не обновляется, поэтому вам нужно использовать промежуточную таблицу. В общем, вам не повезет, если DataSet и связанные классы будут работать с любыми другими классами данных во фреймворке - вам, вероятно, придется начать отходить от них, поскольку они основаны на концепция загрузки всего набора записей в память, а не их потоковая передача из TDS. - person Aaronaught; 11.04.2010

Как сказал Ааронаут - SqlBulkCopy с NotifyAfter должен работать (хотя он работает для меня).

Я не думаю, что это будет «существенное изменение» в вашей текущей реализации DAL ...

person sventevit    schedule 10.04.2010