/// <summary>
        ///  When overriden, defines the custom behavior to be invoked after attempting to save the specified <paramref name="aggregate"/>.
        /// </summary>
        /// <param name="aggregate">The modified <see cref="Aggregate"/> instance if <paramref name="commit"/> is not <value>null</value>; otherwise the original <see cref="Aggregate"/> instance if <paramref name="error"/> is not <value>null</value>.</param>
        /// <param name="commit">The <see cref="Commit"/> generated if the save was successful; otherwise <value>null</value>.</param>
        /// <param name="error">The <see cref="Exception"/> thrown if the save was unsuccessful; otherwise <value>null</value>.</param>
        public override void PostSave(Aggregate aggregate, Commit commit, Exception error)
        {
            base.PostSave(aggregate, commit, error);

            if (error is ConcurrencyException)
                statistics.IncrementConflictCount();
        }
        /// <summary>
        /// Initializes a new instance of <see cref="SaveResult"/>.
        /// </summary>
        /// <param name="aggregate">The saved <see cref="Aggregate"/>.</param>
        /// <param name="commit">The generated <see cref="Commit"/></param>
        public SaveResult(Aggregate aggregate, Commit commit)
        {
            Verify.NotNull(aggregate, nameof(aggregate));
            Verify.NotNull(commit, nameof(commit));

            Aggregate = aggregate;
            Commit = commit;
        }
        /// <summary>
        /// Verify that the specified <paramref name="aggregate"/> state has not been corrupted after a failed save attempt and update the check sum if the save was successful.
        /// </summary>
        /// <param name="aggregate">The modified <see cref="Aggregate"/> instance if <paramref name="commit"/> is not <value>null</value>; otherwise the original <see cref="Aggregate"/> instance if <paramref name="error"/> is not <value>null</value>.</param>
        /// <param name="commit">The <see cref="Commit"/> generated if the save was successful; otherwise <value>null</value>.</param>
        /// <param name="error">The <see cref="Exception"/> thrown if the save was unsuccessful; otherwise <value>null</value>.</param>
        public override void PostSave(Aggregate aggregate, Commit commit, Exception error)
        {
            if (error != null)
                aggregate.VerifyHash();

            if (commit != null)
                aggregate.UpdateHash();
        }
        /// <summary>
        /// Apply the specified <see cref="Event"/> <paramref name="e"/> to the provided <paramref name="aggregate"/>.
        /// </summary>
        /// <param name="e">The event to be applied to the provided <paramref name="aggregate"/>.</param>
        /// <param name="aggregate">The <see cref="Aggregate"/> instance on which the event is to be applied.</param>
        public void Apply(Event e, Aggregate aggregate)
        {
            Verify.NotNull(e, nameof(e));
            Verify.NotNull(aggregate, nameof(aggregate));

            var applyMethods = GetKnownApplyMethods(aggregate);
            var applyMethod = GetApplyMethod(aggregate, e, applyMethods);

            Log.Trace("Applying event {0} to aggregate {1}", e, aggregate);

            applyMethod(aggregate, e);
        }
 /// <summary>
 ///  When overriden, defines the custom behavior to be invoked after attempting to save the specified <paramref name="aggregate"/>.
 /// </summary>
 /// <param name="aggregate">The modified <see cref="Aggregate"/> instance if <paramref name="commit"/> is not <value>null</value>; otherwise the original <see cref="Aggregate"/> instance if <paramref name="error"/> is not <value>null</value>.</param>
 /// <param name="commit">The <see cref="Commit"/> generated if the save was successful; otherwise <value>null</value>.</param>
 /// <param name="error">The <see cref="Exception"/> thrown if the save was unsuccessful; otherwise <value>null</value>.</param>
 public virtual void PostSave(Aggregate aggregate, Commit commit, Exception error)
 {
 }
 /// <summary>
 /// When overriden, defines the custom behavior to be invokes after successfully retrieving the specified <paramref name="aggregate"/>.
 /// </summary>
 /// <param name="aggregate">The loaded aggregate instance.</param>
 public virtual void PostGet(Aggregate aggregate)
 {
 }
 /// <summary>
 /// Invokes zero or more customized <see cref="PipelineHook.PostGet"/> implementations.
 /// </summary>
 /// <param name="aggregate">The loaded aggregate instance.</param>
 private void InvokePostGetHooks(Aggregate aggregate)
 {
     foreach (var pipelineHook in postGetHooks)
     {
         Log.Trace("Invoking post-get pipeline hook: {0}", pipelineHook);
         pipelineHook.PostGet(aggregate);
     }
 }
