tags:

views:

190

answers:

7

I was reading an article in MSDN several months ago and have recently started using the following snippet to execute ADO.NET code, but I get the feeling it could be bad. Am I over reacting or is it perfectly acceptable?

private void Execute(Action<SqlConnection> action)
{
    SqlConnection conn = null;
    try {
        conn = new SqlConnection(ConnectionString);
        conn.Open();
        action.Invoke(conn);
    } finally {
        if (conn != null && conn.State == ConnectionState.Open) {
            try {
                conn.Close();
            } catch {
            }
        }
    }
}

public bool GetSomethingById() {
    SomeThing aSomething = null
    bool valid = false;
    Execute(conn =>
    {
        using (SqlCommand cmd = conn.CreateCommand()) {
            cmd.CommandText = ....
            ...
            SqlDataReader reader = cmd.ExecuteReader();
            ...
            aSomething = new SomeThing(Convert.ToString(reader["aDbField"]));
        }
    });
    return aSomething;
}
A: 

That's acceptable. I've created a SqlUtilities class two years ago that had a similar method. You can take it one step further if you like.

EDIT: Couldn't find the code, but I typed a small example (probably with many syntax errors ;))

SQLUtilities

public delegate T CreateMethod<T> (SqlDataReader reader);
public static T CreateEntity<T>(string query, CreateMethod<T> createMethod, params SqlParameter[] parameters) {
   // Open the Sql connection
   // Create a Sql command with the query/sp and parameters
   SqlDataReader reader = cmd.ExecuteReader();
   return createMethod(reader);
   // Probably some finally statements or using-closures etc. etc.
} 

Calling code

private SomeThing Create(SqlDataReader reader) {
    SomeThing something = new SomeThing();
    something.ID = Convert.ToIn32(reader["ID"]);
    ...
    return something;
}

public SomeThing GetSomeThingByID (int id) {
    return SqlUtilities.CreateEntity<SomeThing> ("something_getbyid", Create, ....);
}

Of course you could use a lambda expression instead of the Create method, and you could easily make a CreateCollection method and reuse the existing Create method.

However if this is a new project. Check out LINQ to entities. Is far easier and flexible than ADO.Net.

Zyphrax
+1  A: 

Not necessarily if that's all you need to do. However it doesn't scale well, so that's why we have frameworks such NHibernate, EF, etc.

Otávio Décio
Could you justify why this approach wouldn't scale well compared to ORMs?
Darin Dimitrov
Sure - I can't imagine myself writing this code over and over again every time I want to talk to a database.
Otávio Décio
Scaling and code repetition (code smell, call it whatever you like) are not at all related or put it that way: writing less code (because you are relying on some other framework) doesn't mean that it would scale better.
Darin Dimitrov
I was not referring to performance, if that's what you thought - just scaling in code size.
Otávio Décio
Sorry, I thought you were referring to scalability - the ability to handle growing amounts of work in a graceful manner.
Darin Dimitrov
@Darin - sorry for the confusion, I should've used a less loaded word.
Otávio Décio
ORMs are out of the question. I've been given the choice between Spinvoke, a discontinued stored procedure C# wrapper or coming up with a "non awful" method. The reason I suggest using this is due to the lack of rewritten code and that the connection is closed regardless of whether an exception occured.
Echilon
A: 

i like this pattern! it is something what c# got from functional languages. the only thing is that you might not need to create connection in every action, but it can be changed. please explain why is it bad?

Andrey
A: 

This is a very reasonable approach to use.

By wrapping your connection logic into a method which takes an Action<SqlConnection>, you're helping prevent duplicated code and the potential for introduced error. Since we can now use lambdas, this becomes an easy, safe way to handle this situation.

Reed Copsey
Thanks for your views.
Echilon
+1  A: 

IMHO it is indeed a bad practice, since you're creating and opening a new database-connection for every statement that you execute.

Why is it bad:

  • performance wise (although connection pooling helps decrease the performance hit): you should open your connection, execute the statements that have to be executed, and close the connection when you don't know when the next statement will be executed.

  • but certainly context-wise. I mean: how will you handle transactions ? Where are your transaction boundaries ? Your application-layer knows when a transaction has to be started and committed, but you're unable to span multiple statements into the same sql-transaction with this way of working.

Frederik Gheysels
I was going to say something to this effect. While it looks like it is possible to execute multiple statements inside a single action I can see it being used to generate results for very specific actions resulting in a lot of unnecessary disconnect/connect behaviors.
Spencer Ruport
He is not opening and closing a database connection for every statement. ADO.NET uses a connection pool. Calling `connection.Close` **doesn't** close the underlying database connection but it returns it to the connection pool for reuse (http://msdn.microsoft.com/en-us/library/8xx3tyca%28VS.80%29.aspx).
Darin Dimitrov
@Darin: that's what I'm saying. But, I semantically, it is still wrong.
Frederik Gheysels
You write: `you're creating and opening a new database-connection for every statement that you execute`. This statement is wrong. Please read the Remarks section of the documentation: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.open.aspx
Darin Dimitrov
+6  A: 

What is the point of doing that when you can do this?

public SomeThing GetSomethingById(int id) 
{
    using (var con = new SqlConnection(ConnectionString)) 
    {
        con.Open();
        using (var cmd = con.CreateCommand()) 
        {
            // prepare command
            using (var rdr = cmd.ExecuteReader()) 
            {
                // read fields
                return new SomeThing(data);
            }
        } 
    }
}

You can promote code reuse by doing something like this.

public static void ExecuteToReader(string connectionString, string commandText, IEnumerable<KeyValuePair<string, object>> parameters, Action<IDataReader> action) 
{
    using (var con = new SqlConnection(connectionString)) 
    {
        con.Open();
        using (var cmd = con.CreateCommand()) 
        {
            cmd.CommandText = commandText;
            foreach (var pair in parameters) 
            {
                var parameter = cmd.CreateParameter();
                parameter.ParameterName = pair.Key; 
                parameter.Value = pair.Value; 
                cmd.Parameters.Add(parameter);
            }
            using (var rdr = cmd.ExecuteReader()) 
            {
                action(rdr);
            }
        } 
    }    
}

You could use it like this:

//At the top create an alias
using DbParams = Dictionary<string, object>;

ExecuteToReader(
    connectionString, 
    commandText, 
    new DbParams() { { "key1", 1 }, { "key2", 2 } }),
    reader => 
    {
        // ...
        // No need to dispose
    }
)
ChaosPandion
+1, totally agreed
Darin Dimitrov
What happens if an exception occurs? Would I be wrong in thinking the connection will be left hanging and not closed?
Echilon
@Echilon - Nope, the using statement will for your purposes close the connection. Technically it will remain open because of connection pooling.
ChaosPandion
@Chaos - I wouldn't use a Dictionary for this, use the DBParameter class instead. Adding - params DBParameter[] parameters - to the end of the method definition is a nice approach.
Zyphrax
@Zyphrax - but then you have to pass the params like this: `new SqlParameter() { ParameterName = "key1", Value = 1 }` which I feel is really verbose.
ChaosPandion
A: 

Well, In my opinion check what you do before going through it.Something that is working doesn't mean it is best and good programming practice.Check out and find a concrete example and benefit of using it.But if you are considering using for big projects it would be nice using frameworks like NHibernate.Because there are a lot projects even frameworks developed based on it,like http://www.cuyahoga-project.org/.

Wonde

related questions