3

Does SqlDataAdapter open its own connection?

        private DataTable UpdateOxa(ProductCatalogSyncData syncDataModel, string connectionString)
    {
        var ds = syncDataModel.SyncDataSet;
        var dtResults = new DataTable("BillingIds");
        var syncConfig = syncDataModel.XDataMapping;
        string EntityName;


        string queryString =
                    @"         
                                IF OBJECT_ID('#CRM2Oxa_ID_MAPPING') IS NOT NULL
                               DROP TABLE #CRM2Oxa_ID_MAPPING


                               CREATE TABLE #CRM2Oxa_ID_MAPPING(
                                [EntityName][nvarchar](1000) NULL,

                                   [TableName][nvarchar](1000) NULL,

                                   [CRMID][uniqueidentifier] NULL,

                                   [OxaID][int] NOT NULL,

                                   [CRMColumnName][nvarchar](1000) NULL
                               ) ";
        var listOfSqlCommands = new List<SqlCommand>();
        var OxaConnection = new SqlConnection(connectionString);

        try
        { 


            OxaConnection.Open();

            using (var createTempTableCommand = new SqlCommand(queryString, OxaConnection))
            {
                createTempTableCommand.ExecuteNonQuery();
            }

                foreach (DataTable dt in ds.Tables)
                {
                    EntityName =
                        StringDefaultIfNull(
                            syncConfig.Root.XPathSelectElement("./entity[@name='" + dt.TableName + "']"),
                            "OxaTableName").Substring(3);

                    var OxaCommand = new SqlCommand();

                    OxaCommand.CommandType = CommandType.StoredProcedure;
                    OxaCommand.CommandText = "Oxa720_P_" + EntityName + "Sync";

                    var entityNameParam = new SqlParameter("@EntityName", dt.TableName);
                    OxaCommand.Parameters.Clear();
                    OxaCommand.Parameters.Add(entityNameParam);
                    var tblParam = new SqlParameter("@O720_" + EntityName, SqlDbType.Structured);
                    tblParam.Value = dt;
                    OxaCommand.Parameters.Add(tblParam);
                    OxaCommand.Connection = OxaConnection;

                    listOfSqlCommands.Add(OxaCommand);
                }



                foreach (var command in listOfSqlCommands)
                {
                    using (var da = new SqlDataAdapter(command))
                    {
                        da.Fill(dtResults);
                    }

                }
        }
        finally
        {
            OxaConnection.Close();
        }
        return dtResults;

    }

I'm getting a message back from the database that the table #temptable does not exist.

Does SqlDataAdapter open its own connection? Perhaps this is why it does not see the local temp table?

Alex Gordon
  • 57,446
  • 287
  • 670
  • 1,062

2 Answers2

2

From the documentation on the SqlDataAdapter.Fill() method:

The IDbConnection object associated with the select command must be valid, but it does not need to be open. If the IDbConnection is closed before Fill is called, it is opened to retrieve data and then closed. If the connection is open before Fill is called, it remains open.

So we see here that that SqlDataAdapter does not use any special private connection, but will try to automatically open whatever connection you give it.

The problem you're having here is each call to the .Fill() method happens in a separate Execution Context.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • thank you so much. in this case, how do i force it to occur in the same execution context>? – Alex Gordon Nov 16 '16 at 14:41
  • You can try a Transaction property, but you may need to build all of these into a single large sql string with several commands. You could also try a Global Temporary Table (`##CRM2Oxa_ID_MAPPING` instead of `#CRM2Oxa_ID_MAPPING`), but that can have it's own problems. – Joel Coehoorn Nov 16 '16 at 14:42
  • i cannot use a global temp table because this method will be run by dozens of processes at the same time and each occurrence of this method will need its own temp table – Alex Gordon Nov 16 '16 at 14:46
  • @MeggieLuski I wouldn't. I would step back and refactor things to deal with strings for longer, so I only ever use one command. – Joel Coehoorn Nov 16 '16 at 14:46
  • unfortunately i cannot use just 1 command because i need to execute multiple stored procedures – Alex Gordon Nov 16 '16 at 14:48
  • You can execute any number of stored procedures from a single SqlCommand in a single ExecuteReader/Scalar/NonQuery call. – Joel Coehoorn Nov 16 '16 at 14:48
  • if i have a list of commands, how can i turn that into 1 command? in my current example how would i change from 1 stored procedures per command to all of them in one command? not looking for you to do my work, but just general guidance, thank you again – Alex Gordon Nov 16 '16 at 14:50
  • `command.CommandText = "exec sp_Procedure1 @arg1_1, @arg1_2; exec sp_Procecure2 @arg2_1, @arg2_2; exec sp_ProcedureN @argn_1, @argn_2, @argn_n;";` – Joel Coehoorn Nov 16 '16 at 14:52
  • that takes me back to the issue where i need to create a temp table, and then return results into a datatable – Alex Gordon Nov 16 '16 at 14:55
  • another words, i can execute that commandtext, but it would have to have _command.CommandText = "create #temptable..; exec sp_Procedure1 arg1_1, arg1_2; exec sp_Procecure2 arg2_1, arg2_2; exec sp_ProcedureN argn_1, argn_2,argn_n;";_ ---- the difficulty here is returning results from each sproc into a datatable, specifically **how would i do the datatable.Fill?** – Alex Gordon Nov 16 '16 at 14:57
  • See here: http://stackoverflow.com/questions/11345761/how-to-fill-dataset-with-multiple-tables – Joel Coehoorn Nov 16 '16 at 15:02
  • i still dont see how i can get the #temptable to be visible to every sproc in that command? – Alex Gordon Nov 16 '16 at 15:06
  • You include the commands to create the temp table at the beginning of the commandtext. – Joel Coehoorn Nov 16 '16 at 15:08
  • in the post that you showed me: http://stackoverflow.com/a/11345818/6761907 they did this: Connection.Open(); using (DbDataReader reader = command.ExecuteReader()) { result.MainTable.Load(reader); reader.NextResult(); result.SecondTable.Load(reader); // ... } --- how would i refrain from "getting" the results from "create #temptable.." ? – Alex Gordon Nov 16 '16 at 15:13
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/128275/discussion-between-meggie-luski-and-joel-coehoorn). – Alex Gordon Nov 16 '16 at 15:49
1

