protected virtual int Update(DataRow[] dataRows, DataTableMapping tableMapping)
{
long logScopeId = DataCommonEventSource.Log.EnterScope("<comm.DbDataAdapter.Update|API> {0}, dataRows[], tableMapping", ObjectID);
try
{
Debug.Assert((null != dataRows) && (0 < dataRows.Length), "Update: bad dataRows");
Debug.Assert(null != tableMapping, "Update: bad DataTableMapping");
// If records were affected, increment row count by one - that is number of rows affected in dataset.
int cumulativeDataRowsAffected = 0;
IDbConnection[] connections = new IDbConnection[5]; // one for each statementtype
ConnectionState[] connectionStates = new ConnectionState[5]; // closed by default (== 0)
bool useSelectConnectionState = false;
IDbCommand tmpcmd = _IDbDataAdapter.SelectCommand;
if (null != tmpcmd)
{
connections[0] = tmpcmd.Connection;
if (null != connections[0])
{
connectionStates[0] = connections[0].State;
useSelectConnectionState = true;
}
}
int maxBatchCommands = Math.Min(UpdateBatchSize, dataRows.Length);
if (maxBatchCommands < 1)
{ // batch size of zero indicates one batch, no matter how large...
maxBatchCommands = dataRows.Length;
}
BatchCommandInfo[] batchCommands = new BatchCommandInfo[maxBatchCommands];
DataRow[] rowBatch = new DataRow[maxBatchCommands];
int commandCount = 0;
// the outer try/finally is for closing any connections we may have opened
try
{
try
{
if (1 != maxBatchCommands)
{
InitializeBatching();
}
StatementType statementType = StatementType.Select;
IDbCommand dataCommand = null;
// for each row which is either insert, update, or delete
foreach (DataRow dataRow in dataRows)
{
if (null == dataRow)
{
continue; // foreach DataRow
}
bool isCommandFromRowUpdating = false;
// obtain the appropriate command
switch (dataRow.RowState)
{
case DataRowState.Detached:
case DataRowState.Unchanged:
continue; // foreach DataRow
case DataRowState.Added:
statementType = StatementType.Insert;
dataCommand = _IDbDataAdapter.InsertCommand;
break;
case DataRowState.Deleted:
statementType = StatementType.Delete;
dataCommand = _IDbDataAdapter.DeleteCommand;
break;
case DataRowState.Modified:
statementType = StatementType.Update;
dataCommand = _IDbDataAdapter.UpdateCommand;
break;
default:
Debug.Assert(false, "InvalidDataRowState");
throw ADP.InvalidDataRowState(dataRow.RowState); // out of Update without completing batch
}
// setup the event to be raised
RowUpdatingEventArgs rowUpdatingEvent = CreateRowUpdatingEvent(dataRow, dataCommand, statementType, tableMapping);
// this try/catch for any exceptions during the parameter initialization
try
{
dataRow.RowError = null;
if (null != dataCommand)
{
// prepare the parameters for the user who then can modify them during OnRowUpdating
ParameterInput(dataCommand.Parameters, statementType, dataRow, tableMapping);
}
}
catch (Exception e) when (ADP.IsCatchableExceptionType(e))
{
ADP.TraceExceptionForCapture(e);
rowUpdatingEvent.Errors = e;
rowUpdatingEvent.Status = UpdateStatus.ErrorsOccurred;
}
OnRowUpdating(rowUpdatingEvent); // user may throw out of Update without completing batch
IDbCommand tmpCommand = rowUpdatingEvent.Command;
isCommandFromRowUpdating = (dataCommand != tmpCommand);
dataCommand = tmpCommand;
tmpCommand = null;
// handle the status from RowUpdating event
UpdateStatus rowUpdatingStatus = rowUpdatingEvent.Status;
if (UpdateStatus.Continue != rowUpdatingStatus)
{
if (UpdateStatus.ErrorsOccurred == rowUpdatingStatus)
{
UpdatingRowStatusErrors(rowUpdatingEvent, dataRow);
continue; // foreach DataRow
}
else if (UpdateStatus.SkipCurrentRow == rowUpdatingStatus)
{
if (DataRowState.Unchanged == dataRow.RowState)
{
cumulativeDataRowsAffected++;
}
continue; // foreach DataRow
}
else if (UpdateStatus.SkipAllRemainingRows == rowUpdatingStatus)
{
if (DataRowState.Unchanged == dataRow.RowState)
{
cumulativeDataRowsAffected++;
}
break; // execute existing batch and return
}
else
{
throw ADP.InvalidUpdateStatus(rowUpdatingStatus); // out of Update
}
}
// else onward to Append/ExecuteNonQuery/ExecuteReader
rowUpdatingEvent = null;
RowUpdatedEventArgs rowUpdatedEvent = null;
if (1 == maxBatchCommands)
{
if (null != dataCommand)
{
batchCommands[0]._commandIdentifier = 0;
batchCommands[0]._parameterCount = dataCommand.Parameters.Count;
batchCommands[0]._statementType = statementType;
batchCommands[0]._updatedRowSource = dataCommand.UpdatedRowSource;
}
batchCommands[0]._row = dataRow;
rowBatch[0] = dataRow; // not doing a batch update, just simplifying code...
commandCount = 1;
}
else
{
Exception errors = null;
try
{
if (null != dataCommand)
{
if (0 == (UpdateRowSource.FirstReturnedRecord & dataCommand.UpdatedRowSource))
{
// append the command to the commandset. If an exception
// occurs, then the user must append and continue
batchCommands[commandCount]._commandIdentifier = AddToBatch(dataCommand);
batchCommands[commandCount]._parameterCount = dataCommand.Parameters.Count;
batchCommands[commandCount]._row = dataRow;
batchCommands[commandCount]._statementType = statementType;
batchCommands[commandCount]._updatedRowSource = dataCommand.UpdatedRowSource;
rowBatch[commandCount] = dataRow;
commandCount++;
if (commandCount < maxBatchCommands)
{
continue; // foreach DataRow
}
// else onward execute the batch
}
else
{
// do not allow the expectation that returned results will be used
errors = ADP.ResultsNotAllowedDuringBatch();
}
}
else
{
// null Command will force RowUpdatedEvent with ErrorsOccured without completing batch
errors = ADP.UpdateRequiresCommand(statementType, isCommandFromRowUpdating);
}
}
catch (Exception e) when (ADP.IsCatchableExceptionType(e))
{
// try/catch for RowUpdatedEventArgs
ADP.TraceExceptionForCapture(e);
errors = e;
}
if (null != errors)
{
rowUpdatedEvent = CreateRowUpdatedEvent(dataRow, dataCommand, StatementType.Batch, tableMapping);
rowUpdatedEvent.Errors = errors;
rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
OnRowUpdated(rowUpdatedEvent); // user may throw out of Update
if (errors != rowUpdatedEvent.Errors)
{ // user set the error msg and we will use it
for (int i = 0; i < batchCommands.Length; ++i)
{
batchCommands[i]._errors = null;
}
}
cumulativeDataRowsAffected += UpdatedRowStatus(rowUpdatedEvent, batchCommands, commandCount);
if (UpdateStatus.SkipAllRemainingRows == rowUpdatedEvent.Status)
{
break;
}
continue; // foreach datarow
}
}
rowUpdatedEvent = CreateRowUpdatedEvent(dataRow, dataCommand, statementType, tableMapping);
// this try/catch for any exceptions during the execution, population, output parameters
try
{
if (1 != maxBatchCommands)
{
IDbConnection connection = DbDataAdapter.GetConnection1(this);
ConnectionState state = UpdateConnectionOpen(connection, StatementType.Batch, connections, connectionStates, useSelectConnectionState);
rowUpdatedEvent.AdapterInit(rowBatch);
if (ConnectionState.Open == state)
{
UpdateBatchExecute(batchCommands, commandCount, rowUpdatedEvent);
}
else
{
// null Connection will force RowUpdatedEvent with ErrorsOccured without completing batch
rowUpdatedEvent.Errors = ADP.UpdateOpenConnectionRequired(StatementType.Batch, false, state);
rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
}
}
else if (null != dataCommand)
{
IDbConnection connection = DbDataAdapter.GetConnection4(this, dataCommand, statementType, isCommandFromRowUpdating);
ConnectionState state = UpdateConnectionOpen(connection, statementType, connections, connectionStates, useSelectConnectionState);
if (ConnectionState.Open == state)
{
UpdateRowExecute(rowUpdatedEvent, dataCommand, statementType);
batchCommands[0]._recordsAffected = rowUpdatedEvent.RecordsAffected;
batchCommands[0]._errors = null;
}
else
{
// null Connection will force RowUpdatedEvent with ErrorsOccured without completing batch
rowUpdatedEvent.Errors = ADP.UpdateOpenConnectionRequired(statementType, isCommandFromRowUpdating, state);
rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
}
}
else
{
// null Command will force RowUpdatedEvent with ErrorsOccured without completing batch
rowUpdatedEvent.Errors = ADP.UpdateRequiresCommand(statementType, isCommandFromRowUpdating);
rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
}
}
catch (Exception e) when (ADP.IsCatchableExceptionType(e))
{
// try/catch for RowUpdatedEventArgs
ADP.TraceExceptionForCapture(e);
rowUpdatedEvent.Errors = e;
rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
}
bool clearBatchOnSkipAll = (UpdateStatus.ErrorsOccurred == rowUpdatedEvent.Status);
{
Exception errors = rowUpdatedEvent.Errors;
OnRowUpdated(rowUpdatedEvent); // user may throw out of Update
// NOTE: the contents of rowBatch are now tainted...
if (errors != rowUpdatedEvent.Errors)
{ // user set the error msg and we will use it
for (int i = 0; i < batchCommands.Length; ++i)
{
batchCommands[i]._errors = null;
}
}
}
cumulativeDataRowsAffected += UpdatedRowStatus(rowUpdatedEvent, batchCommands, commandCount);
if (UpdateStatus.SkipAllRemainingRows == rowUpdatedEvent.Status)
{
if (clearBatchOnSkipAll && 1 != maxBatchCommands)
{
ClearBatch();
commandCount = 0;
}
break; // from update
}
if (1 != maxBatchCommands)
{
ClearBatch();
commandCount = 0;
}
for (int i = 0; i < batchCommands.Length; ++i)
{
batchCommands[i] = default(BatchCommandInfo);
}
commandCount = 0;
} // foreach DataRow
// must handle the last batch
if (1 != maxBatchCommands && 0 < commandCount)
{
RowUpdatedEventArgs rowUpdatedEvent = CreateRowUpdatedEvent(null, dataCommand, statementType, tableMapping);
try
{
IDbConnection connection = DbDataAdapter.GetConnection1(this);
ConnectionState state = UpdateConnectionOpen(connection, StatementType.Batch, connections, connectionStates, useSelectConnectionState);
DataRow[] finalRowBatch = rowBatch;
if (commandCount < rowBatch.Length)
{
finalRowBatch = new DataRow[commandCount];
Array.Copy(rowBatch, 0, finalRowBatch, 0, commandCount);
}
rowUpdatedEvent.AdapterInit(finalRowBatch);
if (ConnectionState.Open == state)
{
UpdateBatchExecute(batchCommands, commandCount, rowUpdatedEvent);
}
else
{
// null Connection will force RowUpdatedEvent with ErrorsOccured without completing batch
rowUpdatedEvent.Errors = ADP.UpdateOpenConnectionRequired(StatementType.Batch, false, state);
rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
}
}
catch (Exception e) when (ADP.IsCatchableExceptionType(e))
{
// try/catch for RowUpdatedEventArgs
ADP.TraceExceptionForCapture(e);
rowUpdatedEvent.Errors = e;
rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
}
Exception errors = rowUpdatedEvent.Errors;
OnRowUpdated(rowUpdatedEvent); // user may throw out of Update
// NOTE: the contents of rowBatch are now tainted...
if (errors != rowUpdatedEvent.Errors)
{ // user set the error msg and we will use it
for (int i = 0; i < batchCommands.Length; ++i)
{
batchCommands[i]._errors = null;
}
}
cumulativeDataRowsAffected += UpdatedRowStatus(rowUpdatedEvent, batchCommands, commandCount);
}
}
finally
{
if (1 != maxBatchCommands)
{
TerminateBatching();
}
}
}
finally
{ // try/finally for connection cleanup
for (int i = 0; i < connections.Length; ++i)
{
QuietClose(connections[i], connectionStates[i]);
}
}
return cumulativeDataRowsAffected;
}
finally
{
DataCommonEventSource.Log.ExitScope(logScopeId);
}
}