2016-04-01 2 views
1

Мне нужно переместить код из старого проекта в новый. Старый проект использует DLL с хранимыми процедурами (32-разрядная версия), но мне нужна эта DLL на 64-разрядном SQL Server, поэтому мне нужно переписать эти процедуры.«Ошибка MS DTC отменила распределенную транзакцию» в хранимой процедуре SQLCLR

Я пишу DLL с помощью хранимых процедур для SQL Server 2008. В Management Studio загружает сборку, а затем создать процедуру с помощью:

CREATE PROCEDURE ... 
AS EXTERNAL NAME 

Старых процедур DLL использует только новое подключение к удаленному SQL Server для запуска хранимого процедуры по нему и возвращать результаты.

Так в моей процедуре создать SqlConnection на удаленный сервер и запустить хранимую процедуру на удаленном сервере:

using (SqlConnection connection = new SqlConnection(String.Format("User ID={0};Password={1};Persist Security Info=True;Initial Catalog={2};Data Source={3}", Login, Password, DBName, ServerName))) 
{ 
    connection.Open(); 
    SqlCommand command = new SqlCommand("Exec ProcName", connection); 
    SqlDataReader reader = command.ExecuteReader(); 
    SqlContext.Pipe.Send(reader); 
} 

Если я запускаю эту процедуру в SSMS, она работает. Но в старом проекте возникает ошибка:

The Microsoft Distributed Transaction Coordinator (MS DTC) has cancelled the distributed transaction.

Служба MSDTC запускается, и я задал все параметры безопасности. Как это исправить? Возможно, есть и другие способы запуска удаленных хранимых процедур (связанный сервер), но мне нужно сохранить старые функциональные возможности проекта.

+1

DTC участвует в * транзакциях * Отправьте код, который * начинает * транзакцию. Ваша хранимая процедура делает что-то действительно злобное - она ​​открывает сервер * new connection *, что означает, что любые существующие транзакции, запущенные вашим кодом, должны будут перерасти в распределенные транзакции с использованием DTC. Почему вы просто не создали связанный сервер и не вызвали удаленную процедуру напрямую? Чтобы запустить этот код, вам также пришлось избавиться от безопасности SQLCLR. –

ответ

1

Есть несколько вещей, которые не совсем верно, здесь происходит:

  1. Почему вы переписывания что-нибудь? Если у вас есть код, худший случай просто перекомпилирует для новой архитектуры.

  2. Почему вы делаете что-то в первую очередь? Код должен быть скомпилирован против «Любой процессор» (в разделе «Цель платформы» на вкладке «SQLCLR Build» в «Свойства проекта»), а не специально для 32-битного или 64-битного. И если он уже был скомпилирован под «Any CPU», то делать нечего. Вы тестировали новую систему перед началом любой перекомпиляции и/или перезаписи?

  3. Не используйте String.Format для создания соединительной строки. Вместо этого используйте SqlConnectionStringBuilder:

    SqlConnectionStringBuilder _ConnectionStringBuilder = 
               new SqlConnectionStringBuilder(); 
    
    _ConnectionStringBuilder.DataSource = ServerName; 
    _ConnectionStringBuilder.InitialCatalog = DBName; 
    _ConnectionStringBuilder.UserID = Login; 
    _ConnectionStringBuilder.Password = Password; 
    
  4. Если вы абсолютно не имеют никакого выбора и должны использовать эту опцию, не указывайте Persist Security Info=True;

  5. Вместо использования new SqlCommand(), создайте SqlCommand с помощью:

    using(SqlCommand command = connection.CreateCommand()) 
    { 
        command.CommandText = "Exec ProcName"; 
    } 
    
  6. Обязательно укажите также command.CommandType = CommandType.StoredProcedure;, чтобы он выполнял вызов RPC вместо специального запроса. Это потребует удаления текста «EXEC» с текущего CommandText «EXEC ProcName»; вы можете указать только [[DatabaseName.]SchemaName.]ProcName.

  7. SqlDataReader представляет собой одноразовый предмет, так же, как и SqlConnectionSqlCommand, поэтому SqlDataReader reader = command.ExecuteReader() должны быть завернуты в using() конструкции.

После того, как изложенные выше, были устранены, вы должны быть в состоянии исправить ошибку, просто установив следующее свойство SqlConnectionStringBuilder: _ConnectionStringBuilder.Enlist = false.

Для получения более подробной информации и примеров, связанных с работой с SQLCLR, см. Серию, которую я пишу по этой теме в SQL Server Central: Stairway to SQLCLR (для чтения контента на этом сайте требуется бесплатная регистрация).

+1

1. У меня есть только код старой DLL, а не весь проект. И этот код находится на Паскале. Компиляция под XE6 (до 64 бит) привела к проблемам с типами, и моя команда решила написать новую библиотеку вместо рефакторинга старого кода. Соглашайтесь с вами, спасибо! Мне просто нужно удалить «Exec» из текста команды, если я использую тип «StoredProcedure». – cerberus

+0

@cerberus Да, жаль, что не упоминал об удалении 'EXEC', поскольку это может быть только' [[DatabaseName.] SchemaName.] ProcName'. Я забыл, что был там, потому что я пытался забыть другие подробности ;-). Я обновлю свой ответ, чтобы включить этот бит. Но как это делается в Паскале? Вы имеете в виду Delphi XE6? Скомпилирован ли XE6 для .NET MSIL? Согласно https://www.embarcadero.com/products/delphi/faq это не так. Или я чего-то не хватает? –

+0

старый код - это проект Delphi 7, он использует API ODS. поэтому он не имеет ничего с .net. мы попробовали XE6 построить до 64-битной платформы, но было много ошибок с типами. поэтому мы решили написать на C# – cerberus

Смежные вопросы