If your SqlConnection was already open, then SqlDataAdapter should use it as is (i.e. without closing/opening it).

One possibility as to why your stored procs cannot see the temp table, is that ADO.NET executed your first SqlCommand (used to create the temp table), with a sp_executesql call. That would mean that the temp table gets created within the scope of the stored proc sp_executesql, and would not be visible to subsequent commands, even though you are using the same connection. To check, you could run a Sql Profiler trace - if you see sp_executesql being used for your first SqlCommand, then you'll have a problem.

This comment at: Sql Server temporary table disappears may be relevant:

I honestly think it has to do with the way the SqlCommand text is structured. If it's a simple select into, with no parameters, then it may be run as a simple select statement, so it won't be wrapped in an SqlProcedure like 'sp_executesql', so it will remain visible to subsequent queries using that same SqlCommand and SqlConnection object. On the other hand, if it's a complex statement, the temp table may be created within a stored procedure like 'sp_executesql', and will go out of scope by the time the command is finished. – Triynko Feb 25 '15 at 21:10

If ADO.NET is indeed using sp_executesql for your table creation command, then you might be able to coax it into not using it, by breaking up the command into 2 SqlCommands: one to drop the temp table if it exists, and another to just create the temp table.

EDIT : on a side note, this code:

IF OBJECT_ID('#CRM2Oxa_ID_MAPPING') IS NOT NULL
       DROP TABLE #CRM2Oxa_ID_MAPPING

should probably be:

IF OBJECT_ID('tempdb..#CRM2Oxa_ID_MAPPING') IS NOT NULL
       DROP TABLE #CRM2Oxa_ID_MAPPING

otherwise OBJECT_ID('#CRM2Oxa_ID_MAPPING') will always be null (unless you are already in the temp database).

EDIT 2 : here's some simple code which works for me:

        DataSet ds = new DataSet();

        using(SqlConnection conn = new SqlConnection("YourConnectionString"))
        {
            conn.Open();

            string str = "if object_id('tempdb..#mytest') is not null drop table #mytest; create table #mytest (id int)";

            // create temp table
            using(SqlCommand cmdc = new SqlCommand(str, conn))
            {
                cmdc.ExecuteNonQuery(); 
            }

            // insert row
            using (SqlCommand cmdi = new SqlCommand("insert #mytest (id) values (1)", conn))
            {
                cmdi.ExecuteNonQuery();
            }

            // use it
            using (SqlCommand cmds = new SqlCommand("dbo.mytestproc", conn))
            {
                cmds.CommandType = CommandType.StoredProcedure;
                cmds.Parameters.Add("@id", SqlDbType.Int).Value = 1;
                cmds.Connection = conn;

                using (SqlDataAdapter da = new SqlDataAdapter(cmds))
                {
                    da.Fill(ds);
                }
            } 

            // clean up - drop temp table
            string strd = "if object_id('tempdb..#mytest') is not null drop table #mytest";
            using (SqlCommand cmdd = new SqlCommand(strd, conn))
            {
                cmdd.ExecuteNonQuery();
            }
        }

        MessageBox.Show("done, num rows " + ds.Tables[0].Rows.Count);

The stored proc looks like this:

create proc dbo.mytestproc(@id int)
as
select * from #mytest where id = @id
GO

At the end, it displays : "done, num rows 1"

Community
  • 1
  • 1
Moe Sisko
  • 11,665
  • 8
  • 50
  • 80
  • thank you very much for this invaluable information. i never knew that this is how it works. to answer your question, in the trace there is no sp_executesql, furthermore, the clientprocessid and the spid of the commands, including the temp table creation command are all the same, can we then assume that the "scope" is the same? – Alex Gordon Nov 17 '16 at 15:34
  • @MeggieLuski - yes, if you are not seeing sp_executesql being used , i would expect that the stored proc *should* be able to see the temp table. Is it still not working ? – Moe Sisko Nov 17 '16 at 22:42
  • @MeggieLuski - what did you change to get it to work ? – Moe Sisko Nov 18 '16 at 02:20