/// <summary> /// Sends this error as a notification email to the address in web.config as Error.Notification.Receiver. /// </summary> public static IEmailQueueItem SendAsNotification(this Exception error, string toNotify) { if (toNotify.IsEmpty()) { return(null); } var email = EmailService.EmailQueueItemFactory(); email.To = toNotify; email.Subject = "Error In Application"; email.Body = "URL: " + HttpContext.Current?.Request?.Url + Environment.NewLine + "IP: " + HttpContext.Current?.Request?.UserHostAddress + Environment.NewLine + "User: " + ApplicationEventManager.GetCurrentUserId(HttpContext.Current?.User) + Environment.NewLine + error.ToLogString(error.Message); Database.Save(email); return(email); }
async Task DoSave(IEntity entity, SaveBehaviour behaviour) { var mode = entity.IsNew ? SaveMode.Insert : SaveMode.Update; var asEntity = entity as Entity; if (mode == SaveMode.Update && (asEntity._ClonedFrom?.IsStale == true) && AnyOpenTransaction()) { throw new InvalidOperationException("This " + entity.GetType().Name + " instance in memory is out-of-date. " + "A clone of it is already updated in the transaction. It is not allowed to update the same instance multiple times in a transaction, because then the earlier updates would be overwriten by the older state of the instance in memory. \r\n\r\n" + @"BAD: Database.Update(myObject, x=> x.P1 = ...); // Note: this could also be nested inside another method that's called here instead. Database.Update(myObject, x=> x.P2 = ...); GOOD: Database.Update(myObject, x=> x.P1 = ...); myObject = Database.Reload(myObject); Database.Update(myObject, x=> x.P2 = ...);"); } if (EntityManager.IsImmutable(entity)) { throw new ArgumentException("An immutable record must be cloned before any modifications can be applied on it. " + $"Type={entity.GetType().FullName}, Id={entity.GetId()}."); } var dataProvider = GetProvider(entity); if (!IsSet(behaviour, SaveBehaviour.BypassValidation)) { await EntityManager.RaiseOnValidating(entity as Entity, EventArgs.Empty); await entity.Validate(); } else if (!dataProvider.SupportValidationBypassing()) { throw new ArgumentException(dataProvider.GetType().Name + " does not support bypassing validation."); } #region Raise saving event if (!IsSet(behaviour, SaveBehaviour.BypassSaving)) { var savingArgs = new System.ComponentModel.CancelEventArgs(); await EntityManager.RaiseOnSaving(entity, savingArgs); if (savingArgs.Cancel) { Cache.Current.Remove(entity); return; } } #endregion if (!IsSet(behaviour, SaveBehaviour.BypassLogging) && !(entity is IApplicationEvent) && Config.Get("Log.Record:Application:Events", defaultValue: true)) { await ApplicationEventManager.RecordSave(entity, mode); } await dataProvider.Save(entity); Cache.Current.UpdateRowVersion(entity); if (mode == SaveMode.Update && asEntity?._ClonedFrom != null && AnyOpenTransaction()) { asEntity._ClonedFrom.IsStale = true; asEntity.IsStale = false; } if (mode == SaveMode.Insert) { EntityManager.SetSaved(entity); } Cache.Current.Remove(entity); if (Transaction.Current != null) { Transaction.Current.TransactionCompleted += (s, e) => { Cache.Current.Remove(entity); } } ; DbTransactionScope.Root?.OnTransactionCompleted(() => Cache.Current.Remove(entity)); if (!(entity is IApplicationEvent)) { await OnUpdated(entity); } if (!IsSet(behaviour, SaveBehaviour.BypassSaved)) { await EntityManager.RaiseOnSaved(entity, new SaveEventArgs(mode)); } // OnSaved event handler might have read the object again and put it in the cache, which would // create invalid CachedReference objects. Cache.Current.Remove(entity); }