public TAggregate GetById <TAggregate>(string bucketId, IIdentity id, int versionToLoad) where TAggregate : class, IAggregateEx
        {
            //try to acquire a lock on the identity, to minimize risk of ConcurrencyException
            //do not sleep more than a certain amount of time (avoid some missing dispose).
            Int32   sleepCount   = 0;
            Boolean lockAcquired = false;

            while (!(lockAcquired = idSerializerDictionary.TryAdd(id, _identity)) && sleepCount < 100)
            {
                //some other thread is accessing that entity. Sleeping is the best choiche, because
                //the lock will be removed after a save, involving IO.
                Thread.Sleep(50);
                sleepCount++;
            }
            if (lockAcquired == true)
            {
                identityAcquireLock.Add(id);
                //Debug.WriteLine("Lock acquired [{3}] for identity {0} _identity {1}, stored {2}", id, _identity, idSerializerDictionary[id], lockAcquired);
            }
            //else
            //{
            //    //Debug.WriteLine("Acquire failed sleepCount = {0}", sleepCount);
            //}

            ISnapshot    snapshot  = this.GetSnapshot <TAggregate>(bucketId, id, versionToLoad);
            IEventStream stream    = this.OpenStream(bucketId, id, versionToLoad, snapshot);
            IAggregateEx aggregate = this.GetAggregate <TAggregate>(snapshot, stream);

            ApplyEventsToAggregate(versionToLoad, stream, aggregate);

            return(aggregate as TAggregate);
        }
        private IEventStream PrepareStream(string bucketId, IAggregateEx aggregate, Dictionary <string, object> headers)
        {
            IEventStream stream;
            var          streamsId = bucketId + "@" + aggregate.Id;

            if (!this._streams.TryGetValue(streamsId, out stream))
            {
                this._streams[streamsId] = stream = this._eventStore.CreateStream(bucketId, aggregate.Id.AsString());
            }

            foreach (var item in headers)
            {
                stream.UncommittedHeaders[item.Key] = item.Value;
            }

            aggregate.GetUncommittedEvents()
            .Cast <object>()
            .Select(x => new EventMessage {
                Body = x
            })
            .ToList()
            .ForEach(stream.Add);

            return(stream);
        }
        public static void ThrowHandlerNotFound(this IAggregateEx aggregate, object eventMessage)
        {
            string exceptionMessage =
                "Aggregate of type '{0}' raised an event of type '{1}' but not handler could be found to handle the message."
                .FormatWith(aggregate.GetType().Name, eventMessage.GetType().Name);

            throw new HandlerForDomainEventNotFoundException(exceptionMessage);
        }
        private void AfterSave(IIdentity id, IRepositoryEx repositoryEx, IAggregateEx aggregate)
        {
            //store the repository on the cache, actually we are keeping the very
            //same entity that was disposed by the caller.
            var cacheEntry = new CacheEntry(repositoryEx, aggregate);

            Cache.Add(id.AsString(), cacheEntry, _cachePolicy);
        }
 protected void SetupContext(IAggregateEx aggregate)
 {
     var context = _currentCommand.AllContextKeys
         .ToDictionary<string, string, object>(
             key => key,
             key => _currentCommand.GetContextData(key)
         );
     context["command.name"] = _currentCommand.GetType().Name;
     aggregate.EnterContext(context);
 }
예제 #6
0
        public void Verify_NumberOfCommitsShapshotPersistenceStrategy(Int32 treshold, Int32 version, Int32 numberOfEventsSaved, Boolean expected)
        {
            NumberOfCommitsShapshotPersistenceStrategy sut = new NumberOfCommitsShapshotPersistenceStrategy(treshold);
            IAggregateEx aggregate = NSubstitute.Substitute.For <IAggregateEx>();

            aggregate.Version.ReturnsForAnyArgs(version);
            var result = sut.ShouldSnapshot(aggregate, numberOfEventsSaved);

            Assert.That(result, Is.EqualTo(expected));
        }
예제 #7
0
        private static Dictionary <string, object> PrepareHeaders(
            IAggregateEx aggregate, Action <IDictionary <string, object> > updateHeaders)
        {
            var headers = new Dictionary <string, object>();

            headers[AggregateTypeHeader] = aggregate.GetType().FullName;
            if (updateHeaders != null)
            {
                updateHeaders(headers);
            }

            return(headers);
        }
