views:

54

answers:

4

Hello. I have a class named Entry declared like this:

class Entry{
    string Id {get;set;}
    string Name {get;set;}
}  

and then a method that will accept multiple such Entry objects for insertion into the database using ADO.NET:

static void InsertEntries(IEnumerable<Entry> entries){
    //build a SqlCommand object
    using(SqlCommand cmd = new SqlCommand()){
        ...
        const string refcmdText = "INSERT INTO Entries (id, name) VALUES (@id{0},@name{0});";
        int count = 0;
        string query = string.Empty;
        //build a large query
        foreach(var entry in entries){
            query += string.Format(refcmdText, count);
            cmd.Parameters.AddWithValue(string.Format("@id{0}",count), entry.Id);
            cmd.Parameters.AddWithValue(string.Format("@name{0}",count), entry.Name);
            count++;
        }
        cmd.CommandText=query;
        //and then execute the command
        ...
    }
}  

And my question is this: should I keep using the above way of sending multiple insert statements (build a giant string of insert statements and their parameters and send it over the network), or should I keep an open connection and send a single insert statement for each Entry like this:

using(SqlCommand cmd = new SqlCommand(){
    using(SqlConnection conn = new SqlConnection(){
        //assign connection string and open connection
        ...
        cmd.Connection = conn;
        foreach(var entry in entries){
            cmd.CommandText= "INSERT INTO Entries (id, name) VALUES (@id,@name);";
            cmd.Parameters.AddWithValue("@id", entry.Id);
            cmd.Parameters.AddWithValue("@name", entry.Name);
            cmd.ExecuteNonQuery();
        }
    }
 }  

What do you think? Will there be a performance difference in the Sql Server between the two? Are there any other consequences I should be aware of? Thank you for your time!

+2  A: 

You should execute the command on every loop instead of building a huge command Text(btw,StringBuilder is made for this) The underlying Connection will not close and re-open for each loop, let the connection pool manager handle this. Have a look at this link for further informations: Tuning Up ADO.NET Connection Pooling in ASP.NET Applications

If you want to ensure that every command is executed successfully you can use a Transaction and Rollback if needed,

Tim Schmelter
Or better use Linq2Sql and let Linq2Sql handle this.
Amitabh
The `SQLCommand` is optimized for the process that Giorgi describes. The underlying connection will be maintained as Tim points out. I would also use a `Transaction` recommended by Tim.
AMissico
+2  A: 

If I were you I would not use either of them.

The disadvantage of the first one is that the parameter names might collide if there are same values in the list.

The disadvantage of the second one is that you are creating command and parameters for each entity.

The best way is to have the command text and parameters constructed once (use Parameters.Add to add the parameters) change their values in the loop and execute the command. That way the statement will be prepared only once. You should also open the connection before you start the loop and close it after it.

Giorgi
The `SQLCommand` is optimized for the process that Giorgi describes. The underlying connection will be maintained as Tim points out. I would also use a `Transaction` recommended by Tim.
AMissico
A: 
    static void InsertSettings(IEnumerable<Entry> settings) {
        using (SqlConnection oConnection = new SqlConnection("Data Source=(local);Initial Catalog=Wip;Integrated Security=True")) {
            oConnection.Open();
            using (SqlTransaction oTransaction = oConnection.BeginTransaction()) {
                using (SqlCommand oCommand = oConnection.CreateCommand()) {
                    oCommand.Transaction = oTransaction;
                    oCommand.CommandType = CommandType.Text;
                    oCommand.CommandText = "INSERT INTO [Setting] ([Key], [Value]) VALUES (@key, @value);";
                    oCommand.Parameters.Add("@key", SqlDbType.NChar);
                    oCommand.Parameters.Add("@value", SqlDbType.NChar);
                    try {
                        foreach (var oSetting in settings) {
                            oCommand.Parameters[0].Value = oSetting.Key;
                            oCommand.Parameters[1].Value = oSetting.Value;
                            if (oCommand.ExecuteNonQuery() != 1) {
                                //'handled as needed, 
                                //' but this snippet will throw an exception to force a rollback
                                throw new InvalidProgramException();
                            }
                        }
                        oTransaction.Commit();
                    } catch (Exception) {
                        oTransaction.Rollback();
                        throw;
                    }
                }
            }
        }
    }
AMissico
A: 

when it are a lot of entries consider to use SqlBulkCopy

Tim Mahy