views:

830

answers:

3

Hello everyone,

I am using C# + .Net 3.5 + VSTS 2008 + ADO.Net + SQL Server 2008. And I am sharing one single SQL Connection object (TestDBConnection variable in my below sample) within my application.

The exception I met with is, "There is already an open DataReader associated with this Command which must be closed first.." Any ideas what is wrong?

The patterns within my application which I am using are all like this, i.e. sharing the single db connection object TestDBConnection, and using the single TestDBConnection variable to create command on it and execute store procedure.

        using (SqlCommand testCommand = new SqlCommand())
        {
            testCommand.Connection = TestDBConnection;
            testCommand.CommandType = CommandType.StoredProcedure;
            testCommand.CommandText = "prc_AddOrderStatus";
            testCommand.Parameters.Add("@orderID", SqlDbType.NVarChar).Value = orderID;
            testCommand.ExecuteNonQuery();
        }

thanks in advance, George

+6  A: 

Don't share the connection, use connection pooling instead. If you are doing two things at the same time on the connection, you might want to look into MARS.

For a test add this to your connection string: ;MultipleActiveResultSets=True; and see if this "fixes" the error. A lot of people believe you should avoid using MARS, so this is something to consider.

RichardOD
Thanks RichardOD, sharing connection object is the root cause of my issue? Are there any documents which mentions one connection is used for query Command A, we can not use it for Command B?
George2
Well from what you describe in John's answer comments, it looks like you have some serious multithread issues. You can use the same connection with more than one command (in the same thread), but the recommended approach is to use the connection pool. If you are using more than one command at a time with SQL Server you will need to look into using MARS. Fix the threading/sharing connection issue first and your problem will probably vanish. In your case I don't think you need MARS. HTH.
RichardOD
+1  A: 

George, is it possible that the exception is telling you the truth? Are there any other commands that you've started but not yet finished?

John Saunders
"Are there any other commands that you've started but not yet finished?" -- I think it is the root cause becaue my application is multiple threaded and each thread may execute query using the pattern I mentioned. Any ideas or solutions?
George2
Yes. "Don't _do_ that!" You probably don't even need the multiple threads! And please create a new SqlConnection object for each command execution. The connection pool will take care of things.
John Saunders
@George In a multithreaded environment sharing the same ADO.NET objects is dangerous. You need to create a new one for each multithreaded action. A quick look at the documentation will confirm this "Any instance members are not guaranteed to be thread safe. "
RichardOD
Thanks @RichardOD! So you think the root cause thread safety issue, correct? Which method do you mean (is not thread safe) I am using in a non-thread safe way?
George2
Sharing a connection across multiple threads is not thread safe. Have at look at this- it may help: http://ayende.com/Blog/archive/2006/06/21/StaticThreadSafety.aspx
RichardOD
+2  A: 
using (sqlConnection theconnection = new sqlconnection(initialise it))
{
 using (SqlCommand testCommand = new SqlCommand())
        {
            testCommand.Connection = theConnection
            testCommand.CommandType = CommandType.StoredProcedure;
            testCommand.CommandText = "prc_AddOrderStatus";
            testCommand.Parameters.Add("@orderID", SqlDbType.NVarChar).Value = orderID;
            testCommand.ExecuteNonQuery();
        }
}

is the pattern that i use in multi threaded cases with no problems at all.

Incidently this is connection pooling.

John Nicholas
Thanks MrTortoise! 1. In your code, how do we know connection pool is used other than creating a new connection object each time? I want to avoid creating connection object each time. 2. "initialise it" means?
George2
George. This is how SQL Server connections work by default. We wouldn't be telling you to do this if it didn't work.
John Saunders
George, if you are having issues understanding how the connection pooling actually works, have a look at the SqlConnection class in Reflector. Creating a new SqlConnection instance does not necessarily mean you are creating a new underlying connection if connection pooling is enabled (which by default it is). You will only not use connection pooling if it is disabled of the connection string is different. You can check this by looking at the ADO.NET performance counters.
RichardOD
I used initialise as i cant remember what the constructor is off hand (I use linq a lot now and so ado.net is fading ... i think its the connection string basically)When you create a new connection like this .net goes off to the connection pool and tries to reuse an existing connection. You use the pool by default with this method, so it is all managed for you unless as richard pointed out you have it disabled. I guess if the conenction pool is full then this would create a new connection. Limiting factor is often query execution + sending data time i find.
John Nicholas