예제 #8
0
        public override void Save(IAggregateEx aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders)
        {
            var checker = aggregate as IInvariantsChecker;

            if (checker != null)
            {
                var result = checker.CheckInvariants();
                if (!result)
                {
                    throw new InvariantNotSatifiedException(aggregate.Id, result.ErrorMessage);
                }
            }

            Save(GetBucketFor(aggregate), aggregate, commitId, updateHeaders);
        }
        public bool ShouldSnapshot(IAggregateEx aggregate, int numberOfEventsSaved)
        {
            //we need to save the aggregate if one of the event number are a multiple of the commit threshold.
            var actualVersion = aggregate.Version;
            var oldVersion    = actualVersion - numberOfEventsSaved;

            for (int i = oldVersion + 1; i <= actualVersion; i++)
            {
                if (i % _commitsThreshold == 0)
                {
                    return(true);
                }
            }
            return(false);
        }
예제 #10
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="bucketId"></param>
        /// <param name="aggregate"></param>
        /// <param name="commitId"></param>
        /// <param name="updateHeaders"></param>
        /// <returns></returns>
        public Int32 Save(string bucketId, IAggregateEx aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders)
        {
            if (_disposed)
            {
                throw new ObjectDisposedException("This instance of repository was already disposed");
            }

            Dictionary <string, object> headers = PrepareHeaders(aggregate, updateHeaders);
            Int32 uncommittedEvents             = aggregate.GetUncommittedEvents().Count;

            while (true)
            {
                IEventStream stream           = this.PrepareStream(bucketId, aggregate, headers);
                int          commitEventCount = stream.CommittedEvents.Count;

                try
                {
                    stream.CommitChanges(commitId);
                    aggregate.ClearUncommittedEvents();
                    return(uncommittedEvents);
                }
                catch (DuplicateCommitException dex)
                {
                    stream.ClearChanges();
                    _logger.Debug(String.Format("Duplicate commit exception bucket {0} - id {1} - commitid {2}. \n{3}", bucketId, aggregate.Id, commitId, dex));
                    return(0); //no events commited
                }
                catch (ConcurrencyException e)
                {
                    _logger.Warn(String.Format("Concurrency Exception bucket {0} - id {1} - commitid {2}. \n{3}", bucketId, aggregate.Id, commitId, e));
                    if (this.ThrowOnConflict(stream, commitEventCount))
                    {
                        //@@FIX -> aggiungere prima del lancio dell'eccezione
                        stream.ClearChanges();
                        throw new ConflictingCommandException(e.Message, e);
                    }
                    stream.ClearChanges();
                }
                catch (StorageException e)
                {
                    throw new PersistenceException(e.Message, e);
                }
                finally
                {
                    ReleaseAggregateId(aggregate.Id);
                }
            }
        }
        public void Snapshot(IAggregateEx aggregate, String bucket, Int32 numberOfEventsSaved)
        {
            if (SnapshotsSettings.HasOptedOut(aggregate.GetType()))
            {
                return;
            }

            var memento  = aggregate.GetSnapshot();
            var snapshot = new Snapshot(bucket, aggregate.Id.AsString(), aggregate.Version, memento);

            if (_cacheEnabled)
            {
                _cache.Set(aggregate.Id.AsString(), snapshot, _standardCachePolicy);
            }

            if (_snapshotPersistenceStrategy.ShouldSnapshot(aggregate, numberOfEventsSaved))
            {
                _persister.Persist(snapshot, aggregate.GetType().FullName);
            }
        }
예제 #12
0
        public override Int32 Save(IAggregateEx aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders)
        {
            var checker = aggregate as IInvariantsChecker;

            if (checker != null)
            {
                var result = checker.CheckInvariants();
                if (!result)
                {
                    throw new InvariantNotSatifiedException(aggregate.Id, result.ErrorMessage);
                }
            }

            var bucketId    = GetBucketFor(aggregate);
            var numOfEvents = Save(bucketId, aggregate, commitId, updateHeaders);

            //If we reach here we need to check if we want to persiste a snapshot.
            SnapshotManager.Snapshot(aggregate, bucketId, numOfEvents);
            return(numOfEvents);
        }
