views:

69

answers:

3

This is interesting (to me anyway), and I'd like to see if anyone has a good answer and explanation for this behavior.

Say you have a singleton database object (or static database object), and you have it stored in a class Foo.

public class Foo
{
    public static SqlConnection DBConn = new SqlConnection(ConfigurationManager.ConnectionStrings["BAR"].ConnectionString);
}

Then, lets say that you are cognizant of the usefulness of calling and disposing your connection (pretend for this example that its a one-time use for purposes of illustration). So you decide to use a 'using' block to take care of the Dispose() call.

using (SqlConnection conn = Foo.DBConn)
{
    conn.Open();
    using (SqlCommand cmd = new SqlCommand())
    {
        cmd.Connection = conn;
        cmd.CommandType = System.Data.CommandType.StoredProcedure;
        cmd.CommandText = "SP_YOUR_PROC";

        cmd.ExecuteNonQuery();
    }
    conn.Close();
}

This fails, throwing an exception on the call to open the connection, stating that the "ConnectionString property is not initialized". It's not an issue with pulling the connection string from the app.config/web.config. When you investigate in a debug session you see that Foo.DBConn is not null, but contains empty properties.

Why is this?

A: 

Perhaps you do not have the corresponding connectionStrings node in you web/app.config?

<connectionStrings>
<add name="BAR"
     connectionString="Data Source=localhost\sqlexpress;Initial Catalog=mydatabase;User Id=myuser;Password=mypassword;" />

Jonas Montonen
No it's there. Proven as it works if you use a non-static variant of the code.
byte
+2  A: 

A little out of topic and not really answering your question but why using a singleton for SqlConnection when ADO.NET already uses a connection pool? Your code could have very well looked like this:

using (var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["BAR"].ConnectionString))
using (var cmd = conn.CreateCommand())
{
    conn.Open();
    cmd.Connection = conn;
    cmd.CommandType = System.Data.CommandType.StoredProcedure;
    cmd.CommandText = "SP_YOUR_PROC";
    cmd.ExecuteNonQuery();
}

And one less thing to worry about in your program: connection lifecycle

Darin Dimitrov
Because this is a language oddity question, not an architectural one :) Thanks for the input though.
byte
A: 

The static field is evaluated sometime before it is used (not deterministically). See beforefieldinit for more detail. So the system may not be ready for creating an SQL-connection when it is called or maybe even properly create the static field after you use it.

Additionally, how would you handle a second SQL-command after you closed the first one? I don't know exactly how SqlConnection works, but after closing (note that this cals Dispose) and disposing the connection, your static Foo.DBConn should be gone, i.e. it won't be reevaluated.

If you want to keep your basic infrastructure, I would replace the static field with a static property which returns a new SqlConnection on get:

public static SqlConnection DBConn
{
    get
    {
        return new SqlConnection(ConfigurationManager.ConnectionStrings["BAR"].ConnectionString);
    }
}
Daniel Rose
@Daniel Rose - You missed the part in the original post about "(pretend for this example that its a one-time use for purposes of illustration)"
byte
@byte You missed the part in my post about "the static field is evaluated sometime before it is used".
Daniel Rose
No I caught that, and you erroneously applied my comment to your post in summary, rather than the specific context of your post to which it was addressing. Feel free to direct message me if you feel the need to continue.
byte
Then I don't get what you want. You state that it works if you use it non-statically, but not when you use it statically. I said that the static field is evaluated *sometime* before it is used. For more detail look at http://www.yoda.arachsys.com/csharp/beforefieldinit.html Actually, more correctly I should strike the "before it is used" part. I'll update the post.
Daniel Rose