Re[2]: Грамотно обработать исключения при работе c SQL Serve
От: kostya.misura  
Дата: 12.01.07 12:07
Оценка:
Здравствуйте, Igore, Вы писали:

1. В таком случае переменную SqlConnection нужно объявлять до блока try.
2. Если SqlConnection обернут в using то в finally нет никакого смысла connection будет disposed при выходе из блока using.
3. Вы finally неправильно написали
Re[3]: Грамотно обработать исключения при работе c SQL Serve
От: kostya.misura  
Дата: 12.01.07 12:17
Оценка:
Здравствуйте, fmiracle

Спасибо. Согласен. Хотя наверное в системах ориентированных на MSSqlServer буду писать по старому.

Можно еще юзать TransactionScope — там в документации все четко про Dispose расписано. Но это уже, наверное, из пушки по воробьям.

И вообще server driven transactions не всегда есть гуд (имхо).
Re[5]: Грамотно обработать исключения при работе c SQL Serve
От: kostya.misura  
Дата: 12.01.07 12:36
Оценка:
Здравствуйте, Pavel M., Вы писали:

PM>что вы имеете в виду?



public class MyCollection : IEnumerable<MyItem>
{
    public IEnumerable<MyItem> Sort(string Value)
    { 
        foreach(MyItem item in this)
        {
            if(item.MyProperty == Value)
                yield return item;
        }
    }
}


MyCollection collection = new MyCollection();
foreach (MyItem item in collection.Sort("MyValue"))
{ 
    //Do something
}

По моему здесь без объяснений понятно, что делает метод Sort, Это я и называю наглядностью. Причем итераторы могут быть любой сложности (кроме некоторых исключений), и при этом проход по коллекции осуществляется только один раз.
Re[4]: Грамотно обработать исключения при работе c SQL Serve
От: Ranger_XL  
Дата: 12.01.07 13:53
Оценка:
Здравствуйте, kostya.misura, Вы писали:

KM>Если использовать 2-й вариант, то надо будет в начале тела цикла ставить command.Parameters.Clear(); Ибо иначе вы добавите в комманду несколько параметров с одинаковыми именами. Что же касается performance — если команда исполняется >1 раза, имхо, предпочтительнее первый вариант, ибо во втором происходит при каждом проходе цикла больше действий, чем в первом, но думаю это не особо существенно.


Остановился на варианте с заданием типов параметров до цикла + cmd.Prepare(). В результате время заливки в локальную базу 1 млн. записей сократилось с 10 до 2 минут, что уже совсем хорошо!

Теперь возникла новая задача — но это уже отдельная тема
Re[6]: Грамотно обработать исключения при работе c SQL Serve
От: Ranger_XL  
Дата: 12.01.07 13:56
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Скорее всего, самый быстрый способ — сделать 100 запросов к базе данных. Затраты на это будут пропорциональны log(N), где N — общее количество файлов, а затраты на подъем всей таблицы в память — O(N).


Так и сделал. (учитывая также, что в перспективе число обработанных файлов в таблице будет расти).
Re[3]: Грамотно обработать исключения при работе c SQL Serve
От: pr0ff  
Дата: 12.01.07 20:17
Оценка:
Здравствуйте, fmiracle, Вы писали:

F>Есть где-нибудь, какое-нибудь требование стандарта, по которому не закоммиченная транзация при Dispose делает Rollback?


Даже если и не сделает (например, по причине обрыва связи), то ее все равно сделает сервер.

Берем рефлектор и смотрим код SqlTransaction.Dispose():
/// <summary>Releases the unmanaged resources used by the <see cref="T:System.Data.Common.DbTransaction"></see>.</summary>
/// <filterpriority>1</filterpriority>
public void Dispose()
{
      this.Dispose(true);
}


SqlTransaction.Dispose(bool):
protected override void Dispose(bool disposing)
{
      if (disposing)
      {
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                  if (!this.IsZombied && !this.IsYukonPartialZombie)
                  {
                        this._internalTransaction.Dispose();
                  }
            }
            catch (OutOfMemoryException exception3)
            {
                  this._connection.Abort(exception3);
                  throw;
            }
            catch (StackOverflowException exception2)
            {
                  this._connection.Abort(exception2);
                  throw;
            }
            catch (ThreadAbortException exception1)
            {
                  this._connection.Abort(exception1);
                  throw;
            }
      }
      base.Dispose(disposing);
}


SqlInternalTransaction.Dispose():
internal void Dispose()
{
      this.Dispose(true);
      GC.SuppressFinalize(this);
}


