In the last few weeks at work, I have had multiple people have issues using a parameterized query in .NET that involved a temp table. It took a little bit of digging, but we finally tracked down the issue. This post is going to cover the cause of the issue as well as a couple of ways to fix it. The database used in this post is Wide World Importers sample database from Microsoft. For instructions on setting it up check out my Getting a Sample SQL Server Database post from last week.

Sample Project Creation

To keep things as simple as possible I am using a console application created using the following .NET CLI command.

dotnet new console

Followed by this command to add in the SQL Client from Nuget.

dotnet add package System.Data.SqlClient

The following is the full Program class with the sample code that will result in the exception this post is dealing with. Yes, I am aware this isn't the be way to structure this type of code so please don't judge it from that aspect. It is meant to be simple to demonstrate the issue.

class Program

{

static void Main(string[] args)

{

Console.WriteLine("Running sample");



using (var connection =

new SqlConnection(@"Data Source=YourServer;

Initial Catalog=YourDatabase;

Integrated Security=SSPI;"))

{

connection.Open();



using (var command = connection.CreateCommand())

{

SqlTest(command);

}

}



Console.ReadLine();

}



private static void SqlTest(SqlCommand command)

{

command.CommandText = @"SELECT OrderId

,CustomerId

,SalespersonPersonID

,BackorderOrderId

,OrderDate

INTO #backorders

FROM Sales.Orders

WHERE BackorderOrderID IS NOT NULL

AND OrderDate > @OrderDateFilter";



command.Parameters.Add("@OrderDateFilter",

SqlDbType.DateTime)

.Value = DateTime.Now.AddYears(-1);

command.ExecuteNonQuery();



command.CommandText = "SELECT OrderId FROM #backorders";



using (var reader = command.ExecuteReader())

{

while (reader.Read())

{

Console.WriteLine(reader["OrderId"]);

}

}

}

}

The Error

Running the application as it exists above will result in the following error.

Invalid object name ‘#backorders’.

Strange error since we just created the #backorder temp table. Let’s give it a try without the filter. The query now looks like the following.

command.CommandText = @"SELECT OrderId

,CustomerId

,SalespersonPersonID

,BackorderOrderId

,OrderDate

INTO #backorders

FROM Sales.Orders

WHERE BackorderOrderID IS NOT NULL";



command.ExecuteNonQuery();

Now the application runs without any issues. What if we try adding back the filter, but without using the command parameter?

command.CommandText = @"SELECT OrderId

,CustomerId

,SalespersonPersonID

,BackorderOrderId

,OrderDate

INTO #backorders

FROM Sales.Orders

WHERE BackorderOrderID IS NOT NULL

AND OrderDate > '2018-01-01'";



command.ExecuteNonQuery();

Again the application runs without any issues.

The Reason

Why is it that adding a command parameter is causing our temp table to disappear? I discovered the issue by using SQL Server Profiler (in SQL Server Management Studio it can be found in Tools > SQL Server Profiler). With the code back to the original version with the command parameter and Profiler connected to the same server as the sample application running the sample application shows the following command received by SQL Server.

exec sp_executesql N'SELECT OrderId

FROM #backorders',

N'@OrderDateFilter datetime',

@OrderDateFilter='2017-08-28 06:41:37.457'

It turns out that when you use command parameters in .NET it gets executed on SQL Server using the sp_executesql stored procedure. This was the key bit of information I was missing before. Now that I know parameterized queries are executed in the scope of a stored procedure it also means the temp table used in our first query is limited to the usage within the stored procedure in which it was created.

by Caspar Rubin on Unsplash

Options to Fix

The first option is to not use parameters on your initial data pull. I don’t recommend this option. Parameters provide a level of protection that we don’t want to lose.

The second option and the way we addressed this issue is to create the temp table first. Now that the temp table has been created outside of a stored procedure it is scoped to the connection and then allows us to insert the data using parameters. The following code is our sample using this strategy.

command.CommandText = @"CREATE TABLE #backorders

(

OrderId int

,CustomerId int

,SalespersonPersonID int

,BackorderOrderID int

,OrderDate date

)";



command.ExecuteNonQuery();



command.CommandText = @"INSERT INTO #backorders

SELECT OrderId

,CustomerId

,SalespersonPersonID

,BackorderOrderId

,OrderDate

FROM Sales.Orders

WHERE BackorderOrderID IS NOT NULL

AND OrderDate > @OrderDateFilter";



command.Parameters.Add("@OrderDateFilter",

SqlDbType.DateTime)

.Value = DateTime.Now.AddYears(-1);

command.ExecuteNonQuery();

Wrapping Up

I hope this saves someone some time. Once I understood what was going on the issue made sense. How .NET deals with a SQL command with parameters is one of those things that just always worked and I never had the need to dig into until now. Always something new to learn which is one of the reasons I love what I do.