#At file:///C:/work/bzr-connector-net/6.1/ based on revid:reggie.burnett@stripped
827 Reggie Burnett 2010-07-19 [merge]
merged
modified:
CHANGES
MySql.Data/Provider/Source/dataadapter.cs
MySql.Data/Tests/Source/DataAdapterTests.cs
=== modified file 'CHANGES'
=== modified file 'CHANGES'
--- a/CHANGES 2010-07-19 18:25:42 +0000
+++ b/CHANGES 2010-07-19 18:29:11 +0000
@@ -1,3 +1,5 @@
+- Fix "Connection must be valid and open" exception When UpdateBatchSize > 1
+ and MySqlDataAdapter is not using an open connection (Bug #38411)
- Make sure MySqlDataAdapter.Update() works for custom stored procedure driven update commands
that make use of UpdateRowSource.FirstReturnedRecord (Bug#54895)
- Ensure exceptions are not silently eaten inside MySqlDataReader.Read() (Bug#53439)
=== modified file 'MySql.Data/Provider/Source/dataadapter.cs'
--- a/MySql.Data/Provider/Source/dataadapter.cs 2010-07-01 22:48:45 +0000
+++ b/MySql.Data/Provider/Source/dataadapter.cs 2010-07-16 14:54:00 +0000
@@ -125,18 +125,74 @@
#endregion
+ /// <summary>
+ /// Open connection if it was closed.
+ /// Necessary to workaround "connection must be open and valid" error
+ /// with batched updates.
+ /// </summary>
+ /// <param name="state">Row state</param>
+ /// <param name="openedConnections"> list of opened connections
+ /// If connection is opened by this function, the list is updated
+ /// </param>
+ /// <returns>true if connection was opened</returns>
+ private void OpenConnectionIfClosed(DataRowState state,
+ List<MySqlConnection> openedConnections)
+ {
+ MySqlCommand cmd = null;
+ switch (state)
+ {
+ case DataRowState.Added:
+ cmd = InsertCommand;
+ break;
+ case DataRowState.Deleted:
+ cmd = DeleteCommand;
+ break;
+ case DataRowState.Modified:
+ cmd = UpdateCommand;
+ break;
+ default:
+ return;
+ }
+
+ if (cmd != null && cmd.Connection != null &&
+ cmd.Connection.connectionState == ConnectionState.Closed)
+ {
+ cmd.Connection.Open();
+ openedConnections.Add(cmd.Connection);
+ }
+ }
+
+
protected override int Update(DataRow[] dataRows, DataTableMapping tableMapping)
{
- int ret = base.Update(dataRows, tableMapping);
- // DbDataAdapter does automatically calls AcceptChanges on table.
- // (even if documentation states otherwise).
- // Do AcceptsChanges() here, for SQL Server compatible behavior
- // (see also Bug#5463)
- foreach (DataRow row in dataRows)
- {
- row.Table.AcceptChanges();
- }
- return ret;
+ List<MySqlConnection> connectionsOpened = new List<MySqlConnection>();
+
+ try
+ {
+ foreach(DataRow row in dataRows)
+ {
+ OpenConnectionIfClosed(row.RowState, connectionsOpened);
+ }
+
+ int ret = base.Update(dataRows, tableMapping);
+
+ // DbDataAdapter does automatically calls AcceptChanges on table.
+ // (even if documentation states otherwise).
+ // Do AcceptsChanges() here, for SQL Server compatible behavior
+ // (see also Bug#5463)
+ foreach (DataRow row in dataRows)
+ {
+ row.Table.AcceptChanges();
+ }
+ return ret;
+
+ }
+ finally
+ {
+ foreach(MySqlConnection c in connectionsOpened)
+ c.Close();
+ }
+
}
#region Batching Support
=== modified file 'MySql.Data/Tests/Source/DataAdapterTests.cs'
--- a/MySql.Data/Tests/Source/DataAdapterTests.cs 2010-07-08 20:21:58 +0000
+++ b/MySql.Data/Tests/Source/DataAdapterTests.cs 2010-07-12 14:38:02 +0000
@@ -898,6 +898,75 @@
}
/// <summary>
+ /// Bug #38411, using closed connection with data adapter.
+ /// </summary>
+ [Test]
+ public void BatchingConnectionClosed()
+ {
+ execSQL("CREATE TABLE Test (id INT, name VARCHAR(20), PRIMARY KEY(id))");
+
+ MySqlConnection c = new MySqlConnection(GetConnectionString(true));
+ MySqlConnection c2 = new MySqlConnection(GetConnectionString(true));
+ MySqlConnection c3 = new MySqlConnection(GetConnectionString(true));
+
+ MySqlDataAdapter da = new MySqlDataAdapter("SELECT * FROM Test", c);
+ MySqlCommand ins = new MySqlCommand("INSERT INTO test (id, name) VALUES (?p1, ?p2)", c);
+ da.InsertCommand = ins;
+ ins.UpdatedRowSource = UpdateRowSource.None;
+ ins.Parameters.Add("?p1", MySqlDbType.Int32).SourceColumn = "id";
+ ins.Parameters.Add("?p2", MySqlDbType.VarChar, 20).SourceColumn = "name";
+
+ MySqlCommand del = new MySqlCommand("delete from test where id=?p1", c2);
+ da.DeleteCommand = del;
+ del.UpdatedRowSource = UpdateRowSource.None;
+ del.Parameters.Add("?p1", MySqlDbType.Int32).SourceColumn = "id";
+
+
+ MySqlCommand upd = new MySqlCommand("update test set id=?p1, name=?p2 where id=?p1", c3);
+ da.UpdateCommand = upd;
+ upd.UpdatedRowSource = UpdateRowSource.None;
+ upd.Parameters.Add("?p1", MySqlDbType.Int32).SourceColumn = "id";
+ upd.Parameters.Add("?p2", MySqlDbType.VarChar, 20).SourceColumn = "name";
+
+ DataTable dt = new DataTable();
+ da.Fill(dt);
+
+ for (int i = 1; i <= 100; i++)
+ {
+ DataRow row = dt.NewRow();
+ row["id"] = i;
+ row["name"] = "name " + i;
+ dt.Rows.Add(row);
+ }
+
+ da.UpdateBatchSize = 10;
+ da.Update(dt);
+
+ dt.Rows.Clear();
+ da.Fill(dt);
+ Assert.AreEqual(100, dt.Rows.Count);
+ for (int i = 0; i < 100; i++)
+ {
+ Assert.AreEqual(i + 1, dt.Rows[i]["id"]);
+ Assert.AreEqual("name " + (i + 1), dt.Rows[i]["name"]);
+ }
+
+ foreach (DataRow row in dt.Rows)
+ {
+ row["name"] = row["name"] + "_xxx";
+ }
+ da.Update(dt);
+ for (int i = 0; i < 100; i++)
+ {
+ dt.Rows[i].Delete();
+ }
+ da.Update(dt);
+ dt.Rows.Clear();
+ da.Fill(dt);
+ Assert.AreEqual(dt.Rows.Count, 0);
+
+ }
+ /// <summary>
/// Bug#54895
/// ConcurrencyException when trying to use UpdateRowSource.FirstReturnedRecord
/// with UpdateCommand and stored procedure.
Attachment: [text/bzr-bundle] bzr/reggie.burnett@oracle.com-20100719182911-4a4v5m9q0e5ji8q1.bundle
| Thread |
|---|
| • bzr commit into connector-net-6.1 branch (reggie.burnett:827) | Reggie Burnett | 19 Jul |