SqlInternalTransaction.Dispose(bool)
private void Dispose(bool disposing)
{
      Bid.PoolerTrace("<sc.SqlInteralTransaction.Dispose|RES|CPOOL> %d#, Disposing\n", this.ObjectID);
      if (disposing && (this._innerConnection != null))
      {
            this._disposing = true;
            this.Rollback();
      }
}
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: Грамотно обработать исключения при работе c SQL Serve
От: _d_m_  
Дата: 13.01.07 05:32
Оценка: 13 (2)
Здравствуйте, Ranger_XL, Вы писали:

R_X>Итак, у нас есть таблица БД, где хранятся имена уже обработанных файлов (десятки тысяч штук). Нам дан список новых файлов (около 100 штук). Надо исключить из списка те файлы, которые уже хранятся в таблице (таких может быть до 70%).


R_X>Пока видится 2 варианта:

R_X>1) 100 запросов к БД в цикле (для каждого нового файла)
R_X>2) прочитать всю таблицу из БД в память и сделать отбраковку локально

Извиняюсь, проглючило — теперь на свежую голову понял вопрос. Вот нормальное решение без 100...N запросов, а всего лишь с одним:

Таблица:
create table ИменаФайлов(
    Имя nvarchar(256) primary key clustered
)


ХП возвращающая имена файлов которых нет в таблице:
set ansi_nulls, quoted_identifier on
create proc ВозвратитьИменаКоторыхНетВТаблице(
    @xml ntext
)
as begin
    set nocount on

    declare @hdoc int, @ret_code int
    exec @ret_code = dbo.sp_xml_preparedocument @hdoc output, @xmldoc
    if @ret_code != 0
    return @ret_code

    select
        Имя
    from
        openxml(@hdoc, '/root/child::*') with(
            ИмяДляПроверки nvarchar(256) '@И',
    ) as Вход
    where
        not exists(
            select
                null
            from
                dbo.ИменаФайлов иф
            where
                иф.Имя = Вход.ИмяДляПроверки
        )
    ;

    exec dbo.sp_xml_removedocument @hdoc
end


Клиентский код:
    // Имеем на входе:
    //   _СоедБД - соединение с базой данных
    //   _Файлы - список файлов для проверки
    // Возвращаемый результат:
    //   остаются только те, которых нет в БД
    List<string> ПроверкаИменФайловНаНаличиеВБазе(SqlConnection  _СоедБД, List<string> _Файлы)
    {
        SqlCommand com = new SqlCommand("dbo.ВозвратитьИменаКоторыхНетВТаблице", _СоедБД);
        com.CommandType = CommandType.StoredProcedure;
        com.Parameters.Add("@xml", SqlDbType.NText);

        StringBuilder xml = new StringBuilder("<?xml version=\"1.0\"?><root>", 32768);
        foreach (string Файл in _Файлы)
        {
            xml.Append("<с И=\"");
            xml.Append(Файл);
            xml.Append("\"/>");
        }
        xml.Append("</root>");

        List<string> ReturnList = new List<string>(_Файлы.Count);
        com.Parameters["@xml"].Value = xml.ToString();
        using (SqlDataReader reader = com.ExecuteReader(CommandBehavior.SingleResult))
        {
            while (reader.Read())
            {
                ReturnList.Add((string)reader["Имя"]);
            }
        }

        return ReturnList;
    }


Такой код эффективней, чем 100 и более запросов. А на 1000 и более, он просто рвет как Тузик грелку — на несколько порядков.
Re[6]: Грамотно обработать исключения при работе c SQL Serve
От: _d_m_  
Дата: 13.01.07 05:35
Оценка:
Здравствуйте, _d_m_, Вы писали:


ХП возвращающая имена файлов которых нет в таблице:
set ansi_nulls, quoted_identifier on
GO
create proc ВозвратитьИменаКоторыхНетВТаблице(
    @xml ntext
)
as begin
    set nocount on

    declare @hdoc int, @ret_code int
    exec @ret_code = dbo.sp_xml_preparedocument @hdoc output, @xml

    ...
Re[6]: Грамотно обработать исключения при работе c SQL Serve
От: Ranger_XL  
Дата: 13.01.07 07:24
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Скорее всего, самый быстрый способ — сделать 100 запросов к базе данных. Затраты на это будут пропорциональны log(N), где N — общее количество файлов.


Кстати, а почему затраты будут O(log N)?
Re[7]: Грамотно обработать исключения при работе c SQL Serve
От: Sinclair Россия https://github.com/evilguest/
Дата: 15.01.07 05:25
Оценка:
Здравствуйте, Ranger_XL, Вы писали:

R_X>Кстати, а почему затраты будут O(log N)?

