views:

160

answers:

5

Hi,

I am designing a database wrapper for C#. Below are the two options I have:

Option A:

class DBWrapper:IDisposable
{
     private SqlConnection sqlConn;

     public DBWrapper()
     {
            sqlConn = new SqlConnection("my connection string");
            sqlConn.Open();
     }

     public DataTable RunQuery(string Sql)
     {
              implementation......
     }

     public Dispose()
     {
            if(sqlConn != null)
                   sqlConn.Close();
     }
}

Option B:

class DBWrapper
{
     public DBWrapper()
     {            
     }

     public DataTable RunQuery(string Sql)
     {
             SqlConnection sqlConn = new SqlConnection("my connection string");
             .....implementation......
             sqlConn.Close();               
     }   
}

For option A connection is opened when class is instantiated. So no matter how many times the caller calls RunQuery the connection is always ready. But If the application instantiates DBWrapper early in the application, the connection will be just opened and doing nothing until the application is finished. Also, it could have many DBWrapper instantiated during the execution. So, it's kinda wasting resources.

For option B it doesn't have the problem option A has, but the a new connection has to be opened and closed everytime the caller calls RunQuery. I am not sure how much it will hurt the performance.

Please share your expertise. Thank you for reading.

+2  A: 

A few comments worth considering:

In the approach where you manage a (perhaps) long-lived connection, it is important to check whether the connection is open before running a query. I've run into issues before where NETCF closed unused connections after a while.

In the approach where you open a new connection per-query, ensure that your connection, commands, and (if used) data readers are all properly wrapped in using statements or try/finally+dispose() blocks to free up connections and locks.

Happy coding!

kbrimington
Thank you for your remind.For Option A, if I make sure that my class doesn't expose any function to close the connection and there is always one constructor to open the connection, it should be OK, right?Ya, I using "using" statement.
Dreteh
@dreteh: I would recommend providing a private/protected property to check the connection prior to use. Something like `protected SqlConnection Connection { get { if (sqlConn.State == ConnectionState.Closed) sqlConn.Open(); return sqlConn; } }`. As a disclaimer, I've never encountered issues with the framework closing my connection outside of the compact framework. Perhaps this suggestion is overkill for a full-framework application. I would be curious to see if the connection automatically closes on a laptop that goes into standby or hibernation.
kbrimington
+2  A: 

For performance reasons, you'll definitely not want to go with Option B (at least in the cases I experienced.) Let me suggest Option C:

class DBWrapper:IDisposable { private SqlConnection sqlConn;

public void EnsureConnectionIsOpen()
{
    if (sqlConn == null)
    {
      sqlConn = new SqlConnection("my connection string");
      sqlConn.Open();
    }

}

public DataTable RunQuery(string Sql)
{
    EnsureConnectionIsOpen();
    implementation......
}

public Dispose()
{
   if(sqlConn != null)
              sqlConn.Close();
}

} 

You might consider using the singleton pattern to make sure there is only one instance of your DBWrapper.

Philipp
Mind that when using the singleton pattern you can have some issues regarding multithreading (if implemented the pattern incorrect). Although singletons are often good solutions for this type of code a person should always be alert and check for multithreading issues when using statics in an application.
Gertjan
Singleton is only one way to share objects between threads, but obviously you always have to carefully design your multithreading as soon as you share objects between threads, no matter how they are shared.
Philipp
If you share a database connection between thread (and in a web scenario also between users) you might get "funny" situations. It will also disable you in having multiple connections. Having multiple connections is sometimes required when gathering data in parent->child relations, in that case you ofter open a second connection to get the children while the first connection is used to gather the parents.
Gertjan
Let me ask a dump question:For web application, when two people are requesting a page at the same time is not consider multiple threads right?
Dreteh
of course it is, see http://stackoverflow.com/questions/657735/how-is-asp-net-multithreaded for more details.
Philipp
Then I think database wrapper should never be singleton.
Dreteh
you're right. SqlConnection is not threadsafe.
Philipp
If you are working within a web application, you can provide a per-request-singleton. Instead of using a static variable for storing the connection, use the `Items` collection on the current `HttpContext`. When using this approach, I usually add `Dispose()` calls in the end request event of the Global.asax. It is important that each thread have its own connection.
kbrimington
It might also be worth mentioning that some dependency injection containers, such as Castle Windsor, provide an API for per-request-scoped objects, which can be very useful in a Web scenario.
kbrimington
+1  A: 

Option B is more transactional, which has its advantages. ADO.NET uses implicit connection pooling, so you do not have to worry about creating new instances of SqlConnection frequently.

You should consider whether you are using a connected or disconnected data model; as the second approach lends itself better to a disconnected model.

But as i've said above, connection pooling means that it makes virtually no difference in practical terms.

Bradley Smith
Your answer is very useful. I am considering option B because I want to overload the RunQeury function that takes an additional connection object so that I can group couple of data accessors to implement transaction(this way they can share the same connection). The only thing I really concern is keep opening and closing a connection might not be efficient.
Dreteh
A: 

You could have an option C where the database is opened on request in RunQuery (if it is not open) and closed on disposed (when it was opened). That way the database is only opened when really needed and will be opened only once.

So in pseudo code:

class DBWrapper
{

     public DBWrapper()
     {            
     }

     SqlConnection sqlConn = null; 

     public DataTable RunQuery(string Sql)
     {
             if(sqlConn == null) sqlConn = new SqlConnection("my connection string");
             .....implementation......               
     }   
     public Dispose()
     {
            if(sqlConn != null)
                   sqlConn.Close();
     }

}

Also mind that the moment Dispose is called is not always directly after the object is not needed anymore (for example a function variable after the function is used). As far as I know it will be executed when the garbage collector collects the object (which is not directly). But I am not totally sure about this. This behaviour might also differ between web and non web application.

Gertjan
But I can see this design will have the issue of Option A as well because imagine if RunQuery is called at the very beginning of the execution and never be used anymore until the end.
Dreteh
That is true but the connection will be opened when first needed while example A opens on constructing the object. If you want to tighten the lifetime of your database you should manually close it since the system cannot guess if and when you might need the database in the future.
Gertjan
+1  A: 

Garbage collector is triggered under rather complex conditions but basically it is invoked when memory exceeds some limit, it is invoked periodically as well but the period is not constant. You never can be sure when exactly garbage collector disposes and consequently (in another run) destroys the object. One thing you can be sure is the fact that garbage collector will never dispose and destroy the object that still has references. For example object that is referenced via static variables on the class neither will be disposed nor destroyed.

Petr Kozelek