예제 #13
0
        public TAggregate GetById <TAggregate>(string bucketId, IIdentity id, int versionToLoad) where TAggregate : class, IAggregateEx
        {
            if (_disposed)
            {
                throw new ObjectDisposedException("This instance of repository was already disposed");
            }

            Stopwatch sw = null;

            if (NeventStoreExGlobalConfiguration.MetricsEnabled)
            {
                sw = Stopwatch.StartNew();
            }

            if (NeventStoreExGlobalConfiguration.RepositoryLockOnAggregateId)
            {
                //try to acquire a lock on the identity, to minimize risk of ConcurrencyException
                //do not sleep more than a certain amount of time (avoid some missing dispose).
                bool lockAcquired = TryAcquireLock(id);
                if (lockAcquired == false)
                {
                    //There is nothing to do if a lock failed to be aquired, but it worth to be signaled.
                    FailedLockCounter.Increment();
                }
            }

            ISnapshot    snapshot  = this.GetSnapshot <TAggregate>(bucketId, id, versionToLoad);
            IEventStream stream    = this.OpenStream(bucketId, id, versionToLoad, snapshot);
            IAggregateEx aggregate = this.GetAggregate <TAggregate>(snapshot, stream);

            //cache loaded stream to avoid reload during save.
            _streams[aggregate] = stream;

            ApplyEventsToAggregate(versionToLoad, stream, aggregate);
            if (NeventStoreExGlobalConfiguration.MetricsEnabled)
            {
                sw.Stop();
                RepositoryLoadCounter.Increment(typeof(TAggregate).Name, sw.ElapsedTicks);
            }
            return(aggregate as TAggregate);
        }
        public void Save(string bucketId, IAggregateEx aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders)
        {
            Dictionary <string, object> headers = PrepareHeaders(aggregate, updateHeaders);

            while (true)
            {
                IEventStream stream           = this.PrepareStream(bucketId, aggregate, headers);
                int          commitEventCount = stream.CommittedEvents.Count;

                try
                {
                    stream.CommitChanges(commitId);
                    aggregate.ClearUncommittedEvents();
                    return;
                }
                catch (DuplicateCommitException)
                {
                    stream.ClearChanges();
                    return;
                }
                catch (ConcurrencyException e)
                {
                    if (this.ThrowOnConflict(stream, commitEventCount))
                    {
                        //@@FIX -> aggiungere prima del lancio dell'eccezione
                        stream.ClearChanges();
                        throw new ConflictingCommandException(e.Message, e);
                    }
                    stream.ClearChanges();
                }
                catch (StorageException e)
                {
                    throw new PersistenceException(e.Message, e);
                }
                finally
                {
                    ReleaseAggregateId(aggregate.Id);
                }
            }
        }
        public virtual void Register(IAggregateEx aggregate)
        {
            if (aggregate == null)
            {
                throw new ArgumentNullException("aggregate");
            }

            this.registered = aggregate;

            // Get instance methods named Apply with one parameter returning void
            var applyMethods =
                aggregate.GetType()
                .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                .Where(
                    m => m.Name == "Apply" && m.GetParameters().Length == 1 && m.ReturnParameter.ParameterType == typeof(void))
                .Select(m => new { Method = m, MessageType = m.GetParameters().Single().ParameterType });

            foreach (var apply in applyMethods)
            {
                MethodInfo applyMethod = apply.Method;
                this.handlers.Add(apply.MessageType, m => applyMethod.Invoke(aggregate, new[] { m }));
            }
        }
예제 #16
0
        protected static void ClearUncommittedEvents()
        {
            IAggregateEx agg = Aggregate;

            agg.ClearUncommittedEvents();
        }
예제 #17
0
 public void Snapshot(IAggregateEx aggregate, String bucket, Int32 numberOfEventsSaved)
 {
 }
 public bool ShouldSnapshot(IAggregateEx aggregate, int numberOfEventsSaved)
 {
     return(false);
 }
예제 #19
0
 string GetBucketFor(IAggregateEx aggregate)
 {
     return(GetBucketFor(aggregate.GetType(), aggregate.Id));
 }
 public ConventionEventRouterEx(bool throwOnApplyNotFound, IAggregateEx aggregate)
     : this(throwOnApplyNotFound)
 {
     this.Register(aggregate);
 }
예제 #21
0
 private static void ApplyEventsToAggregate(int versionToLoad, IEventStream stream, IAggregateEx aggregate)
 {
     if (versionToLoad == 0 || aggregate.Version < versionToLoad)
     {
         foreach (var @event in stream.CommittedEvents.Select(x => x.Body))
         {
             aggregate.ApplyEvent(@event);
         }
     }
 }
 public CacheEntry(IRepositoryEx repositoryEx, IAggregateEx aggregate)
 {
     RepositoryEx = repositoryEx;
     Aggregate    = aggregate;
 }
예제 #23
0
 public virtual Int32 Save(IAggregateEx aggregate, Guid commitId, Action <IDictionary <string, object> > updateHeaders)
 {
     return(Save(Bucket.Default, aggregate, commitId, updateHeaders));
 }