private void ExecuteBatchMultipleCommands(UpdateSet updateSet) { //Note: for multiple commands, we cannot include trans statements into batch commands, like add 'Begin Trans' to the first command // and 'Commit' to the last command - this will fail. We start/commit trans using separate calls // Also, we have to manage connection explicitly, to start/commit transaction var conn = updateSet.Connection; try { var inNewTrans = conn.DbTransaction == null; if (inNewTrans) { conn.BeginTransaction(commitOnSave: true); } foreach (var batchCmd in updateSet.BatchCommands) { ExecuteDbCommand(batchCmd.DbCommand, conn, DbExecutionType.NonQuery); if (batchCmd.HasActions) { foreach (var action in batchCmd.GetPostActions()) { action(); } } }//foreach if (inNewTrans) { conn.Commit(); } ReleaseConnection(conn); } catch { ReleaseConnection(conn, inError: true); throw; } }
// Note: scheduled commands are already in batch commands private void SaveChangesInBatchMode(UpdateSet updateSet) { var session = updateSet.Session; var postExecActions = new List <Action>(); var batchBuilder = new DbBatchCommandSetBuilder(this, updateSet); batchBuilder.Build(); LogComment(session, "-- BEGIN BATCH ({0} rows, {1} batch command(s)) ---------------------------", updateSet.AllRecords.Count, updateSet.BatchCommands.Count); if (updateSet.BatchCommands.Count == 1) { ExecuteBatchSingleCommand(updateSet); } else { ExecuteBatchMultipleCommands(updateSet); } LogComment(session, "-- END BATCH --------------------------------------\r\n"); //execute post-execute actions; it is usually handling output parameter values // Finalize records after update foreach (var rec in updateSet.AllRecords) { rec.CustomTag = null; //clear temp ref that batch process has set rec.SubmitCount++; rec.EntityInfo.SaveEvents.OnSubmittedChanges(rec); } }
private void ExecuteBatchSingleCommand(UpdateSet updateSet) { var conn = updateSet.Connection; try { var batchCmd = updateSet.BatchCommands[0]; var dbCmd = batchCmd.DbCommand; if (conn.DbTransaction == null) //surround it with beginTrans/commit { dbCmd.CommandText = string.Format("{0}\r\n{1}\r\n{2}", _driver.BatchBeginTransaction, dbCmd.CommandText, _driver.BatchCommitTransaction); } ExecuteDbCommand(dbCmd, conn, DbExecutionType.NonQuery); if (batchCmd.HasActions) { foreach (var action in batchCmd.GetPostActions()) { action(); } } ReleaseConnection(conn); } catch { ReleaseConnection(conn, inError: true); throw; } }
public void SaveChanges(EntitySession session) { if (session.HasChanges()) { var records = session.RecordsChanged.Where(rec => ShouldUpdate(rec)).ToList(); var conn = GetConnection(session); var updateSet = new UpdateSet(conn, _timeService.UtcNow, records); var batchMode = ShouldUseBatchMode(updateSet); if (batchMode) { SaveChangesInBatchMode(updateSet); } else { SaveChangesNoBatch(session, updateSet); } } //commit if we have session connection with transaction and CommitOnSave var sConn = session.CurrentConnection; if (sConn != null) { if (sConn.DbTransaction != null && sConn.Flags.IsSet(ConnectionFlags.CommitOnSave)) { sConn.Commit(); } if (sConn.Lifetime != ConnectionLifetime.Explicit) { ReleaseConnection(sConn); } } session.ScheduledCommands.Clear(); }
public DbBatchCommandSetBuilder(Database db, UpdateSet updateSet) { _db = db; _driver = _db.DbModel.Driver; _updateSet = updateSet; // Clear identity holders in record.CustomTag if (_updateSet.UsesOutParams) foreach (var rec in updateSet.AllRecords) rec.CustomTag = null; }
public DbBatchCommandSetBuilder(Database db, UpdateSet updateSet) { _db = db; _driver = _db.DbModel.Driver; _updateSet = updateSet; // Clear identity holders in record.CustomTag if (_updateSet.UsesOutParams) { foreach (var rec in updateSet.AllRecords) { rec.CustomTag = null; } } }
private void ExecuteBatchSingleCommand(UpdateSet updateSet) { var conn = updateSet.Connection; try { var batchCmd = updateSet.BatchCommands[0]; var dbCmd = batchCmd.DbCommand; if (conn.DbTransaction == null) //surround it with beginTrans/commit dbCmd.CommandText = string.Format("{0}\r\n{1}\r\n{2}", _driver.BatchBeginTransaction, dbCmd.CommandText, _driver.BatchCommitTransaction); ExecuteDbCommand(dbCmd, conn, DbExecutionType.NonQuery); if (batchCmd.HasActions) foreach (var action in batchCmd.GetPostActions()) action(); ReleaseConnection(conn); } catch { ReleaseConnection(conn, inError: true); throw; } }
// Checks if batch is needed (record count > 1); and driver supports batch mode, and if batch is not disabled; // If there are any out params for CRUD stored procs (identity, timestamp) then driver should out param for batch mode // MS SQL supports batch with output params; SQL CE does not support batch at all - one statement at a time. // MySql and Postgres support batch, but there are some troubles in using output parameters inside the batch, // so for these drivers batch OutParamsInBatchMode is not set, so we do not use batch mode if there are any. private bool ShouldUseBatchMode(UpdateSet updateSet) { var totalCount = updateSet.AllRecords.Count + updateSet.Session.ScheduledCommands.Count; if (totalCount > 1 && Settings.ModelConfig.Options.IsSet(DbOptions.UseBatchMode)) { //Probably we should use batch mode; check if it is disabled or stored procs are disabled if (updateSet.Session.Options.IsSet(EntitySessionOptions.DisableBatch | EntitySessionOptions.DisableStoredProcs)) { return(false); } // check if there any out params if (updateSet.UsesOutParams && !_driver.Supports(DbFeatures.OutParamsInBatchedUpdates)) { return(false); } //otherwise return true return(true); } return(false); }
// Note: scheduled commands are already in batch commands private void SaveChangesInBatchMode(UpdateSet updateSet) { var session = updateSet.Session; var postExecActions = new List<Action>(); var batchBuilder = new DbBatchCommandSetBuilder(this, updateSet); batchBuilder.Build(); LogComment(session, "-- BEGIN BATCH ({0} rows, {1} batch command(s)) ---------------------------", updateSet.AllRecords.Count, updateSet.BatchCommands.Count); if (updateSet.BatchCommands.Count == 1) { ExecuteBatchSingleCommand(updateSet); } else { ExecuteBatchMultipleCommands(updateSet); } LogComment(session, "-- END BATCH --------------------------------------\r\n"); //execute post-execute actions; it is usually handling output parameter values // Finalize records after update foreach (var rec in updateSet.AllRecords) { rec.CustomTag = null; //clear temp ref that batch process has set rec.SubmitCount++; rec.EntityInfo.SaveEvents.OnSubmittedChanges(rec); } }
private void ExecuteBatchMultipleCommands(UpdateSet updateSet) { //Note: for multiple commands, we cannot include trans statements into batch commands, like add 'Begin Trans' to the first command // and 'Commit' to the last command - this will fail. We start/commit trans using separate calls // Also, we have to manage connection explicitly, to start/commit transaction var conn = updateSet.Connection; try { var inNewTrans = conn.DbTransaction == null; if (inNewTrans) conn.BeginTransaction(commitOnSave: true); foreach (var batchCmd in updateSet.BatchCommands) { ExecuteDbCommand(batchCmd.DbCommand, conn, DbExecutionType.NonQuery); if (batchCmd.HasActions) foreach (var action in batchCmd.GetPostActions()) action(); }//foreach if (inNewTrans) conn.Commit(); ReleaseConnection(conn); } catch { ReleaseConnection(conn, inError: true); throw; } }
private void SaveChangesNoBatch(EntitySession session, UpdateSet updateSet) { var conn = updateSet.Connection; var withTrans = conn.DbTransaction == null && updateSet.UseTransaction; try { var start = CurrentTickCount; if (withTrans) { conn.BeginTransaction(commitOnSave: true); } //execute commands if (session.ScheduledCommands.Count > 0) { ExecuteScheduledCommands(conn, session, CommandSchedule.TransactionStart); } //Apply record updates foreach (var rec in updateSet.AllRecords) { ApplyUpdate(conn, rec); } //Execute scheduled commands if (session.ScheduledCommands.Count > 0) { ExecuteScheduledCommands(conn, session, CommandSchedule.TransactionEnd); } if (conn.DbTransaction != null && conn.Flags.IsSet(ConnectionFlags.CommitOnSave)) { conn.Commit(); } ReleaseConnection(conn); } catch { ReleaseConnection(conn, inError: true); throw; } }
private void SaveChangesNoBatch(EntitySession session, UpdateSet updateSet) { var conn = updateSet.Connection; var withTrans = conn.DbTransaction == null && updateSet.UseTransaction; try { var start = CurrentTickCount; if(withTrans) conn.BeginTransaction(commitOnSave: true); //execute commands if (session.ScheduledCommands.Count > 0) ExecuteScheduledCommands(conn, session, CommandSchedule.TransactionStart); //Apply record updates foreach(var rec in updateSet.AllRecords) ApplyUpdate(conn, rec); //Execute scheduled commands if (session.ScheduledCommands.Count > 0) ExecuteScheduledCommands(conn, session, CommandSchedule.TransactionEnd); if (conn.DbTransaction != null && conn.Flags.IsSet(ConnectionFlags.CommitOnSave)) conn.Commit(); ReleaseConnection(conn); } catch { ReleaseConnection(conn, inError: true); throw; } }
public void SaveChanges(EntitySession session) { if (session.HasChanges()) { var records = session.RecordsChanged.Where(rec => ShouldUpdate(rec)).ToList(); var conn = GetConnection(session); var updateSet = new UpdateSet(conn, _timeService.UtcNow, records); var batchMode = ShouldUseBatchMode(updateSet); if (batchMode) SaveChangesInBatchMode(updateSet); else SaveChangesNoBatch(session, updateSet); } //commit if we have session connection with transaction and CommitOnSave var sConn = session.CurrentConnection; if (sConn != null) { if (sConn.DbTransaction != null && sConn.Flags.IsSet(ConnectionFlags.CommitOnSave)) sConn.Commit(); if (sConn.Lifetime != ConnectionLifetime.Explicit) ReleaseConnection(sConn); } session.ScheduledCommands.Clear(); }
// Checks if batch is needed (record count > 1); and driver supports batch mode, and if batch is not disabled; // If there are any out params for CRUD stored procs (identity, timestamp) then driver should out param for batch mode // MS SQL supports batch with output params; SQL CE does not support batch at all - one statement at a time. // MySql and Postgres support batch, but there are some troubles in using output parameters inside the batch, // so for these drivers batch OutParamsInBatchMode is not set, so we do not use batch mode if there are any. private bool ShouldUseBatchMode(UpdateSet updateSet) { var totalCount = updateSet.AllRecords.Count + updateSet.Session.ScheduledCommands.Count; if (totalCount > 1 && Settings.ModelConfig.Options.IsSet(DbOptions.UseBatchMode)) { //Probably we should use batch mode; check if it is disabled or stored procs are disabled if(updateSet.Session.Options.IsSet(EntitySessionOptions.DisableBatch | EntitySessionOptions.DisableStoredProcs)) return false; // check if there any out params if(updateSet.UsesOutParams && !_driver.Supports(DbFeatures.OutParamsInBatchedUpdates)) return false; //otherwise return true return true; } return false; }