public void SaveChanges(EntitySession session) { if (session.HasChanges()) { var conn = GetConnection(session); var updateSet = new DbUpdateSet(session, this.DbModel, conn); var batchMode = ShouldUseBatchMode(updateSet); if (batchMode) { SaveChangesInBatchMode(updateSet); } else { SaveChangesNoBatch(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(DbConnectionFlags.CommitOnSave)) { sConn.Commit(); } if (sConn.Lifetime != DbConnectionLifetime.Explicit) { ReleaseConnection(sConn); } } session.ScheduledCommandsAtStart = null; session.ScheduledCommandsAtEnd = null; }
// Note: scheduled commands are already in batch commands private void SaveChangesInBatchMode(DbUpdateSet updateSet) { var batchBuilder = new DbBatchBuilder(this); var batch = batchBuilder.Build(updateSet); LogComment(updateSet.Session, "-- BEGIN BATCH ({0} rows, {1} batch command(s)) ---------------------------", updateSet.Records.Count, batch.Commands.Count); if (batch.Commands.Count == 1) { ExecuteBatchSingleCommand(batch); } else { ExecuteBatchMultipleCommands(batch); } LogComment(updateSet.Session, "-- END BATCH --------------------------------------\r\n"); var postExecActions = new List <Action>(); var session = updateSet.Session; //execute post-execute actions; it is usually handling output parameter values // Finalize records after update foreach (var rec in updateSet.Records) { rec.EntityInfo.SaveEvents.OnSubmittedChanges(rec); } }
// 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(DbUpdateSet updateSet) { var canUseBatch = Settings.Driver.Supports(DbFeatures.BatchedUpdates) && Settings.ModelConfig.Options.IsSet(DbOptions.UseBatchMode); if (!canUseBatch) { return(false); } var totalCount = updateSet.Records.Count + updateSet.Session.ScheduledCommandsCount(); if (totalCount <= 1) { return(false); } var options = updateSet.Session.Options; if (options.IsSet(EntitySessionOptions.DisableBatchMode)) { return(false); } // check if there any out params if (updateSet.InsertsIdentity && !_driver.Supports(DbFeatures.OutParamsInBatchedUpdates)) { return(false); } return(true); }
private void RefreshIdentityReferences(DbUpdateSet updateSet, IList <EntityRecord> records) { foreach (var rec in records) { if (rec.EntityInfo.Flags.IsSet(EntityFlags.ReferencesIdentity)) { rec.RefreshIdentityReferences(); } } }
public DbBatch Build(DbUpdateSet updateSet) { _batch = new DbBatch() { UpdateSet = updateSet }; _commandBuilder = new DataCommandBuilder(_driver, batchMode: true, mode: SqlGenMode.PreferLiteral); AddScheduledCommands(updateSet.Session.ScheduledCommandsAtStart); foreach (var group in updateSet.UpdateGroups) { foreach (var tableGroup in group.TableGroups) { AddUpdateGroup(tableGroup); }// foreach group } AddScheduledCommands(updateSet.Session.ScheduledCommandsAtEnd); FinalizeCurrentCommand(completed: true); return(_batch); }
private void SaveTableGroupRecordsOneByOne(DbUpdateTableGroup tableGrp, DataConnection conn, DbUpdateSet updateSet) { foreach (var rec in tableGrp.Records) { if (updateSet.InsertsIdentity && rec.EntityInfo.Flags.IsSet(EntityFlags.ReferencesIdentity)) { rec.RefreshIdentityReferences(); } var cmdBuilder = new DataCommandBuilder(this._driver); var sql = SqlFactory.GetCrudSqlForSingleRecord(tableGrp.Table, rec); cmdBuilder.AddRecordUpdate(sql, rec); var cmd = cmdBuilder.CreateCommand(conn, sql.ExecutionType, sql.ResultProcessor); ExecuteDataCommand(cmd); } }
private void SaveChangesNoBatch(DbUpdateSet updateSet) { var session = updateSet.Session; var conn = updateSet.Connection; var withTrans = conn.DbTransaction == null && updateSet.UseTransaction; try { LogComment(session, "-- SaveChanges starting, {0} records ------------", updateSet.Records.Count); var start = _timeService.ElapsedMilliseconds; if (withTrans) { conn.BeginTransaction(commitOnSave: true); } //execute commands ExecuteScheduledCommands(conn, session, session.ScheduledCommandsAtStart); //Apply record updates foreach (var grp in updateSet.UpdateGroups) { foreach (var tableGrp in grp.TableGroups) { switch (tableGrp.Operation) { case LinqOperation.Insert: if (CanProcessMany(tableGrp)) { var cmdBuilder = new DataCommandBuilder(this._driver, mode: SqlGenMode.PreferLiteral); var sql = SqlFactory.GetCrudInsertMany(tableGrp.Table, tableGrp.Records, cmdBuilder); cmdBuilder.AddInsertMany(sql, tableGrp.Records); var cmd = cmdBuilder.CreateCommand(conn, sql.ExecutionType, sql.ResultProcessor); ExecuteDataCommand(cmd); } else { SaveTableGroupRecordsOneByOne(tableGrp, conn, updateSet); } break; case LinqOperation.Update: SaveTableGroupRecordsOneByOne(tableGrp, conn, updateSet); break; case LinqOperation.Delete: if (CanProcessMany(tableGrp)) { var cmdBuilder = new DataCommandBuilder(this._driver); var sql = SqlFactory.GetCrudDeleteMany(tableGrp.Table); cmdBuilder.AddDeleteMany(sql, tableGrp.Records, new object[] { tableGrp.Records }); var cmd = cmdBuilder.CreateCommand(conn, DbExecutionType.NonQuery, sql.ResultProcessor); ExecuteDataCommand(cmd); } else { SaveTableGroupRecordsOneByOne(tableGrp, conn, updateSet); } break; } } //foreach tableGrp } //Execute scheduled commands ExecuteScheduledCommands(conn, session, session.ScheduledCommandsAtEnd); if (conn.DbTransaction != null && conn.Flags.IsSet(DbConnectionFlags.CommitOnSave)) { conn.Commit(); } var end = _timeService.ElapsedMilliseconds; LogComment(session, "-- SaveChanges completed. Records: {0}, Time: {1} ms. ------------", updateSet.Records.Count, end - start); ReleaseConnection(conn); } catch { ReleaseConnection(conn, inError: true); throw; } }