Beispiel #8
0
 /// <summary>
 /// When overriden, defines the custom behavior to be invoked prior to saving the specified <paramref name="aggregate"/>.
 /// </summary>
 /// <param name="aggregate">The aggregate to be modified by the current <paramref name="context"/>.</param>
 /// <param name="context">The current <see cref="CommandContext"/> containing the pending aggregate modifications.</param>
 public virtual void PreSave(Aggregate aggregate, CommandContext context)
 {
 }
 /// <summary>
 ///  When overriden, defines the custom behavior to be invoked after attempting to save the specified <paramref name="aggregate"/>.
 /// </summary>
 /// <param name="aggregate">The modified <see cref="Aggregate"/> instance if <paramref name="commit"/> is not <value>null</value>; otherwise the original <see cref="Aggregate"/> instance if <paramref name="error"/> is not <value>null</value>.</param>
 /// <param name="commit">The <see cref="Commit"/> generated if the save was successful; otherwise <value>null</value>.</param>
 /// <param name="error">The <see cref="Exception"/> thrown if the save was unsuccessful; otherwise <value>null</value>.</param>
 public override void PostSave(Aggregate aggregate, Commit commit, Exception error)
 {
     if (commit?.Id != null)
         DispatchCommit(commit);
     else
         Log.Warn("Commit not dispatched");
 }
        /// <summary>
        /// Gets the set of known apply methods for the given <paramref name="aggregate"/> instance.
        /// </summary>
        /// <param name="aggregate">The <see cref="Aggregate"/> instance on which the event is to be applied.</param>
        private ApplyMethodCollection GetKnownApplyMethods(Aggregate aggregate)
        {
            Type aggregateType = aggregate.GetType();
            ApplyMethodCollection applyMethods;

            if (!knownApplyMethods.TryGetValue(aggregateType, out applyMethods))
                throw new MappingException(Exceptions.AggregateTypeUndiscovered.FormatWith(aggregate.GetType()));

            return applyMethods;
        }
 /// <summary>
 /// Verify that the specified <paramref name="aggregate"/> state has not been corrupted before proceeding with the save.
 /// </summary>
 /// <param name="aggregate">The aggregate to be modified by the current <paramref name="context"/>.</param>
 /// <param name="context">The current <see cref="CommandContext"/> containing the pending aggregate modifications.</param>
 public override void PreSave(Aggregate aggregate, CommandContext context)
 {
     aggregate.VerifyHash();
 }
        /// <summary>
        /// Verify that the retrieved <paramref name="aggregate"/> state has not been corrupted before returning to the caller.
        /// </summary>
        /// <param name="aggregate">The loaded aggregate instance.</param>
        public override void PostGet(Aggregate aggregate)
        {
            Verify.NotNull(aggregate, nameof(aggregate));

            aggregate.VerifyHash();
        }
 /// <summary>
 /// Invokes zero or more customized <see cref="PipelineHook.PreSave"/> implementations.
 /// </summary>
 /// <param name="aggregate">The aggregate to be modified by the current <paramref name="context"/>.</param>
 /// <param name="context">The current <see cref="CommandContext"/> containing the pending aggregate modifications.</param>
 private void InvokePreSaveHooks(Aggregate aggregate, CommandContext context)
 {
     foreach (var pipelineHook in preSaveHooks)
     {
         Log.Trace("Invoking pre-save pipeline hook: {0}", pipelineHook);
         pipelineHook.PreSave(aggregate, context);
     }
 }
 /// <summary>
 /// Invokes zero or more customized <see cref="PipelineHook.PostSave"/> implementations.
 /// </summary>
 /// <param name="aggregate">The modified <see cref="Aggregate"/> instance if <paramref name="commit"/> is not <value>null</value>; otherwise the original <see cref="Aggregate"/> instance if <paramref name="error"/> is not <value>null</value>.</param>
 /// <param name="commit">The <see cref="Commit"/> generated if the save was successful; otherwise <value>null</value>.</param>
 /// <param name="error">The <see cref="Exception"/> thrown if the save was unsuccessful; otherwise <value>null</value>.</param>
 private void InvokePostSaveHooks(Aggregate aggregate, Commit commit, Exception error)
 {
     foreach (var pipelineHook in postSaveHooks)
     {
         Log.Trace("Invoking post-save pipeline hook: {0}", pipelineHook);
         pipelineHook.PostSave(aggregate, commit, error);
     }
 }
 /// <summary>
 /// When overriden, defines the custom behavior to be invoked prior to saving the specified <paramref name="aggregate"/>.
 /// </summary>
 /// <param name="aggregate">The aggregate to be modified by the current <paramref name="context"/>.</param>
 /// <param name="context">The current <see cref="CommandContext"/> containing the pending aggregate modifications.</param>
 public virtual void PreSave(Aggregate aggregate, CommandContext context)
 {
 }
        /// <summary>
        /// Get the associated apply method for the specified <see cref="Event"/> <paramref name="e"/> from the known <paramref name="applyMethods"/>.
        /// </summary>
        /// <param name="aggregate">The <see cref="Aggregate"/> instance on which the event is to be applied.</param>
        /// <param name="e">The <see cref="Event"/> to be applied.</param>
        /// <param name="applyMethods">The set of known apply methods for a given aggregate instance</param>
        private static Action<Aggregate, Event> GetApplyMethod(Aggregate aggregate, Event e, ApplyMethodCollection applyMethods)
        {
            Action<Aggregate, Event> applyMethod;
            Type eventType = e.GetType();

            if (applyMethods.TryGetValue(eventType, out applyMethod))
                return applyMethod;

            if (!applyMethods.ApplyOptional)
                throw new MappingException(Exceptions.AggregateApplyMethodNotFound.FormatWith(aggregate.GetType(), e.GetType()));

            return VoidApplyMethod;
        }
        /// <summary>
        /// Save the specified <paramref name="context"/> changes for the given aggregate.
        /// </summary>
        /// <param name="aggregate">The current aggregate version for which the context applies.</param>
        /// <param name="context">The command context containing the aggregate changes to be applied.</param>
        public SaveResult Save(Aggregate aggregate, CommandContext context)
        {
            Verify.NotNull(aggregate, nameof(aggregate));
            Verify.NotNull(context, nameof(context));

            var commit = CreateCommit(aggregate, context);
            try
            {
                eventStore.Save(commit);
            }
            catch (DuplicateCommitException)
            {
                Log.Warn("Duplicate commit: {0}", commit);
            }

            // NOTE: Apply commit directly to existing aggregate. By default, each call to `Get` returns a new `Aggregate` instance.
            //       Should the caller hold on to a reference to `this` aggregate instance, it is their responsibility to gaurd against
            //       modifications (via `aggregate.Copy()` call) if they require an unaltered instance of `aggregate`.
            ApplyCommitToAggregate(commit, aggregate);
            if (aggregate.Version > 0 && aggregate.Version % snapshotInterval == 0)
                snapshotStore.Save(new Snapshot(aggregate.Id, aggregate.Version, aggregate));

            return new SaveResult(aggregate, commit);
        }
        /// <summary>
        /// Creates a commit for the specified <paramref name="aggregate"/> based on the provided <paramref name="context"/>.
        /// </summary>
        /// <param name="aggregate">The <see cref="Aggregate"/> instance for which the commit is to be applied.</param>
        /// <param name="context">The <see cref="CommandContext"/> instance containing the pending modifications to the associated <paramref name="aggregate"/>.</param>
        private static Commit CreateCommit(Aggregate aggregate, CommandContext context)
        {
            EventCollection events = context.GetRaisedEvents();
            HeaderCollection headers;

            if (aggregate.Version == 0)
            {
                var typeHeader = new Header(Header.Aggregate, aggregate.GetType().GetFullNameWithAssembly(), checkReservedNames: false);

                headers = new HeaderCollection(context.Headers.Concat(typeHeader));
            }
            else
            {
                headers = context.Headers;
            }

            return new Commit(context.CommandId, aggregate.Id, aggregate.Version + 1, headers, events);
        }
        /// <summary>
        /// Applies all <see cref="Commit.Events"/> to the specified <paramref name="aggregate"/> instance.
        /// </summary>
        /// <param name="commit">The <see cref="Commit"/> to be applied to the specified <paramref name="aggregate"/>.</param>
        /// <param name="aggregate">The <see cref="Aggregate"/> instance for which the commit is to be applied.</param>
        private void ApplyCommitToAggregate(Commit commit, Aggregate aggregate)
        {
            var expectedVersion = aggregate.Version + 1;
            if (commit.Version != expectedVersion)
                throw new InvalidOperationException(Exceptions.MissingAggregateCommits.FormatWith(expectedVersion, commit.Version));

            aggregate.Version = commit.Version;
            foreach (var e in commit.Events)
            {
                using (new EventContext(aggregate.Id, commit.Headers, e))
                    aggregateUpdater.Apply(e, aggregate);
            }
        }
