/// <summary> /// Execute a service from a client /// </summary> /// <param name="customerId"></param> /// <param name="userId"></param> /// <param name="profile"></param> /// <param name="area"></param> /// <param name="moduleId"></param> /// <param name="service"></param> /// <param name="record"></param> /// <param name="identity"></param> /// <returns>Data</returns> public JObject ExecuteService(int customerId, int userId, UserProfile.EUserProfile profile, string area, int moduleId, string service, JObject record, JObject identity) { JObject result = null; Info($"Executing the service: ['{service}', '{(record == null ? "null" : record.ToString(Formatting.None))}', '{(identity == null ? "null" : identity.ToString(Formatting.None))}'] ..."); // Lock database during the execution of the service using (DatabaseLock lockDatabase = Database.Lock(customerId)) { // Execute the request try { result = Database.ExecuteService(customerId, userId, profile, area, moduleId, service, record, identity); // Unlock the database lockDatabase.Commit(); } catch (System.Exception ex) { Exception("An exception occurs on executing the service", ex); // Rollback the requests done throw; } } Info($"The service has correctly been executed : {(result == null ? "null" : result.ToString(Formatting.None))}"); return(result); }
/// <summary> /// Override the Dispose to write a message on deleting the database context /// </summary> /// <param name="disposing"></param> protected override void Dispose(bool disposing) { if (IsVerbose()) { Verbose($"Deleting {ConfigurationManager.DatabaseProvider} database instance ..."); } if (_lock != null) { _lock.Dispose(); _lock = null; } base.Dispose(disposing); }
/// <summary> /// Execute a list of requests from a client /// </summary> /// <param name="transaction"></param> /// <returns>RequestId, Error, Record</returns> public List <Tuple <DSRecord, InformationRecord> > ExecuteTransaction(DSTransaction transaction) { Info($"Executing the transaction [{transaction.RequestId}] containing {transaction.Requests.Count} requests ..."); List <Tuple <DSRecord, InformationRecord> > recordsTreated = new List <Tuple <DSRecord, InformationRecord> >(); List <Tuple <string, DSRecord, InformationRecord> > recordsToUpdate = new List <Tuple <string, DSRecord, InformationRecord> >(); // Retrieve the database schema DSDatabase schema = ConfigurationManager.Schemas[transaction.Area]; if (schema == null) { Error("No schema available!"); throw new ExceptionDefinitionRecord("ERR_SCHEMA"); } // Lock database during the execution of the request using (DatabaseLock lockDatabase = Database.Lock(transaction.CustomerId)) { try { if (IsVerbose()) { Verbose("Getting the first tick of the transaction ..."); } // Get the tick string tickKey = $"Database.Tick.{transaction.CustomerId}"; int tick = 0; ParameterRecord tickRecord = Database._Parameter.FirstOrDefault(p => p.Key.Equals(tickKey)); if (tickRecord == null) { tickRecord = new ParameterRecord { Key = tickKey, Value = transaction.Requests.Count.ToString() }; tickRecord = Database._Parameter.Add(tickRecord); } else { tick = int.Parse(tickRecord.Value); tickRecord.Value = (tick + transaction.Requests.Count).ToString(); } Database.SaveChanges(); if (IsDebug()) { Debug($"First tick is {tick}"); } // Execute the OnBefore trigger if (IsVerbose()) { Verbose("Executing the pre-request of the transaction ..."); } transaction.SetNewTick(tick); foreach (DSRequest request in transaction.Requests) { // Execute the request if (IsVerboseAll()) { Verbose($"Executing the pre-request[{request.Id}] with tick[{request.NewTick}]: {request} ..."); } // Execute the trigger before requesting schema.OnBeforeExecuteRequest(Database, request.NewTick, transaction.CustomerId, transaction.UserId, transaction.Area, transaction.Profile, request.Table, request.Action, request.RecordId, request.Record, request.Identity); } if (IsDebug()) { Debug($"Pre-request executed for {transaction.Requests.Count} requests"); } // Execute each request if (IsVerbose()) { Verbose("Executing the transaction ..."); } // Execution by lot List <RequestTableRecord> actions = new List <RequestTableRecord>(); int index = 0; foreach (List <DSRequest> lot in transaction.LotRequests) { // Execute the lot of requests if (IsVerbose()) { Verbose($"Executing the lot[{index}] with {lot.Count} requests ..."); } // Execute the lot of requests recordsTreated.AddRange(schema.ExecuteRequest(Database, transaction, lot)); // Saving data Database.SaveChanges(); index++; } if (IsVerbose()) { Verbose("Building the list of actions ..."); } index = 0; foreach (DSRequest request in transaction.Requests) { Tuple <DSRecord, InformationRecord> recordTreated = recordsTreated[index]; // Keep in memory all records executed recordsToUpdate.Add(Tuple.Create(request.Table, recordTreated.Item1, recordTreated.Item2)); // The request is correctly executed ... Store a new request actions.Add(new RequestTableRecord { Tick = request.NewTick, CustomerId = transaction.CustomerId, UserId = transaction.UserId, RequestId = transaction.RequestId, Table = request.Table, Action = request.Action, Id = (recordTreated == null || recordTreated.Item1 == null ? -1 : recordTreated.Item1.Id) }); Info($"The request[{request.Id}] has correctly been executed : {(recordTreated == null ? "null" : recordTreated.ToString())}"); index++; } if (IsDebug()) { Debug($"Transaction executed with {transaction.Requests.Count} requests"); } // Write actions into the RequestTable if (IsVerbose()) { Verbose($"Writing {actions.Count} actions into the RequestTable ..."); } Database._RequestTable.AddRange(actions); Database.SaveChanges(); if (IsDebug()) { Debug($"{actions.Count} actions written into the RequestTable"); } // Execute the OnAfter trigger if (IsVerbose()) { Verbose("Executing the post-request of the transaction ..."); } foreach (DSRequest request in transaction.Requests) { // Execute the request if (IsVerboseAll()) { Verbose($"Executing the post-request[{request.Id}] with tick[{request.NewTick}]: {request} ..."); } // Execute the trigger before requesting DSRecord record = recordsTreated[request.Id].Item1; if (record != null) { schema.OnAfterExecuteRequest(Database, request.NewTick, transaction.CustomerId, transaction.UserId, transaction.Area, transaction.Profile, request.Table, request.Action, record.Id, record); } } if (IsDebug()) { Debug($"Post-request executed for {transaction.Requests.Count} requests"); } // Unlock the database and commit all changes if (IsVerbose()) { Verbose("Committing changes ..."); } lockDatabase.Commit(); if (IsVerbose()) { Verbose("Changes committed"); } // Update the cache manager if (IsVerbose()) { Verbose("Updating cache ..."); } DatabaseCacheManager.Instance.UpdateCache(recordsToUpdate, transaction.CustomerId, tick + transaction.Requests.Count); if (IsVerbose()) { Verbose("Cache updated"); } } catch (System.Exception ex) { Exception("An exception occurs on executing the transaction", ex); bool saveFailed = false; do { saveFailed = false; try { Database.SaveChanges(); } catch (DbUpdateException ex2) { saveFailed = true; ex2.Entries.Single().Reload(); } }while (saveFailed); // Rollback all request already executed throw ex; } } Info("Transaction done"); return(recordsTreated); }
/// <summary> /// Lock the database for a customer /// </summary> /// <param name="customerId"></param> public DatabaseLock Lock(int customerId) { string lockKey = $"Database.Lock.{customerId}"; // Create Database.Lock if the line doesn't exist ParameterRecord parameterRecord = _Parameter.FirstOrDefault(p => p.Key.Equals(lockKey)); if (parameterRecord == null) { parameterRecord = new ParameterRecord() { Key = $"Database.Lock.{customerId}", Value = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }; parameterRecord = _Parameter.Add(parameterRecord); SaveChanges(); } // Lock the database DbContextTransaction transaction = Database.BeginTransaction(); switch (_provider.Type) { case Provider.Provider.EProvider.Oracle: if (Database.Connection.State != System.Data.ConnectionState.Open) { Database.Connection.Open(); } Database.ExecuteSqlCommand($"UPDATE \"_Parameter\" SET \"Value\" = '{DateTime.Now:yyyy-MM-dd HH:mm:ss}' where \"Key\" = 'Database.Lock.{customerId}'"); break; case Provider.Provider.EProvider.SQLServer: if (Database.Connection.State != System.Data.ConnectionState.Open) { Database.Connection.Open(); } Database.ExecuteSqlCommand($"UPDATE [{ConfigurationManager.DatabaseSchema}].[_Parameter] SET [Value] = '{DateTime.Now:yyyy-MM-dd HH:mm:ss}' where [Key] = 'Database.Lock.{customerId}'"); break; case Provider.Provider.EProvider.Firebird: if (Database.Connection.State != System.Data.ConnectionState.Open) { Database.Connection.Open(); } Database.ExecuteSqlCommand($"UPDATE \"_Parameter\" SET \"Value\" = '{DateTime.Now:yyyy-MM-dd HH:mm:ss}' where \"Key\" = 'Database.Lock.{customerId}'"); break; case Provider.Provider.EProvider.MySQL: if (Database.Connection.State != System.Data.ConnectionState.Open) { Database.Connection.Open(); } Database.ExecuteSqlCommand($"UPDATE `{ConfigurationManager.DatabaseSchema}`.`_Parameter` SET `Value` = '{DateTime.Now:yyyy-MM-dd HH:mm:ss}' where `Key` = 'Database.Lock.{customerId}'"); break; default: Warn("Unable to lock the database due to an implementation of locking database missing!"); break; } _lock = new DatabaseLock(transaction); return(_lock); }