internal static void TryFlushCachedModel(T model, Mutator mutator = null) { if (!(Configuration?.UseCaching == true && Current.Cache.OperationalStatus == EOperationalStatus.Operational)) { return; } var key = mutator?.KeyPrefix + model.GetDataKey(); CacheFactory.FlushModel <T>(key); }
private static BulkDataOperation <T> BulkExecute(EActionType type, IEnumerable <T> models, Mutator mutator = null, bool rawMode = false) { ValidateState(type); var logStep = ""; object logObj = null; if (models == null) { return(null); } var modelSet = models.ToList(); var modelCount = modelSet.Count; var silent = modelCount == 1 || Info <T> .Settings.Silent; var _timed = new TimeLog(); if (modelSet.Count == 0) { return(null); } var resultPackage = new BulkDataOperation <T> { Type = type }; // First let's obtain any ServiceTokenGuid set by the user. lock (_bulkSaveLock) { try { var successSet = new List <T>(); var failureSet = new List <T>(); Clicker logClicker = null; _timed.Start($"{type} bulk [Warm-up]", !silent); if (!silent) { logClicker = modelSet.GetClicker(_timed.CurrentMessage, !silent); } resultPackage.Control = new ConcurrentDictionary <string, DataOperationControl <T> >(); var paralelizableClicker = logClicker; if (!rawMode) { Parallel.ForEach(modelSet, new ParallelOptions { MaxDegreeOfParallelism = 5 }, item => { paralelizableClicker?.Click(); if (item.IsNew()) { var tempKey = mutator?.KeyPrefix + item.ToJson().Sha512Hash(); if (resultPackage.Control.ContainsKey(tempKey)) { if (!silent) { Current.Log.Warn <T>(_timed.Log($" [Warm-up] duplicated key: {tempKey}")); } failureSet.Add(item); } else { resultPackage.Control[tempKey] = new DataOperationControl <T> { Current = item, IsNew = true, Original = null }; } return; } var modelKey = mutator?.KeyPrefix + item.GetDataKey(); if (resultPackage.Control.ContainsKey(modelKey)) { if (!silent) { Current.Log.Warn <T>(_timed.Log($"Repeated Identifier: {modelKey}. Data: {item.ToJson()}")); } return; } resultPackage.Control[modelKey] = new DataOperationControl <T> { Current = item }; }); logClicker?.End(); _timed.Log($"{type} bulk [Before]", false); logClicker = modelSet.GetClicker(_timed.CurrentMessage); logStep = "obtaining original keys"; var originalKeys = resultPackage.Control.Where(controlItem => !controlItem.Value.IsNew).Select(controlPair => controlPair.Key).ToList(); logStep = "obtaining original models"; var originalSet = Get(originalKeys).ToList(); logStep = _timed.Log("Populating Control structure"); var originalMap = originalSet.ToDictionary(i => i.GetDataKey(), i => i).ToList(); foreach (var item in originalMap) { resultPackage.Control[item.Key].Original = item.Value; } logStep = "processing Control structure"; foreach (var controlItem in resultPackage.Control) { if (!silent) { logClicker.Click(); } var currentModel = controlItem.Value.Current; var originalModel = controlItem.Value.Original; var canProceed = true; logStep = "checking if model is new"; if (!controlItem.Value.IsNew) { logObj = currentModel; originalModel = type == EActionType.Remove ? ProcBeforePipeline(EActionType.Remove, EActionScope.Model, mutator, currentModel, originalModel) : ProcBeforePipeline(controlItem.Value.IsNew ? EActionType.Insert : EActionType.Update, EActionScope.Model, mutator, currentModel, originalModel); if (originalModel == null) { failureSet.Add(currentModel); controlItem.Value.Success = false; controlItem.Value.Message = "Failed ProcBeforePipeline"; canProceed = false; } } else { if (type == EActionType.Remove) // So we're removing a New object. Just ignore. { failureSet.Add(currentModel); controlItem.Value.Success = false; controlItem.Value.Message = $"Invalid {type} operation: Record is New()"; canProceed = false; } else { originalModel = currentModel; } } if (canProceed) { logStep = "Adding model to process list"; logObj = currentModel; successSet.Add(originalModel); if (type == EActionType.Remove) { originalModel.BeforeRemove(); } else { if (!originalModel.IsNew()) { originalModel.BeforeUpdate(); } else { originalModel.BeforeInsert(); } originalModel.BeforeSave(); } controlItem.Value.Success = true; } } if (!silent) { logClicker.End(); } logStep = _timed.Log($"{type} {successSet.Count} models"); if (type == EActionType.Remove) { Info <T> .Settings.Adapter.BulkRemove(successSet); } else { Info <T> .Settings.Adapter.BulkUpsert(successSet); } if (!silent) { logClicker = modelSet.GetClicker($"{type} bulk [After]"); } logStep = _timed.Log("post-processing individual models"); Parallel.ForEach(resultPackage.Control.Where(i => i.Value.Success), new ParallelOptions { MaxDegreeOfParallelism = 5 }, controlModel => { var key = controlModel.Key; if (!silent) { logClicker.Click(); } if (type == EActionType.Remove) { controlModel.Value.Current.AfterRemove(); ProcAfterPipeline(EActionType.Remove, EActionScope.Model, mutator, controlModel.Value.Current, controlModel.Value.Original); } else { if (controlModel.Value.IsNew) { controlModel.Value.Current.AfterInsert(key); } else { controlModel.Value.Current.AfterUpdate(key); } controlModel.Value.Current.AfterSave(key); ProcAfterPipeline(controlModel.Value.IsNew ? EActionType.Insert : EActionType.Update, EActionScope.Model, mutator, controlModel.Value.Current, controlModel.Value.Original); } CacheFactory.FlushModel <T>(key); }); resultPackage.Success = successSet; resultPackage.Failure = failureSet; logStep = _timed.Log($"{type} bulk operation complete. Success: {resultPackage.Success.Count} | Failure: {resultPackage.Failure.Count}"); if (!silent) { logClicker.End(); } _timed.End(false); } else //RawMode means no triggers. AT ALL. { if (type == EActionType.Remove) { Info <T> .Settings.Adapter.BulkRemove(modelSet); } else { Info <T> .Settings.Adapter.BulkUpsert(modelSet); } } return(resultPackage); } catch (Exception e) { if (!silent) { Current.Log.Add <T>(e); } var ex = new Exception($"{type} - Error while {logStep} {logObj?.ToJson()}: {e.Message}", e); _timed.End(); throw ex; } } }