Beispiel #20
0
 /// <summary>
 /// When overriden, defines the custom behavior to be invokes after successfully retrieving the specified <paramref name="aggregate"/>.
 /// </summary>
 /// <param name="aggregate">The loaded aggregate instance.</param>
 public virtual void PostGet(Aggregate aggregate)
 {
 }
Beispiel #21
0
        /// <summary>
        /// Verify that the retrieved <paramref name="aggregate"/> state has not been corrupted before returning to the caller.
        /// </summary>
        /// <param name="aggregate">The loaded aggregate instance.</param>
        public override void PostGet(Aggregate aggregate)
        {
            Verify.NotNull(aggregate, nameof(aggregate));

            aggregate.VerifyHash();
        }
Beispiel #22
0
 /// <summary>
 ///  When overriden, defines the custom behavior to be invoked after attempting to save the specified <paramref name="aggregate"/>.
 /// </summary>
 /// <param name="aggregate">The modified <see cref="Aggregate"/> instance if <paramref name="commit"/> is not <value>null</value>; otherwise the original <see cref="Aggregate"/> instance if <paramref name="error"/> is not <value>null</value>.</param>
 /// <param name="commit">The <see cref="Commit"/> generated if the save was successful; otherwise <value>null</value>.</param>
 /// <param name="error">The <see cref="Exception"/> thrown if the save was unsuccessful; otherwise <value>null</value>.</param>
 public virtual void PostSave(Aggregate aggregate, Commit commit, Exception error)
 {
 }