Потому что индекс.
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: Грамотно обработать исключения при работе c SQL Serve
От: Sinclair Россия https://github.com/evilguest/
Дата: 15.01.07 08:54
Оценка: 8 (1)
Здравствуйте, fmiracle, Вы писали:
F>Я когда-то тоже делал просто using, используя OleDBProvider для какой-то базы (точно не помню, кажется — DB2 была это)... А потом в том же проекте переключился на использование ее родного драйвера, который при диспозе незакоммиченной транзации делал — ха — коммит!
F>Я сперва не поверил, проверял Рефлектором Да, действительно делал коммит.
Разработчикам этого чуда надо оторвать рабочие конечности.
Не устаю поражаться дефициту здравого смысла в природе!

Пояснения: с точки зрения кода, транзакция должна иметь четко очерченные границы. Крайне нежелательно иметь расхождения структуры кода и структуры транзакций, т.е. к примеру если какая-то бизнес-функция при возврате оставляет незакрытую транзакцию.

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

Поэтому типичный код работы с транзакцией выглядит так:
// begin transaction
try
{
  ...
  // commit transaction
}
catch
{
  // rollback transaction
}

К сожалению, в современных языках нет способа описать "блок, который выполняется только если в защищенном блоке исключений не произошло". Зато можно считать, что "последний оператор в защищенном блоке" и есть такой "блок" — потому, что возникшее до него исключение приведет к выходу из блока.
Поэтому Commit и выполняют в конце блока try.
Далее, безусловный catch — не самое удобное место для rollback transaction. Потому, что могут быть и другие catch(), и в них придется дублировать логику отката. Поэтому в тех языках, где это возможно, применяют и менее красивую, но более надежную конструкцию:
// begin transaction
try
{
  ...
  // commit transaction
}
finally
{
  //if (transaction.stillOpen)
    // rollback transaction
}

Шарп мало того, что предоставляет блок finally, он еще и позволяет заранее, в начале защищаемого блока "заказать" исполнение finally при помощи конструкции using(). Это означает, что в Transaction.Dispose() можно и нужно делать только Rollback! Смысла делать Commit — нету никакого, т.к. это не ведет к сокращению кода.

Сравним код с Dispose == Rollback:
using(Transaction t = connection.BeginTransaction)
{
  ...
  t.Commit();
}

И код с Dispose == Commit:
using(Transaction t = connection.BeginTransaction)
{
  try
    {
  ...
    }
    catch
    {
    t.Rollback();
    }
}

Этот код еще корявее, чем для случая отсутствия управления транзакцией в Dispose.
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Грамотно обработать исключения при работе c SQL Server
От: Аноним  
Дата: 12.01.07 05:05
Оценка:
Самый неясный момент для меня сейчас: как грамотно организовать обработку исключений (и запись их в журнал!) при работе с SQL server
и при этом освободить ресурсы (connection) в случае ошибки?

Это делается классическим способом
try
{
}
catch(SqlException exc)
{
}
finally
{
conn.Close();
}
Хочу заметить, что в Вашем коде есть ошибка. Никогда нельзя перехватывать исключения общего типа. Программист всегда должен знать, какие типы исключений он в данном месте отлавливает.

Вызывает сомнение использование константы DBCONNECTION_STRING для хранения строки подключения. Строки подключения надо хранить в Settings.

Также считаю, что на C# для обхода элементов конкретезированного списка логичнее использовать foreach, а не цикл for.

Согласен с утверждением, что вместо конкатенации строк надо использовать параметризированные запросы. Это все есть стиль хорошего программирования. Но в Вашем случае можно использовать хранимую процедуру. Будет быстрее и трафик меньше

И Если Вас не затруднит, подскажите, пожалуйста, а чем вызвана необходимость вот такой конструкции:
try {
transaction.Rollback ();
}
catch (Exception ex2) {
LogMessage ("WriteRecordsToDB(): transaction.Rollback() failed.", ex2);
}
Почему нельзя сделать просто
transaction.Rollback ();


данное сообщение получено с www.gotdotnet.ru
ссылка на оригинальное сообщение
Re[4]: Грамотно обработать исключения при работе c SQL Serve
От: Аноним  
Дата: 12.01.07 07:50
Оценка:
Итак, у нас есть таблица БД, где хранятся имена уже обработанных файлов (десятки тысяч штук). Нам дан список новых файлов (около 100 штук). Надо исключить из списка те файлы, которые уже хранятся в таблице (таких может быть до 70%).

Пока видится 2 варианта:
1) 100 запросов к БД в цикле (для каждого нового файла)
2) прочитать всю таблицу из БД в память и сделать отбраковку локально


Все намного проще. На сиквел сервере создается
временная таблица со списком файлов (100 имен) и делается INNER JOIN к таблице с файлами.


данное сообщение получено с www.gotdotnet.ru
ссылка на оригинальное сообщение
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.