a_m0d
a_m0d

Reputation: 12205

TSQL error in .Net but not in SSMS

Having just built a c# application to run SQL scripts, I've run into a few issues with error handling.

We have a stored procedure in our database which modifies the default constraint for tables (by dropping any existing one and then creating the new one), and it runs fine when we call it from SSMS. However, when we call the same stored procedure from the .Net SqlClient, it gives an error. The error I get in the SqlException has the message:

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

This shouldn't happen, since we never see the message in SSMS unless we don't run the drop statement first. If the Drop Constraint statement is commented out, we see the same message.

Why would the constraint not get dropped when we call the stored procedure from in .Net?

For reference, the relevant code inside the stored procedure is:

  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: I considered trapping for errors, and ignoring non-fatal errors such as this. However, unfortunately this specific error is a Level 16 error, and errors such as updating a non-existent table are only Level 15. So I can't let this error pass by filtering on the Error Class of the exception.

EDIT 2: Should have mentioned, the @strConstraintName is determined automatically based on the table name and column name:

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

Resolution

So, it turns out that the vw_DEFAULT_CONSTRAINT that was determining the name of the default constraint was badly written.

It was filtering based on the value of USER_ID() for some reason (the view was written before I started) which wasn't necessary. Removing this restriction from the view returned the correct constraint name.

@ErikE's answer provided a good push in the correct direction. Saving the variable values into a temporary table allowed me to see that the 2 methods weren't actually working the same, which led me to investigate the view further. Kudos to @ErikE for this, which is why I selected his as the correct answer.

I do find it strange though that the value of USER_ID() was not the same, since the connections were identical.

Upvotes: 1

Views: 428

Answers (3)

Pero P.
Pero P.

Reputation: 26992

Try profiling the execution of the query and extracting the SET statements the SqlClient executes on connection. For example this is what I had profiled in the past when debugging performance differences of a query executed by the SqlClient versus running it in 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

Run these SET statements into SSMS and then try executing your query there again to see if you can replicate the behaviour. Whilst I don't believe this will surface your issue it is at least a good exercise to ensure your session configuration in SSMS is the same as if the query were being executed by the SqlClient

Upvotes: 1

ErikE
ErikE

Reputation: 50251

If running an SP-statement trace in SQL Profiler doesn't reveal a difference, can you log the @SQL variable contents being executed to a table and review them afterward? I'm really quite confused that they are truly identical.

Is it possible that the drop default constraint is failing due to a name problem? I think you answered this but it would be a plausible answer.

Did you know that dynamic SQL is executed with the permissions of the caller, not the SP owner, unless you take special measures (such as EXECUTE AS)? Could this be the issue?

Try commenting out the create default from the SP, running it from .Net, and seeing if the default was really dropped by the first statement. Or put a WAITFOR DELAY '5:00' in between them so you can check mid-run.

Upvotes: 3

pistipanko
pistipanko

Reputation: 775

Try tu use SQL Server Profiler and compare the query from the ADO.NET to your query from the SSMS.

Upvotes: 1

Related Questions