Beispiel #23
0
 /// <summary>
 /// Verify that the specified <paramref name="aggregate"/> state has not been corrupted before proceeding with the save.
 /// </summary>
 /// <param name="aggregate">The aggregate to be modified by the current <paramref name="context"/>.</param>
 /// <param name="context">The current <see cref="CommandContext"/> containing the pending aggregate modifications.</param>
 public override void PreSave(Aggregate aggregate, CommandContext context)
 {
     aggregate.VerifyHash();
 }
        /// <summary>
        /// Save the specified <paramref name="context"/> changes for the given aggregate.
        /// </summary>
        /// <param name="aggregate">The current aggregate version for which the context applies.</param>
        /// <param name="context">The command context containing the aggregate changes to be applied.</param>
        public SaveResult Save(Aggregate aggregate, CommandContext context)
        {
            Verify.NotNull(aggregate, nameof(aggregate));
            Verify.NotNull(context, nameof(context));

            var copy = aggregate.Copy();
            var aggregateType = aggregate.GetType();
            var key = String.Concat(aggregateType.GetFullNameWithAssembly(), "-", aggregate.Id);
            using (var aggregateLock = new AggregateLock(aggregateType, aggregate.Id))
            {
                aggregateLock.Aquire();

                try
                {
                    var result = aggregateStore.Save(copy, context);

                    memoryCache.Set(key, copy, CreateCacheItemPolicy());

                    return result;
                }
                catch (ConcurrencyException)
                {
                    memoryCache.Remove(key);
                    throw;
                }
            }
        }
        /// <summary>
        /// Save the specified <paramref name="context"/> changes for the given aggregate.
        /// </summary>
        /// <param name="aggregate">The current aggregate version for which the context applies.</param>
        /// <param name="context">The command context containing the aggregate changes to be applied.</param>
        public SaveResult Save(Aggregate aggregate, CommandContext context)
        {
            InvokePreSaveHooks(aggregate, context);

            try
            {
                var result = aggregateStore.Save(aggregate, context);

                InvokePostSaveHooks(result.Aggregate, result.Commit, null);

                return result;
            }
            catch (Exception ex)
            {
                InvokePostSaveHooks(aggregate, null, ex);

                throw;
            }
        }