views:

463

answers:

2

Hi!

Can I avoid the open DataReader exception ("There is already an open DataReader associated with this Command which must be closed first.") when using constructs like this one?

public void FirstMethod()
{
    using (var command = connection.CreateCommand())
    {
        command.CommandText = "...";
        using (var reader = command.ExecuteReader())
        {
            // do something with the data
            SecondMethod();
        }
    }
}

public void SecondMethod()
{
    using (var command = connection.CreateCommand())
    {
        command.CommandText = "...";
        using (var reader = command.ExecuteReader()) // Exception
        {
        }
    }
}

Best Regards

+3  A: 

You could use local connections (i.e. local to the method); with connection pooling, the advantages of keeping a connection as a field are minimal, and regularly cause this type of... fun. Of course, you can then get into issues of blocking yourself if you are editing data - perhaps mitigated using things like TransactionScope. Example at the bottom...

Alternatively, you could enable MARS (multiple active result sets) on the connection; that should do it. Just include "MultipleActiveResultSets=True" as a pair in the connection-string (SQL Server 2005 and above).

public void FirstMethod() {
    using (var connection = CreateAndOpenConnection())
    using (var command = connection.CreateCommand()) {
        command.CommandText = "...";
        using (var reader = command.ExecuteReader()) {
            // do something with the data
            SecondMethod();
        }
    }
}
public void SecondMethod() {
    using (var connection = CreateAndOpenConnection())
    using (var command = connection.CreateCommand()) {
        command.CommandText = "...";
        using (var reader = command.ExecuteReader()) // Exception
        { }
    }
}
private SqlConnection CreateAndOpenConnection() {
    var conn = new SqlConnection(connectionString);
    conn.Open(); // perhaps dispose if this fails...
    return conn;
}
Marc Gravell
A: 

Why don't you just change your method signatures?

public void FirstMethod()
{
    using (var command = connection.CreateCommand())
    {
        command.CommandText = "...";
        using (var reader = command.ExecuteReader())
        {
            // do something with the data
            SecondMethod(reader);
        }
    }
}

public void SecondMethod(var reader)
{
    // do stuff with reader...
}

Not only does this avoid the problem, but it also incurs less overhead as you're only creating 1 command/reader as opposed to 2. (This assumes your method calls are synchronous.)

Also, consider using strongly-typed variables instead of the var keyword; var was introduced to support anonymous types and should be used only if necessary.

Ian Kemp
Well I would do that if the second methods would base on the same sql statement, which is of course not the case (otherwise, why would I need to methods?).
Oliver Hanappi
@Ian: All variables are strongly-typed in C#, however you declare them. (C#4, when it's released, will allow `dynamic` variables, but they're nothing to do with the `var` keyword.)
LukeH
Yes, the var keyword just hides the concrete type, but represents it implicitly. So you can write "int i = 1" as well as "var i = 1", both statements are equal.
Oliver Hanappi
Is not the reader "forward-only"? Therefore, you would not be able to // do stuff with reader...
AMissico
@AMissico: A `DataReader` can potentially contain multiple resultsets. See the `NextResult` method: http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.data.framework.datareader.nextresult.aspx
LukeH