/// <summary> /// Notification de la fin de la session /// </summary> private ISessionInformation OnSessionCompleted(SessionLocalInfo info, IEnumerable <IEventNotifier> notifiers, IExecutionResultInternal messages) { DebugContract.Requires(notifiers != null, "notifiers"); // On fait une copie de la session car les événements peuvent // être souscrits dans un autre thread var sessionContext = new SessionInformation(this, info, SessionDataContext.TrackingData, messages); try { // Notifications via RX. foreach (var notifier in notifiers) { notifier.NotifySessionCompleted(sessionContext, SessionContext); } // Déclenchement des événements // D'abord évenement hard var tmp = Completing; if (tmp != null) { tmp(this, new SessionCompletingEventArgs(sessionContext)); } } catch (Exception ex) { // Si une erreur survient dans une notification, on l'intercepte Log(new DiagnosticMessage(MessageType.Error, ex.Message, "SessionTerminated", SessionDataContext.InValidationProcess, null, ex)); } return(sessionContext); }
private ExecutionResult ExecuteConstraints(SessionDataContext ctx, SessionLocalInfo currentInfo) { var messages = ctx.MessageList; // Notification des événements. // La notification se fait encore dans le scope actif // A partir d'ici, les événements (issues des commandes) NE SONT PLUS pris en compte car si une nouvelle commande est émise // il faut pouvoir : // 1 - exécuter les contraintes sur les nouvelles modifications. // 2 - renvoyer les nouveaux évents. // Ce qui n'est pas possible à partir d'ici puisqu'on est dans le processus gérant ces cas. // // Si on veut générer d'autres events, il faut s'abonner dans un autre thread (pour pouvoir créer une nouvelle session) ctx.ReadOnly = true; var hasInvolvedElements = false; using (CodeMarker.MarkBlock("Session.PrepareInvolvedElements")) { // Récupération des éléments impactés au cours de la session // Ne pas le faire lors des chargements des metadonnées car : // 1 - Il n'y a pas de contraintes sur les metadonnées // 2 - En cas de chargement d'une extension, les métadonnées ne sont pas encore accessibles et cela fait planter le code. hasInvolvedElements = SessionDataContext.TrackingData.PrepareModelElements(IsAborted, (currentInfo.Mode & SessionMode.LoadingSchema) == SessionMode.LoadingSchema); } // Validation implicite sur les éléments modifiés au cours de la session // Ce code ne doit pas se faire lorsqu'on charge les metadonnées if (hasInvolvedElements && (currentInfo.Mode & SessionMode.SkipConstraints) != SessionMode.SkipConstraints) { try { // Vérification des contraintes implicites using (CodeMarker.MarkBlock("Session.CheckConstraints")) { CheckConstraints(InvolvedElements); } // Une contrainte peut entrainer un rollback de la transaction // si il retourne une erreur dans result. ctx.Aborted = ctx.MessageList != null && ctx.MessageList.HasErrors; } catch (Exception ex) { ctx.Aborted = true; var exception = ex; if (exception is AggregateException) { exception = ((AggregateException)ex).InnerException; } Log(new DiagnosticMessage(MessageType.Error, exception.Message, ExceptionMessages.Diagnostic_ConstraintsProcessError, SessionDataContext.InValidationProcess, null, exception)); } } return(messages); }
internal SessionInformation(Session session, SessionLocalInfo info, ISessionTrackingData trackingData, IExecutionResultInternal messages) { DebugContract.Requires(session, "session"); DebugContract.Requires(trackingData); _context = session.SessionContext; TrackingData = trackingData; CancellationToken = session.CancellationToken; IsAborted = session.IsAborted; IsNested = session.IsNested; Store = session.Store; IsReadOnly = session.IsReadOnly; Mode = info.Mode; OriginStoreId = info.OriginStoreId; SessionId = session.SessionId; DefaultDomainModel = info.DefaultDomainModel; _contextInfos = info.Infos; Events = session.Events.ToList(); if (messages != null) { HasErrors = messages.HasErrors; HasWarnings = messages.HasWarnings; } }
private Tuple <ExecutionResult, ISessionInformation> CompleteTopLevelTransaction(IEnumerable <IEventNotifier> notifiers, SessionDataContext ctx, SessionLocalInfo currentInfo) { // Sauvegarde des références vers les objets qui sont utilisés aprés que les données de la session auront été supprimées ExecutionResult messages = null; ISessionInformation sessionInfo = null; // Il ne peut pas y avoir d'erreur dans cette partie de code try { ctx.Disposing = true; // Si la session était en lecture seule, on simplifie les traitements // Pas de validation if (!ctx.ReadOnly) { messages = ExecuteConstraints(ctx, currentInfo); } // Contexte en lecture seule de la session mais tjs dans le scope // Envoi des events même en read-only pour s'assurer que le OnSessionCompleted soit bien notifié if (!ctx.CancellationToken.IsCancellationRequested) { using (CodeMarker.MarkBlock("Session.OnSessionCompleted")) { sessionInfo = OnSessionCompleted(currentInfo, notifiers, messages); } } // Si tout va bien, on commite if (!IsAborted && _scope != null && (messages == null || !messages.HasErrors)) { _scope.Complete(); } } catch (Exception ex) { Log(new DiagnosticMessage(MessageType.Error, ex.Message, ExceptionMessages.Diagnostic_ApplicationError, SessionDataContext.InValidationProcess, null, ex)); } finally { DisposeSession(ctx); } return(Tuple.Create(messages ?? ExecutionResult.Empty, sessionInfo)); }