/// <inheritdoc /> public async Task Save <T>(T es) where T : class, TEventSourced { if (es == null) { return; } var events = es.GetUncommittedEvents().ToList(); if (events.Count == 0) { return; } var stream = await _streams.Find <T>(es.Id, _timeline.Id) ?? _streams.CreateEmpty(es, _timeline.Id); // _streams.GetOrAdd(es, _timeline.Id); if (stream.Version >= 0 && es.Version - events.Count < stream.Version) { throw new InvalidOperationException($"Stream ( {stream.Key}@{stream.Version} ) is ahead of aggregate root with version {es.Version - events.Count} saving {events.Count} events )"); } foreach (var e in events.Cast <Event>()) { if (e.Timestamp == default) { e.Timestamp = _timeline.Now; } if (e.LocalId == default) { e.LocalId = new EventId(Configuration.ReplicaName, e.Timestamp); } if (e.OriginId == default) { e.OriginId = e.LocalId; } e.Stream = stream.Key; e.Timeline = _timeline.Id; } await _eventStore.AppendToStream(stream, events); if (es is ISaga saga) { var commands = saga.GetUncommittedCommands(); foreach (var command in commands) { await _bus.CommandAsync(command); } } #if USE_ES_CACHE _cache[stream.Key] = es; #endif }
public async Task <T> Find <T>(Guid id) where T : class, IEventSourced, new() { var stream = _streams.Find(id); if (stream == null) { return(null); } var events = await _eventStore.ReadStream(stream, 0, int.MaxValue); var aggregate = new T(); aggregate.LoadFrom <T>(id, events); return(aggregate); }
public MergeFlow(ITimeline currentBranch, IEventStore <T> eventStore, IStreamLocator streamLocator, bool doMerge) : base(Configuration.DataflowOptions) { _inputBlock = new ActionBlock <IStream>( async s => { var version = ExpectedVersion.EmptyStream; var parentStream = s.Branch(currentBranch?.Id, ExpectedVersion.EmptyStream); var parent = s.Parent; while (parent != null && parent.Version > ExpectedVersion.EmptyStream) { parentStream = await streamLocator.Find(parent); version = parentStream.Version; if (currentBranch != null && (version > parent.Version && parent.Timeline == currentBranch.Id)) { var theseEvents = await eventStore .ReadStream <IEvent>(parentStream, parent.Version + 1, version - parent.Version) .Select(e => e.MessageId).ToList(); var thoseEvents = await eventStore .ReadStream <IEvent>(s, parent.Version + 1, version - parent.Version) .Select(e => e.MessageId).ToList(); if (theseEvents.Zip(thoseEvents, (e1, e2) => (e1, e2)).Any(x => x.Item1 != x.Item2)) { throw new InvalidOperationException( $"{currentBranch?.Id} timeline has moved on in the meantime, aborting...( {parentStream.Key} : {version} > {parent.Version} )"); } return; } if (s.Version == version) { return; } if (parentStream.Timeline == currentBranch?.Id) { break; } parent = parent.Parent; } if (!doMerge) { return; } Interlocked.Increment(ref _numberOfStreams); var events = await eventStore.ReadStream <IEvent>(s, version + 1, s.Version - version).ToList(); foreach (var e in events.OfType <Event>()) { e.Stream = parentStream.Key; } Interlocked.Add(ref _numberOfEvents, events.Count); await eventStore.AppendToStream(parentStream, events, false); }, Configuration.DataflowOptions.ToDataflowBlockOptions(true)); // .ToExecutionBlockOption(true)); RegisterChild(_inputBlock); }