MSDTC Исключение во время транзакции: C # - PullRequest
1 голос
/ 19 июня 2011

Я получаю исключение MSDTC в транзакции в приложении C #. Функциональность заключается в загрузке одной lakh (сто тысяч) записей почтового индекса в таблицы базы данных после чтения из файла CSV. Эта операция выполняется примерно в 20 пакетных операциях с базой данных (каждая из которых содержит 5000 записей). Функциональность работает нормально, если я не использую транзакцию.

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

Есть мысли о том, в чем может быть проблема?

Исключение: «Доступ к сети для диспетчера распределенных транзакций (MSDTC) отключен. Включите DTC для доступа к сети в конфигурации безопасности MSDTC с помощью инструмента администрирования служб компонентов. ”

Источник: System.Transactions

Внутреннее исключение: «Менеджер транзакций отключил поддержку удаленных / сетевых транзакций. (Исключение из HRESULT: 0x8004D024) ”

Примечание. Внутри транзакции есть цикл for. Это вызывает какие-либо проблемы?

Фактическое требование: в таблице почтовых индексов есть несколько существующих почтовых индексов. Каждый месяц администратор будет загружать новый CSV-файл zipcode. Новые элементы из CSV вставляются. Почтовые индексы, которые отсутствуют в csv (но присутствуют в базе данных), считаются списанными и подлежат удалению. Список списанных почтовых индексов должен быть возвращен в интерфейс пользователя. Вновь добавленные почтовые индексы также необходимо вернуть.

    private void ProcessZipCodes(StringBuilder dataStringToProcess, int UserID)
    {
        int CountOfUnchangedZipCode = 0;
        string strRetiredZipCode = "";
        string strNewZipCode = "";
        dataStringToProcess.Remove(dataStringToProcess.Length - 1, 1);

        if (dataStringToProcess.Length > 0)
        {

            List<string> batchDataStringList = GetDataStringInBatches(dataStringToProcess);

           //TimeSpan.FromMinutes(0) - to make transaction scope as infinite.
            using (TransactionScope transaction = TransactionScopeFactory.GetTransactionScope(TimeSpan.FromMinutes(0)))
            {

                foreach (string dataString in batchDataStringList)
                {
                    PerformDatabaseOperation(dataString, UserID);
                }

                transaction.Complete();
            }
        }


    }

    private List<string> GetDataStringInBatches(StringBuilder dataStringToProcess)
    {

        List<string> batchDataStringList = new List<string>();
        int loopCounter = 0;
        string currentBatchString = string.Empty;
        int numberOfRecordsinBacth = 5000;
        int sizeOfTheBatch = 0;

        List<string> individualEntriesList = new List<string>();
        string dataString = string.Empty;
        if (dataStringToProcess != null)
        {
            dataString = dataStringToProcess.ToString();
        }
        individualEntriesList.AddRange(dataString.Split(new char[] { '|' }));


        for (loopCounter = 0; loopCounter < individualEntriesList.Count; loopCounter++)
        {

            if (String.IsNullOrEmpty(currentBatchString))
            {
                currentBatchString = System.Convert.ToString(individualEntriesList[loopCounter]);
            }
            else
            {
                currentBatchString = currentBatchString+"|"+System.Convert.ToString(individualEntriesList[loopCounter]);
            }

            sizeOfTheBatch = sizeOfTheBatch + 1;
            if (sizeOfTheBatch == numberOfRecordsinBacth)
            {
                batchDataStringList.Add(currentBatchString);
                sizeOfTheBatch = 0;
                currentBatchString = String.Empty;
            }


        }

        return batchDataStringList;


    }

    private void PerformDatabaseOperation(string dataStringToProcess, int UserID)
    {
        SqlConnection mySqlConnection = new SqlConnection("data source=myServer;initial catalog=myDB; Integrated Security=SSPI; Connection Timeout=0");
        SqlCommand mySqlCommand = new SqlCommand("aspInsertUSAZipCode", mySqlConnection);
        mySqlCommand.CommandType = CommandType.StoredProcedure;
        mySqlCommand.Parameters.Add("@DataRows", dataStringToProcess.ToString());
        mySqlCommand.Parameters.Add("@currDate", DateTime.Now);
        mySqlCommand.Parameters.Add("@userID", UserID);
        mySqlCommand.Parameters.Add("@CountOfUnchangedZipCode", 1000);
        mySqlCommand.CommandTimeout = 0;
        mySqlConnection.Open();
        int numberOfRows = mySqlCommand.ExecuteNonQuery();
    }

Dev Env: Visual Studion 2005

Framework: .Net 3.0

БД: SQL Server 2005

Когда я запускаю запрос SELECT [Размер], Max_Size, Data_Space_Id, [File_Id], Type_Desc, [Имя] FROM MyDB.sys.database_files WHERE data_space_id = 0 - это говорит, что размер (журнала) составляет 128

UPDATE У нас есть три разные базы данных, используемые в нашем приложении. Один для данных, один для истории и один для регистрации. Когда я добавляю enlist = false в вышеупомянутую строку подключения, в настоящее время это работает. Но это в моей среде разработки. Я скептически отношусь к тому, будет ли это работать и в производстве. Есть мысли о потенциальных рисках?

Спасибо

Lijo

Ответы [ 3 ]

6 голосов
/ 19 июня 2011

Когда вы открываете более одного соединения в пределах TransactionScope, запущенная транзакция автоматически преобразуется в распределенную транзакцию. Чтобы распределенные транзакции работали, MSDTC как на SQL Server, так и на компьютере, на котором выполняется приложение, должен быть настроен на разрешение доступа к сети. SQL Server и локальный DTC взаимодействуют при выполнении распределенных транзакций.

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

  1. Перейдите в «Панель управления» -> «Администрация» -> «Службы компонентов».
  2. Просматривайте дерево, пока не дойдете до узла с именем "Local DTC" или чего-то в этом роде.
  3. Щелкните правой кнопкой мыши и выберите «Свойства».
  4. Перейдите в раздел «Безопасность» и убедитесь, что вы разрешаете доступ к сети, а также разрешаете входящий и исходящий обмен данными с DTC.
  5. Нажмите «ОК».

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

Кстати, не забудьте закрыть соединение после использования в PerformDatabaseOperation. Хорошей практикой является поместить его в using блок:

using (SqlConnection mySqlConnection = new .....)
{
    // Some code here...
    mySqlConnection.Open();
    // Some more code ...
}
1 голос
/ 19 июня 2011

Возможно ли, что aspInsertUSAZipCode взаимодействует со связанным сервером? Если это произойдет, он попытается преобразовать вашу текущую локальную транзакцию в распределенную транзакцию (с сохранением целостности транзакции между вашим сервером и связанным сервером).

Может потребоваться сделать это вне транзакции, если MSDTC на удаленном сервере нельзя настроить для распределенных транзакций. Я думаю, что лучший способ сделать это - создать временную таблицу, а затем SqlBulkCopy свои записи в нее и затем выполнить aspInsertUSAZipCode, используя временную таблицу на сервере. Возможно, вам придется использовать курсор.

Назначение временной таблицы состоит в том, чтобы, если что-то пошло не так, таблица была удалена при разрыве вашего соединения.

0 голосов
/ 19 июня 2011

Возможно, вы достигли максимального количества данных в пределе транзакции.

Проверьте журнал событий на наличие ошибок msdtc http://technet.microsoft.com/en-us/library/cc774415(WS.10).aspx

Наличие 100 000 строк в одной транзакции приведет кдоставит вам проблемы.

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

...