2012-02-03 1 views
1

Просто построив приложение C# для запуска SQL-скриптов, я столкнулся с несколькими проблемами с обработкой ошибок.Ошибка TSQL в .Net, но не в SSMS

У нас есть хранимая процедура в нашей базе данных, которая изменяет ограничение по умолчанию для таблиц (путем удаления любого существующего, а затем создания нового), и оно отлично работает, когда мы вызываем его из SSMS. Однако, когда мы вызываем ту же самую хранимую процедуру из .Net SqlClient, она дает ошибку. Я получаю ошибку в SqlException имеет сообщение:

System.Data.SqlClient.SqlException: Column already has a DEFAULT bound to it. 
Could not create constraint. See previous errors. 

Это не должно произойти, так как мы никогда не видим сообщение в SSMS, если мы не запустить заявление падение первого. Если заявление Drop Constraint закомментировано, мы видим одно и то же сообщение.

Почему ограничение не будет удалено при вызове хранимой процедуры из .Net?

Для справки, соответствующий код внутри хранимой процедуры:

SELECT @strSQL = 'ALTER TABLE [' + @strTableName + '] DROP CONSTRAINT [' + @strConstraintName + ']' 
    EXEC sp_executesql @strSQL 

    SELECT @strSQL = 'ALTER TABLE [' + @strTableName + '] ADD CONSTRAINT DF_' + @strTableName + '_' + @strColumnName + ' DEFAULT (' + @strDefaultValue + ') FOR ' + @strColumnName 
    EXEC sp_executesql @strSQL 

EDIT: Я считал захват на наличие ошибок, и игнорирование не фатальные ошибки, такие как это. Однако, к сожалению, эта конкретная ошибка является ошибкой уровня 16, и такие ошибки, как обновление несуществующей таблицы, - это только уровень 15. Поэтому я не могу позволить этой ошибке проходить, фильтруя класс ошибок исключения.

EDIT 2: Если бы было сказано, @strConstraintName определяется автоматически на основе имени таблицы и столбца:

SELECT 
     @strConstraintName = vdc.CONSTRAINT_NAME 
    FROM 
     dbo.vw_DEFAULT_CONSTRAINT vdc 
    WHERE 
     vdc.TABLE_NAME = @strTableName 
     AND vdc.COLUMN_NAME = @strColumnName 

Разрешение

Таким образом, получается, что vw_DEFAULT_CONSTRAINT, что определяло имя ограничения по умолчанию было плохо написано.

По какой-то причине он был основан на значении USER_ID() (представление было написано до того, как я начал), что не было необходимо. Удаление этого ограничения из представления вернуло правильное имя ограничения.

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

Мне кажется странным, что значение USER_ID() не было одинаковым, поскольку соединения были идентичными.

+0

Имеет ли пользователь C# те же привилегии, что и пользователь SSMS? –

+0

@ron tornambe - соединение использует те же учетные данные, поэтому да, это точно то же самое –

+0

Кроме того, пользователь является владельцем базы данных, поэтому имеет полные права на него –

ответ

3

Если запуск трассировки SP-оператора в SQL Profiler не выявил разницы, можете ли вы записать содержимое переменной @SQL в таблицу и просмотреть их позже? Я действительно очень смущен, что они действительно идентичны.

Возможно ли, что ограничение по умолчанию по умолчанию не работает из-за проблемы с именем? Я думаю, вы ответили на это, но это был бы правдоподобный ответ.

Знаете ли вы, что динамический SQL выполняется с разрешениями вызывающего, а не владельца SP, если вы не принимаете специальные меры (например, EXECUTE AS)? Может ли это быть проблемой?

Попробуйте прокомментировать создание по умолчанию из SP, запустив его из .Net и посмотрев, действительно ли по умолчанию был сброшен первый оператор.Или поставьте WAITFOR DELAY '5:00' между ними, чтобы вы могли проверить средний ход.

+0

У меня есть след, и нет заметной разницы. Кажется, что одни и те же утверждения называются в том же порядке. –

+0

@ a_m0d - Что оказалось проблемой? –

+0

@Lieven - Посмотрите мой ответ ниже, для чего проблема. –

1

Попробуйте использовать SQL Server Profiler и сравните запрос из ADO.NET с вашим запросом из SSMS.

1

Попробуйте профилировать выполнение запроса и извлечь операторы SET, выполняемые при подключении SqlClient10. Например, это то, что я профилированные в прошлом при отладке производительности различия запроса, выполняемого SqlClient против запуска его в SSMS:

set quoted_identifier on 
set arithabort off 
set numeric_roundabort off 
set ansi_warnings on 
set ansi_padding on 
set ansi_nulls on 
set concat_null_yields_null on 
set cursor_close_on_commit off 
set implicit_transactions off 
set language us_english 
set dateformat mdy 
set datefirst 7 
set transaction isolation level read committed 

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