From ebb94353c578d9c32e3b7c229dffd0f031851a3c Mon Sep 17 00:00:00 2001 From: Koen Metsu Date: Thu, 31 Aug 2017 14:19:37 +0200 Subject: [PATCH 1/6] Upgrade to 4.6.2 as preparation for sqlstreamstore --- src/Core/AggregateSource/AggregateSource.csproj | 4 ++-- src/Core/AggregateSource/Properties/Resources.Designer.cs | 4 ++-- .../EventStoreShopping/EventStoreShopping/App.config | 6 +++--- .../EventStoreShopping/EventStoreShopping.csproj | 5 +++-- src/SampleSource/SampleSource.csproj | 4 ++-- .../AggregateSource.Testing.NUnit.csproj | 4 ++-- .../AggregateSource.Testing.Tests.csproj | 4 ++-- src/Testing/AggregateSource.Testing.Tests/packages.config | 2 +- .../AggregateSource.Testing.Xunit.csproj | 5 +++-- .../AggregateSource.Testing/AggregateSource.Testing.csproj | 4 ++-- src/Testing/AggregateSource.Testing/packages.config | 2 +- 11 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/Core/AggregateSource/AggregateSource.csproj b/src/Core/AggregateSource/AggregateSource.csproj index 4144772..81b9cf5 100644 --- a/src/Core/AggregateSource/AggregateSource.csproj +++ b/src/Core/AggregateSource/AggregateSource.csproj @@ -1,5 +1,5 @@  - + Debug @@ -22,7 +22,7 @@ prompt 4 bin\Debug\net45\AggregateSource.xml - v4.5 + v4.6.2 AllRules.ruleset false diff --git a/src/Core/AggregateSource/Properties/Resources.Designer.cs b/src/Core/AggregateSource/Properties/Resources.Designer.cs index 777be3b..8c83c22 100644 --- a/src/Core/AggregateSource/Properties/Resources.Designer.cs +++ b/src/Core/AggregateSource/Properties/Resources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18408 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -19,7 +19,7 @@ namespace AggregateSource.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { diff --git a/src/Recipes/EventStoreShopping/EventStoreShopping/App.config b/src/Recipes/EventStoreShopping/EventStoreShopping/App.config index 8e15646..8d23437 100644 --- a/src/Recipes/EventStoreShopping/EventStoreShopping/App.config +++ b/src/Recipes/EventStoreShopping/EventStoreShopping/App.config @@ -1,6 +1,6 @@ - + - + - \ No newline at end of file + diff --git a/src/Recipes/EventStoreShopping/EventStoreShopping/EventStoreShopping.csproj b/src/Recipes/EventStoreShopping/EventStoreShopping/EventStoreShopping.csproj index 3313c04..5a8c566 100644 --- a/src/Recipes/EventStoreShopping/EventStoreShopping/EventStoreShopping.csproj +++ b/src/Recipes/EventStoreShopping/EventStoreShopping/EventStoreShopping.csproj @@ -1,5 +1,5 @@  - + Debug @@ -9,8 +9,9 @@ Properties EventStoreShopping EventStoreShopping - v4.5 + v4.6.2 512 + AnyCPU diff --git a/src/SampleSource/SampleSource.csproj b/src/SampleSource/SampleSource.csproj index 7b3dab3..ffb3fca 100644 --- a/src/SampleSource/SampleSource.csproj +++ b/src/SampleSource/SampleSource.csproj @@ -1,5 +1,5 @@  - + Debug @@ -23,7 +23,7 @@ DEBUG;TRACE;NET45 prompt 4 - v4.5 + v4.6.2 pdbonly diff --git a/src/Testing/AggregateSource.Testing.NUnit/AggregateSource.Testing.NUnit.csproj b/src/Testing/AggregateSource.Testing.NUnit/AggregateSource.Testing.NUnit.csproj index ddbf6ac..d72ce85 100644 --- a/src/Testing/AggregateSource.Testing.NUnit/AggregateSource.Testing.NUnit.csproj +++ b/src/Testing/AggregateSource.Testing.NUnit/AggregateSource.Testing.NUnit.csproj @@ -1,5 +1,5 @@  - + Debug @@ -21,7 +21,7 @@ TRACE;DEBUG;NUNIT;NET45 prompt 4 - v4.5 + v4.6.2 bin\Debug\AggregateSource.Testing.NUnit.XML diff --git a/src/Testing/AggregateSource.Testing.Tests/AggregateSource.Testing.Tests.csproj b/src/Testing/AggregateSource.Testing.Tests/AggregateSource.Testing.Tests.csproj index 65bb349..a2606d4 100644 --- a/src/Testing/AggregateSource.Testing.Tests/AggregateSource.Testing.Tests.csproj +++ b/src/Testing/AggregateSource.Testing.Tests/AggregateSource.Testing.Tests.csproj @@ -1,5 +1,5 @@  - + Debug @@ -21,7 +21,7 @@ DEBUG;TRACE;NET45 prompt 4 - v4.5 + v4.6.2 pdbonly diff --git a/src/Testing/AggregateSource.Testing.Tests/packages.config b/src/Testing/AggregateSource.Testing.Tests/packages.config index c1d76a9..dd5afa0 100644 --- a/src/Testing/AggregateSource.Testing.Tests/packages.config +++ b/src/Testing/AggregateSource.Testing.Tests/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/Testing/AggregateSource.Testing.Xunit/AggregateSource.Testing.Xunit.csproj b/src/Testing/AggregateSource.Testing.Xunit/AggregateSource.Testing.Xunit.csproj index 8c71563..8ffec08 100644 --- a/src/Testing/AggregateSource.Testing.Xunit/AggregateSource.Testing.Xunit.csproj +++ b/src/Testing/AggregateSource.Testing.Xunit/AggregateSource.Testing.Xunit.csproj @@ -1,5 +1,5 @@  - + Debug @@ -11,6 +11,7 @@ AggregateSource.Testing.Xunit v4.5 512 + true @@ -20,7 +21,7 @@ TRACE;DEBUG;XUNIT;NET45 prompt 4 - v4.5 + v4.6.2 pdbonly diff --git a/src/Testing/AggregateSource.Testing/AggregateSource.Testing.csproj b/src/Testing/AggregateSource.Testing/AggregateSource.Testing.csproj index 71abb12..c81c9f9 100644 --- a/src/Testing/AggregateSource.Testing/AggregateSource.Testing.csproj +++ b/src/Testing/AggregateSource.Testing/AggregateSource.Testing.csproj @@ -1,5 +1,5 @@  - + Debug @@ -22,7 +22,7 @@ prompt 4 bin\Debug\net45\AggregateSource.Testing.xml - v4.5 + v4.6.2 pdbonly diff --git a/src/Testing/AggregateSource.Testing/packages.config b/src/Testing/AggregateSource.Testing/packages.config index dcd5f19..656f907 100644 --- a/src/Testing/AggregateSource.Testing/packages.config +++ b/src/Testing/AggregateSource.Testing/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file From f538cbadaa3fddd2391d962c601c16639a1e1d9d Mon Sep 17 00:00:00 2001 From: Koen Metsu Date: Thu, 31 Aug 2017 14:22:31 +0200 Subject: [PATCH 2/6] First shot at sqlstreamstore implementation --- src/SqlStreamStore/.nuget/packages.config | 4 + ...ggregateSource.SqlStreamStore.Tests.csproj | 97 ++++ .../Framework/AggregateRootEntityStub.cs | 26 ++ .../Framework/Catch.cs | 18 + .../Framework/EventStub.cs | 32 ++ .../Framework/RepositoryScenarioBuilder.cs | 111 +++++ .../Framework/Snapshots/SnapshotStateStub.cs | 32 ++ .../SnapshotableAggregateRootEntityStub.cs | 39 ++ .../Model.cs | 16 + .../Properties/AssemblyInfo.cs | 4 + .../RepositoryTests.cs | 255 +++++++++++ .../Snapshots/SnapshotableRepositoryTests.cs | 424 ++++++++++++++++++ .../packages.config | 6 + .../AggregateSource.SqlStreamStore.sln | 53 +++ ...egateSource.SqlStreamStore.sln.DotSettings | 6 + .../AggregateSource.SqlStreamStore.csproj | 78 ++++ .../Properties/AssemblyInfo.cs | 4 + .../Repository.cs | 131 ++++++ .../Snapshots/SnapshotableRepository.cs | 137 ++++++ .../packages.config | 5 + 20 files changed, 1478 insertions(+) create mode 100644 src/SqlStreamStore/.nuget/packages.config create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/AggregateSource.SqlStreamStore.Tests.csproj create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/AggregateRootEntityStub.cs create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Catch.cs create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/EventStub.cs create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/RepositoryScenarioBuilder.cs create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Snapshots/SnapshotStateStub.cs create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Snapshots/SnapshotableAggregateRootEntityStub.cs create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Model.cs create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Properties/AssemblyInfo.cs create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/RepositoryTests.cs create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Snapshots/SnapshotableRepositoryTests.cs create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/packages.config create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.sln create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.sln.DotSettings create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore/AggregateSource.SqlStreamStore.csproj create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore/Properties/AssemblyInfo.cs create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore/Repository.cs create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore/Snapshots/SnapshotableRepository.cs create mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore/packages.config diff --git a/src/SqlStreamStore/.nuget/packages.config b/src/SqlStreamStore/.nuget/packages.config new file mode 100644 index 0000000..7025a72 --- /dev/null +++ b/src/SqlStreamStore/.nuget/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/AggregateSource.SqlStreamStore.Tests.csproj b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/AggregateSource.SqlStreamStore.Tests.csproj new file mode 100644 index 0000000..1ceafce --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/AggregateSource.SqlStreamStore.Tests.csproj @@ -0,0 +1,97 @@ + + + + + Debug + AnyCPU + {7E6BACB9-7804-429A-A71E-78B12D0AB786} + Library + Properties + AggregateSource.SqlStreamStore + AggregateSource.SqlStreamStore.Tests + v4.6.2 + 512 + + + + true + full + false + bin\Debug\net45 + DEBUG;TRACE;NET45 + prompt + 4 + v4.6.2 + + + pdbonly + true + bin\Release\net45 + TRACE;NET45 + prompt + 4 + v4.6.2 + + + + ..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll + True + + + ..\packages\SqlStreamStore.1.0.2\lib\net461\SqlStreamStore.dll + + + ..\packages\SqlStreamStore.MsSql.1.0.2\lib\net461\SqlStreamStore.MsSql.dll + + + + + + + + Framework\AggregateRootEntity.cs + + + Properties\SharedAssemblyInfo.cs + + + Properties\SharedVersionInfo.cs + + + + + + + + + + + + + + {cc3fcc99-9e18-45de-9b39-76031d45624d} + AggregateSource + + + {06aa30d7-8cd5-4ec4-99f2-50129dee0d57} + AggregateSource.SqlStreamStore + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/AggregateRootEntityStub.cs b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/AggregateRootEntityStub.cs new file mode 100644 index 0000000..e866a4b --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/AggregateRootEntityStub.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using AggregateSource; + +namespace SSS.Framework +{ + public class AggregateRootEntityStub : AggregateRootEntity + { + public static readonly Func Factory = () => new AggregateRootEntityStub(); + + readonly List _recordedEvents; + + public AggregateRootEntityStub() + { + _recordedEvents = new List(); + + Register(_ => _recordedEvents.Add(_)); + } + + public IList RecordedEvents + { + get { return new ReadOnlyCollection(_recordedEvents); } + } + } +} \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Catch.cs b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Catch.cs new file mode 100644 index 0000000..43f4431 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Catch.cs @@ -0,0 +1,18 @@ +using System; + +namespace SSS.Framework +{ + public static class Catch + { + public static void ExceptionOf(Action action) + { + // ReSharper disable EmptyGeneralCatchClause + try + { + action(); + } + catch {} + // ReSharper restore EmptyGeneralCatchClause + } + } +} \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/EventStub.cs b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/EventStub.cs new file mode 100644 index 0000000..1447ca2 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/EventStub.cs @@ -0,0 +1,32 @@ +namespace SSS.Framework +{ + public class EventStub + { + public int Value { get; set; } + + public EventStub() {} + + public EventStub(int value) + { + Value = value; + } + + public override bool Equals(object obj) + { + return Equals(obj as EventStub); + } + + bool Equals(EventStub @event) + { + return !ReferenceEquals(@event, null) && Value.Equals(@event.Value); + } + + public override int GetHashCode() + { + unchecked + { + return Value.GetHashCode()*10 + 2; + } + } + } +} \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/RepositoryScenarioBuilder.cs b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/RepositoryScenarioBuilder.cs new file mode 100644 index 0000000..511f7c3 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/RepositoryScenarioBuilder.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using AggregateSource; +//using SSS.Snapshots; +using SqlStreamStore; +using SqlStreamStore.Streams; +using SSS.Framework.Snapshots; +using StreamStoreStore.Json; + +namespace SSS.Framework +{ + public class RepositoryScenarioBuilder + { + readonly IStreamStore _eventStore; + readonly List> _eventStoreSchedule; + readonly List> _unitOfWorkSchedule; + UnitOfWork _unitOfWork; + + public RepositoryScenarioBuilder() + { + _eventStore = new InMemoryStreamStore(() => DateTime.UtcNow); + _unitOfWork = new UnitOfWork(); + _eventStoreSchedule = new List>(); + _unitOfWorkSchedule = new List>(); + } + + public RepositoryScenarioBuilder WithUnitOfWork(UnitOfWork value) + { + _unitOfWork = value; + return this; + } + + public RepositoryScenarioBuilder ScheduleAppendToStream(string stream, params object[] events) + { + if (stream == null) throw new ArgumentNullException("stream"); + if (events == null) throw new ArgumentNullException("events"); + _eventStoreSchedule.Add( + store => + { + var messages = events + .Select(o => + new NewStreamMessage( + messageId: Guid.NewGuid(), + type: o.GetType().AssemblyQualifiedName, + jsonData: SimpleJson.SerializeObject(o))) + .ToList(); + + store.AppendToStream(new StreamId(stream), ExpectedVersion.Any, messages.ToArray(), CancellationToken.None).GetAwaiter().GetResult(); + }); + return this; + } + + //public RepositoryScenarioBuilder ScheduleSnapshots(params Snapshot[] snapshots) + //{ + // if (snapshots == null) throw new ArgumentNullException("snapshots"); + // _eventStoreSchedule.Add( + // store => + // { + // foreach (var snapshot in snapshots) + // store.Advanced.AddSnapshot(snapshot); + // }); + // return this; + //} + + public RepositoryScenarioBuilder ScheduleDeleteStream(string stream) + { + if (stream == null) throw new ArgumentNullException("stream"); + _eventStoreSchedule.Add(store => store.DeleteStream(stream).GetAwaiter().GetResult()); + return this; + } + + public RepositoryScenarioBuilder ScheduleAttachToUnitOfWork(Aggregate aggregate) + { + if (aggregate == null) throw new ArgumentNullException("aggregate"); + _unitOfWorkSchedule.Add(uow => uow.Attach(aggregate)); + return this; + } + + public Repository BuildForRepository() + { + ExecuteScheduledActions(); + return new Repository( + AggregateRootEntityStub.Factory, + _unitOfWork, + _eventStore); + } + + //public SnapshotableRepository BuildForSnapshotableRepository() + //{ + // ExecuteScheduledActions(); + // return new SnapshotableRepository( + // SnapshotableAggregateRootEntityStub.Factory, + // _unitOfWork, + // _eventStore); + //} + + void ExecuteScheduledActions() + { + foreach (var action in _eventStoreSchedule) + { + action(_eventStore); + } + foreach (var action in _unitOfWorkSchedule) + { + action(_unitOfWork); + } + } + } +} \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Snapshots/SnapshotStateStub.cs b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Snapshots/SnapshotStateStub.cs new file mode 100644 index 0000000..0a7d50f --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Snapshots/SnapshotStateStub.cs @@ -0,0 +1,32 @@ +namespace SSS.Framework.Snapshots +{ + public class SnapshotStateStub + { + public int Value { get; private set; } + + public SnapshotStateStub() {} + + public SnapshotStateStub(int value) + { + Value = value; + } + + public override bool Equals(object obj) + { + return Equals(obj as SnapshotStateStub); + } + + bool Equals(SnapshotStateStub other) + { + return !ReferenceEquals(other, null) && Value.Equals(other.Value); + } + + public override int GetHashCode() + { + unchecked + { + return Value.GetHashCode()*10 + 1; + } + } + } +} \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Snapshots/SnapshotableAggregateRootEntityStub.cs b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Snapshots/SnapshotableAggregateRootEntityStub.cs new file mode 100644 index 0000000..455f31b --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Snapshots/SnapshotableAggregateRootEntityStub.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using AggregateSource; + +namespace SSS.Framework.Snapshots +{ + public class SnapshotableAggregateRootEntityStub : AggregateRootEntity, ISnapshotable + { + public static readonly Func Factory = + () => new SnapshotableAggregateRootEntityStub(); + + readonly List _recordedEvents; + + public SnapshotableAggregateRootEntityStub() + { + _recordedEvents = new List(); + + Register(_ => _recordedEvents.Add(_)); + } + + public object RecordedSnapshot { get; private set; } + + public IList RecordedEvents + { + get { return new ReadOnlyCollection(_recordedEvents); } + } + + public void RestoreSnapshot(object state) + { + RecordedSnapshot = state; + } + + public object TakeSnapshot() + { + return new SnapshotStateStub(new Random().Next()); + } + } +} \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Model.cs b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Model.cs new file mode 100644 index 0000000..73ec527 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Model.cs @@ -0,0 +1,16 @@ +using System; + +namespace SSS +{ + public class Model + { + public Model() + { + KnownIdentifier = "aggregate/" + Guid.NewGuid(); + UnknownIdentifier = "aggregate/" + Guid.NewGuid(); + } + + public string KnownIdentifier { get; private set; } + public string UnknownIdentifier { get; private set; } + } +} \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Properties/AssemblyInfo.cs b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..18da2c9 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Reflection; + +[assembly: AssemblyTitle("AggregateSource.SqlStreamStore.Tests")] +[assembly: AssemblyDescription("AggregateSource integration with SqlStreamStore. Unit tests.")] \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/RepositoryTests.cs b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/RepositoryTests.cs new file mode 100644 index 0000000..a46be01 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/RepositoryTests.cs @@ -0,0 +1,255 @@ +using System; +using System.Threading.Tasks; +using AggregateSource; +using SqlStreamStore; +using NUnit.Framework; +using SSS.Framework; + +namespace SSS +{ + namespace RepositoryTests + { + [TestFixture] + public class Construction + { + UnitOfWork _unitOfWork; + Func _factory; + IStreamStore _store; + + [SetUp] + public void SetUp() + { + _store = new InMemoryStreamStore(() => DateTime.UtcNow); + _unitOfWork = new UnitOfWork(); + _factory = AggregateRootEntityStub.Factory; + } + + [Test] + public void FactoryCanNotBeNull() + { + Assert.Throws( + () => new Repository(null, _unitOfWork, _store)); + } + + [Test] + public void UnitOfWorkCanNotBeNull() + { + Assert.Throws( + () => new Repository(_factory, null, _store)); + } + + [Test] + public void EventStoreCanNotBeNull() + { + Assert.Throws( + () => new Repository(_factory, _unitOfWork, null)); + } + + [Test] + public void UsingCtorReturnsInstanceWithExpectedProperties() + { + var sut = new Repository(_factory, _unitOfWork, _store); + Assert.That(sut.RootFactory, Is.SameAs(_factory)); + Assert.That(sut.UnitOfWork, Is.SameAs(_unitOfWork)); + Assert.That(sut.EventStore, Is.SameAs(_store)); + } + } + + [TestFixture] + public class WithEmptyStoreAndEmptyUnitOfWork + { + Repository _sut; + Model _model; + + [SetUp] + public void SetUp() + { + _model = new Model(); + _sut = new RepositoryScenarioBuilder().BuildForRepository(); + } + + [Test] + public void GetThrows() + { + var exception = + Assert.ThrowsAsync(() => _sut.GetAsync(_model.UnknownIdentifier)); + Assert.That(exception.Identifier, Is.EqualTo(_model.UnknownIdentifier)); + Assert.That(exception.ClrType, Is.EqualTo(typeof(AggregateRootEntityStub))); + } + + [Test] + public async Task GetOptionalReturnsEmpty() + { + var result = await _sut.GetOptionalAsync(_model.UnknownIdentifier); + + Assert.That(result, Is.EqualTo(Optional.Empty)); + } + + [Test] + public void AddAttachesToUnitOfWork() + { + var root = AggregateRootEntityStub.Factory(); + + _sut.Add(_model.KnownIdentifier, root); + + Aggregate aggregate; + var result = _sut.UnitOfWork.TryGet(_model.KnownIdentifier, out aggregate); + Assert.That(result, Is.True); + Assert.That(aggregate.Identifier, Is.EqualTo(_model.KnownIdentifier)); + Assert.That(aggregate.Root, Is.SameAs(root)); + } + } + + [TestFixture] + public class WithEmptyStoreAndFilledUnitOfWork + { + Repository _sut; + AggregateRootEntityStub _root; + Model _model; + + [SetUp] + public void SetUp() + { + _model = new Model(); + _root = AggregateRootEntityStub.Factory(); + _sut = new RepositoryScenarioBuilder(). + ScheduleAttachToUnitOfWork(new Aggregate(_model.KnownIdentifier, 0, _root)). + BuildForRepository(); + } + + [Test] + public void GetThrowsForUnknownId() + { + var exception = + Assert.ThrowsAsync(async () => await _sut.GetAsync(_model.UnknownIdentifier)); + Assert.That(exception.Identifier, Is.EqualTo(_model.UnknownIdentifier)); + Assert.That(exception.ClrType, Is.EqualTo(typeof(AggregateRootEntityStub))); + } + + [Test] + public async Task GetReturnsRootOfKnownId() + { + var result = await _sut.GetAsync(_model.KnownIdentifier); + + Assert.That(result, Is.SameAs(_root)); + } + + [Test] + public async Task GetOptionalReturnsEmptyForUnknownId() + { + var result = await _sut.GetOptionalAsync(_model.UnknownIdentifier); + + Assert.That(result, Is.EqualTo(Optional.Empty)); + } + + [Test] + public async Task GetOptionalReturnsRootForKnownId() + { + var result = await _sut.GetOptionalAsync(_model.KnownIdentifier); + + Assert.That(result, Is.EqualTo(new Optional(_root))); + } + } + + [TestFixture] + public class WithStreamPresentInStore + { + Repository _sut; + Model _model; + + [SetUp] + public void SetUp() + { + _model = new Model(); + _sut = new RepositoryScenarioBuilder(). + ScheduleAppendToStream(_model.KnownIdentifier, new EventStub(1)). + BuildForRepository(); + } + + [Test] + public void GetThrowsForUnknownId() + { + var exception = + Assert.ThrowsAsync(async () => await _sut.GetAsync(_model.UnknownIdentifier)); + Assert.That(exception.Identifier, Is.EqualTo(_model.UnknownIdentifier)); + Assert.That(exception.ClrType, Is.EqualTo(typeof(AggregateRootEntityStub))); + } + + [Test] + public async Task GetReturnsRootOfKnownId() + { + var result = await _sut.GetAsync(_model.KnownIdentifier); + + Assert.That(result.RecordedEvents, Is.EquivalentTo(new[] { new EventStub(1) })); + } + + [Test] + public async Task GetOptionalReturnsEmptyForUnknownId() + { + var result = await _sut.GetOptionalAsync(_model.UnknownIdentifier); + + Assert.That(result, Is.EqualTo(Optional.Empty)); + } + + [Test] + public async Task GetOptionalReturnsRootForKnownId() + { + var result = await _sut.GetOptionalAsync(_model.KnownIdentifier); + + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value.RecordedEvents, Is.EquivalentTo(new[] { new EventStub(1) })); + } + } + + [TestFixture] + public class WithDeletedStreamInStore + { + Repository _sut; + Model _model; + + [SetUp] + public void SetUp() + { + _model = new Model(); + _sut = new RepositoryScenarioBuilder(). + ScheduleAppendToStream(_model.KnownIdentifier, new EventStub(1)). + ScheduleDeleteStream(_model.KnownIdentifier). + BuildForRepository(); + } + + [Test] + public void GetThrowsForUnknownId() + { + var exception = + Assert.ThrowsAsync(async () => await _sut.GetAsync(_model.UnknownIdentifier)); + Assert.That(exception.Identifier, Is.EqualTo(_model.UnknownIdentifier)); + Assert.That(exception.ClrType, Is.EqualTo(typeof(AggregateRootEntityStub))); + } + + [Test] + public void GetThrowsForKnownDeletedId() + { + var exception = + Assert.ThrowsAsync(() => _sut.GetAsync(_model.KnownIdentifier)); + Assert.That(exception.Identifier, Is.EqualTo(_model.KnownIdentifier)); + Assert.That(exception.ClrType, Is.EqualTo(typeof(AggregateRootEntityStub))); + } + + [Test] + public async Task GetOptionalReturnsEmptyForUnknownId() + { + var result = await _sut.GetOptionalAsync(_model.UnknownIdentifier); + + Assert.That(result, Is.EqualTo(Optional.Empty)); + } + + [Test] + public async Task GetOptionalReturnsEmptyForKnownDeletedId() + { + var result = await _sut.GetOptionalAsync(_model.KnownIdentifier); + + Assert.That(result, Is.EqualTo(Optional.Empty)); + } + } + } +} \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Snapshots/SnapshotableRepositoryTests.cs b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Snapshots/SnapshotableRepositoryTests.cs new file mode 100644 index 0000000..84e9966 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Snapshots/SnapshotableRepositoryTests.cs @@ -0,0 +1,424 @@ +using System; +using AggregateSource; +using SqlStreamStore; +using NUnit.Framework; +using SSS.Framework; +using SSS.Framework.Snapshots; + +namespace SSS.Snapshots +{ + namespace SnapshotableRepositoryTests + { + [TestFixture] + public class Construction + { + IStoreEvents _eventStore; + UnitOfWork _unitOfWork; + Func _factory; + + [SetUp] + public void SetUp() + { + _eventStore = Wireup.Init().UsingInMemoryPersistence().Build(); + _unitOfWork = new UnitOfWork(); + _factory = SnapshotableAggregateRootEntityStub.Factory; + } + + [Test] + public void FactoryCanNotBeNull() + { + Assert.Throws( + () => new SnapshotableRepository(null, _unitOfWork, _eventStore)); + } + + [Test] + public void UnitOfWorkCanNotBeNull() + { + Assert.Throws( + () => new SnapshotableRepository(_factory, null, _eventStore)); + } + + [Test] + public void EventStoreCanNotBeNull() + { + Assert.Throws( + () => new SnapshotableRepository(_factory, _unitOfWork, null)); + } + } + + [TestFixture] + public class WithEmptyStoreAndEmptyUnitOfWorkAndNoSnapshot + { + SnapshotableRepository _sut; + UnitOfWork _unitOfWork; + Model _model; + + [SetUp] + public void SetUp() + { + _model = new Model(); + _unitOfWork = new UnitOfWork(); + _sut = new RepositoryScenarioBuilder(). + WithUnitOfWork(_unitOfWork). + BuildForSnapshotableRepository(); + } + + [Test] + public void GetThrows() + { + var exception = + Assert.Throws(() => _sut.GetAsync(_model.UnknownIdentifier)); + Assert.That(exception.Identifier, Is.EqualTo(_model.UnknownIdentifier)); + Assert.That(exception.ClrType, Is.EqualTo(typeof(SnapshotableAggregateRootEntityStub))); + } + + [Test] + public void GetOptionalReturnsEmpty() + { + var result = _sut.GetOptionalAsync(_model.UnknownIdentifier); + + Assert.That(result, Is.EqualTo(Optional.Empty)); + } + + [Test] + public void AddAttachesToUnitOfWork() + { + var root = SnapshotableAggregateRootEntityStub.Factory(); + + _sut.Add(_model.KnownIdentifier, root); + + Aggregate aggregate; + var result = _unitOfWork.TryGet(_model.KnownIdentifier, out aggregate); + Assert.That(result, Is.True); + Assert.That(aggregate.Identifier, Is.EqualTo(_model.KnownIdentifier)); + Assert.That(aggregate.Root, Is.SameAs(root)); + } + } + + + [TestFixture] + public class WithEmptyStoreAndEmptyUnitOfWorkAndSnapshot + { + SnapshotableRepository _sut; + UnitOfWork _unitOfWork; + Model _model; + + [SetUp] + public void SetUp() + { + _model = new Model(); + _unitOfWork = new UnitOfWork(); + _sut = new RepositoryScenarioBuilder(). + WithUnitOfWork(_unitOfWork). + ScheduleSnapshots(new Snapshot(_model.KnownIdentifier, 100, new object())). + BuildForSnapshotableRepository(); + } + + [Test] + public void GetThrows() + { + var exception = + Assert.Throws(() => _sut.GetAsync(_model.UnknownIdentifier)); + Assert.That(exception.Identifier, Is.EqualTo(_model.UnknownIdentifier)); + Assert.That(exception.ClrType, Is.EqualTo(typeof(SnapshotableAggregateRootEntityStub))); + } + + [Test] + public void GetOptionalReturnsEmpty() + { + var result = _sut.GetOptionalAsync(_model.UnknownIdentifier); + + Assert.That(result, Is.EqualTo(Optional.Empty)); + } + + [Test] + public void AddAttachesToUnitOfWork() + { + var root = SnapshotableAggregateRootEntityStub.Factory(); + + _sut.Add(_model.KnownIdentifier, root); + + Aggregate aggregate; + var result = _unitOfWork.TryGet(_model.KnownIdentifier, out aggregate); + Assert.That(result, Is.True); + Assert.That(aggregate.Identifier, Is.EqualTo(_model.KnownIdentifier)); + Assert.That(aggregate.Root, Is.SameAs(root)); + } + } + + [TestFixture] + public class WithEmptyStoreAndFilledUnitOfWorkAndNoSnapshot + { + SnapshotableRepository _sut; + UnitOfWork _unitOfWork; + SnapshotableAggregateRootEntityStub _root; + Model _model; + + [SetUp] + public void SetUp() + { + _model = new Model(); + _root = SnapshotableAggregateRootEntityStub.Factory(); + _unitOfWork = new UnitOfWork(); + _unitOfWork.Attach(new Aggregate(_model.KnownIdentifier, 0, _root)); + _sut = new RepositoryScenarioBuilder().WithUnitOfWork(_unitOfWork).BuildForSnapshotableRepository(); + } + + [Test] + public void GetThrowsForUnknownId() + { + var exception = + Assert.Throws(() => _sut.GetAsync(_model.UnknownIdentifier)); + Assert.That(exception.Identifier, Is.EqualTo(_model.UnknownIdentifier)); + Assert.That(exception.ClrType, Is.EqualTo(typeof(SnapshotableAggregateRootEntityStub))); + } + + [Test] + public void GetReturnsRootOfKnownId() + { + var result = _sut.GetAsync(_model.KnownIdentifier); + + Assert.That(result, Is.SameAs(_root)); + } + + [Test] + public void GetOptionalReturnsEmptyForUnknownId() + { + var result = _sut.GetOptionalAsync(_model.UnknownIdentifier); + + Assert.That(result, Is.EqualTo(Optional.Empty)); + } + + [Test] + public void GetOptionalReturnsRootForKnownId() + { + var result = _sut.GetOptionalAsync(_model.KnownIdentifier); + + Assert.That(result, Is.EqualTo(new Optional(_root))); + } + } + + [TestFixture] + public class WithEmptyStoreAndFilledUnitOfWorkAndSnapshot + { + SnapshotableRepository _sut; + UnitOfWork _unitOfWork; + SnapshotableAggregateRootEntityStub _root; + Model _model; + + [SetUp] + public void SetUp() + { + _model = new Model(); + _root = SnapshotableAggregateRootEntityStub.Factory(); + _unitOfWork = new UnitOfWork(); + _unitOfWork.Attach(new Aggregate(_model.KnownIdentifier, 0, _root)); + _sut = new RepositoryScenarioBuilder(). + WithUnitOfWork(_unitOfWork). + ScheduleSnapshots(new Snapshot(_model.KnownIdentifier, 100, new object())). + BuildForSnapshotableRepository(); + } + + [Test] + public void GetThrowsForUnknownId() + { + var exception = + Assert.Throws(() => _sut.GetAsync(_model.UnknownIdentifier)); + Assert.That(exception.Identifier, Is.EqualTo(_model.UnknownIdentifier)); + Assert.That(exception.ClrType, Is.EqualTo(typeof(SnapshotableAggregateRootEntityStub))); + } + + [Test] + public void GetReturnsRootOfKnownId() + { + var result = _sut.GetAsync(_model.KnownIdentifier); + + Assert.That(result, Is.SameAs(_root)); + } + + [Test] + public void GetReturnsRootOfKnownIdNotRestoredFromSnapshot() + { + var result = _sut.GetAsync(_model.KnownIdentifier); + + Assert.That(result.RecordedSnapshot, Is.Null); + } + + [Test] + public void GetOptionalReturnsEmptyForUnknownId() + { + var result = _sut.GetOptionalAsync(_model.UnknownIdentifier); + + Assert.That(result, Is.EqualTo(Optional.Empty)); + } + + [Test] + public void GetOptionalReturnsRootForKnownId() + { + var result = _sut.GetOptionalAsync(_model.KnownIdentifier); + + Assert.That(result, Is.EqualTo(new Optional(_root))); + } + + [Test] + public void GetOptionalReturnsRootForKnownIdNotRestoredFromSnapshot() + { + var result = _sut.GetOptionalAsync(_model.KnownIdentifier); + + Assert.That(result.Value.RecordedSnapshot, Is.Null); + } + } + + [TestFixture] + public class WithFilledStoreAndNoSnapshot + { + SnapshotableRepository _sut; + UnitOfWork _unitOfWork; + Model _model; + + [SetUp] + public void SetUp() + { + _model = new Model(); + _unitOfWork = new UnitOfWork(); + _sut = new RepositoryScenarioBuilder(). + WithUnitOfWork(_unitOfWork). + ScheduleAppendToStream(_model.KnownIdentifier, new EventStub(1)). + BuildForSnapshotableRepository(); + } + + [Test] + public void GetThrowsForUnknownId() + { + var exception = + Assert.Throws(() => _sut.GetAsync(_model.UnknownIdentifier)); + Assert.That(exception.Identifier, Is.EqualTo(_model.UnknownIdentifier)); + Assert.That(exception.ClrType, Is.EqualTo(typeof(SnapshotableAggregateRootEntityStub))); + } + + [Test] + public void GetReturnsRootOfKnownId() + { + var result = _sut.GetAsync(_model.KnownIdentifier); + + Assert.That(result.RecordedEvents, Is.EquivalentTo(new[] { new EventStub(1) })); + } + + [Test] + public void GetOptionalReturnsEmptyForUnknownId() + { + var result = _sut.GetOptionalAsync(_model.UnknownIdentifier); + + Assert.That(result, Is.EqualTo(Optional.Empty)); + } + + [Test] + public void GetOptionalReturnsRootForKnownId() + { + var result = _sut.GetOptionalAsync(_model.KnownIdentifier); + + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value.RecordedEvents, Is.EquivalentTo(new[] { new EventStub(1) })); + } + } + + [TestFixture] + public class WithFilledStoreAndSnapshot + { + SnapshotableRepository _sut; + UnitOfWork _unitOfWork; + Model _model; + + [SetUp] + public void SetUp() + { + _model = new Model(); + _unitOfWork = new UnitOfWork(); + _sut = new RepositoryScenarioBuilder(). + WithUnitOfWork(_unitOfWork). + ScheduleAppendToStream(_model.KnownIdentifier, new EventStub(1)). + ScheduleSnapshots(new Snapshot(_model.KnownIdentifier, 1, new State(1))). + ScheduleAppendToStream(_model.KnownIdentifier, new EventStub(2)). + BuildForSnapshotableRepository(); + } + + [Test] + public void GetThrowsForUnknownId() + { + var exception = + Assert.Throws(() => _sut.GetAsync(_model.UnknownIdentifier)); + Assert.That(exception.Identifier, Is.EqualTo(_model.UnknownIdentifier)); + Assert.That(exception.ClrType, Is.EqualTo(typeof(SnapshotableAggregateRootEntityStub))); + } + + [Test] + public void GetReturnsRootOfKnownId() + { + var result = _sut.GetAsync(_model.KnownIdentifier); + + Assert.That(result.RecordedEvents, Is.EquivalentTo(new[] { new EventStub(2) })); + } + + [Test] + public void GetReturnsRootRestoredFromSnapshot() + { + var result = _sut.GetAsync(_model.KnownIdentifier); + + Assert.That(result.RecordedSnapshot, Is.EqualTo(new State(1))); + } + + [Test] + public void GetOptionalReturnsEmptyForUnknownId() + { + var result = _sut.GetOptionalAsync(_model.UnknownIdentifier); + + Assert.That(result, Is.EqualTo(Optional.Empty)); + } + + [Test] + public void GetOptionalReturnsRootForKnownId() + { + var result = _sut.GetOptionalAsync(_model.KnownIdentifier); + + Assert.That(result.HasValue, Is.True); + Assert.That(result.Value.RecordedEvents, Is.EquivalentTo(new[] { new EventStub(2) })); + } + + [Test] + public void GetOptionalReturnsRootForKnownIdRestoredFromSnapshot() + { + var result = _sut.GetOptionalAsync(_model.KnownIdentifier); + + Assert.That(result.Value.RecordedSnapshot, Is.EqualTo(new State(1))); + } + + class State + { + bool Equals(State other) + { + return _value == other._value; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((State) obj); + } + + public override int GetHashCode() + { + return _value; + } + + readonly int _value; + + public State(int value) + { + _value = value; + } + } + } + } +} \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/packages.config b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/packages.config new file mode 100644 index 0000000..a5ae2e2 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.sln b/src/SqlStreamStore/AggregateSource.SqlStreamStore.sln new file mode 100644 index 0000000..964265d --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.sln @@ -0,0 +1,53 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AggregateSource.SqlStreamStore", "AggregateSource.SqlStreamStore\AggregateSource.SqlStreamStore.csproj", "{06AA30D7-8CD5-4EC4-99F2-50129DEE0D57}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AggregateSource", "..\Core\AggregateSource\AggregateSource.csproj", "{CC3FCC99-9E18-45DE-9B39-76031D45624D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AggregateSource.SqlStreamStore.Tests", "AggregateSource.SqlStreamStore.Tests\AggregateSource.SqlStreamStore.Tests.csproj", "{7E6BACB9-7804-429A-A71E-78B12D0AB786}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{C15AD8E5-A792-4C07-B792-BD6DED990E68}" + ProjectSection(SolutionItems) = preProject + .nuget\packages.config = .nuget\packages.config + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release 4.0|Any CPU = Release 4.0|Any CPU + Release 4.5|Any CPU = Release 4.5|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {06AA30D7-8CD5-4EC4-99F2-50129DEE0D57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {06AA30D7-8CD5-4EC4-99F2-50129DEE0D57}.Debug|Any CPU.Build.0 = Debug|Any CPU + {06AA30D7-8CD5-4EC4-99F2-50129DEE0D57}.Release 4.0|Any CPU.ActiveCfg = Release|Any CPU + {06AA30D7-8CD5-4EC4-99F2-50129DEE0D57}.Release 4.0|Any CPU.Build.0 = Release|Any CPU + {06AA30D7-8CD5-4EC4-99F2-50129DEE0D57}.Release 4.5|Any CPU.ActiveCfg = Release|Any CPU + {06AA30D7-8CD5-4EC4-99F2-50129DEE0D57}.Release 4.5|Any CPU.Build.0 = Release|Any CPU + {06AA30D7-8CD5-4EC4-99F2-50129DEE0D57}.Release|Any CPU.ActiveCfg = Release|Any CPU + {06AA30D7-8CD5-4EC4-99F2-50129DEE0D57}.Release|Any CPU.Build.0 = Release|Any CPU + {CC3FCC99-9E18-45DE-9B39-76031D45624D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC3FCC99-9E18-45DE-9B39-76031D45624D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC3FCC99-9E18-45DE-9B39-76031D45624D}.Release 4.0|Any CPU.ActiveCfg = Release|Any CPU + {CC3FCC99-9E18-45DE-9B39-76031D45624D}.Release 4.0|Any CPU.Build.0 = Release|Any CPU + {CC3FCC99-9E18-45DE-9B39-76031D45624D}.Release 4.5|Any CPU.ActiveCfg = Release|Any CPU + {CC3FCC99-9E18-45DE-9B39-76031D45624D}.Release 4.5|Any CPU.Build.0 = Release|Any CPU + {CC3FCC99-9E18-45DE-9B39-76031D45624D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC3FCC99-9E18-45DE-9B39-76031D45624D}.Release|Any CPU.Build.0 = Release|Any CPU + {7E6BACB9-7804-429A-A71E-78B12D0AB786}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E6BACB9-7804-429A-A71E-78B12D0AB786}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E6BACB9-7804-429A-A71E-78B12D0AB786}.Release 4.0|Any CPU.ActiveCfg = Release|Any CPU + {7E6BACB9-7804-429A-A71E-78B12D0AB786}.Release 4.0|Any CPU.Build.0 = Release|Any CPU + {7E6BACB9-7804-429A-A71E-78B12D0AB786}.Release 4.5|Any CPU.ActiveCfg = Release|Any CPU + {7E6BACB9-7804-429A-A71E-78B12D0AB786}.Release 4.5|Any CPU.Build.0 = Release|Any CPU + {7E6BACB9-7804-429A-A71E-78B12D0AB786}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E6BACB9-7804-429A-A71E-78B12D0AB786}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.sln.DotSettings b/src/SqlStreamStore/AggregateSource.SqlStreamStore.sln.DotSettings new file mode 100644 index 0000000..cde9ace --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.sln.DotSettings @@ -0,0 +1,6 @@ + + False + False + True + True + True \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore/AggregateSource.SqlStreamStore.csproj b/src/SqlStreamStore/AggregateSource.SqlStreamStore/AggregateSource.SqlStreamStore.csproj new file mode 100644 index 0000000..34cbcf4 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore/AggregateSource.SqlStreamStore.csproj @@ -0,0 +1,78 @@ + + + + + Debug + AnyCPU + {06AA30D7-8CD5-4EC4-99F2-50129DEE0D57} + Library + Properties + AggregateSource.SqlStreamStore + AggregateSource.SqlStreamStore + v4.6.2 + 512 + + + + true + full + false + bin\Debug\net45 + DEBUG;TRACE;NET45 + prompt + 4 + bin\Debug\net45\AggregateSource.SqlStreamStore.XML + v4.6.2 + + + pdbonly + true + bin\Release\net45 + TRACE;NET45 + prompt + 4 + bin\Releas45\AggregateSource.SqlStreamStore.XML + v4.6.2 + + + + ..\packages\SqlStreamStore.1.0.2\lib\net461\SqlStreamStore.dll + + + ..\packages\SqlStreamStore.MsSql.1.0.2\lib\net461\SqlStreamStore.MsSql.dll + + + + + + + + Properties\SharedAssemblyInfo.cs + + + Properties\SharedVersionInfo.cs + + + + + + + {cc3fcc99-9e18-45de-9b39-76031d45624d} + AggregateSource + + + + + + + + + + + \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore/Properties/AssemblyInfo.cs b/src/SqlStreamStore/AggregateSource.SqlStreamStore/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..63f76f3 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Reflection; + +[assembly: AssemblyTitle("AggregateSource.SqlStreamStore")] +[assembly: AssemblyDescription("AggregateSource integration with SqlStreamStore.")] \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore/Repository.cs b/src/SqlStreamStore/AggregateSource.SqlStreamStore/Repository.cs new file mode 100644 index 0000000..642dbc7 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore/Repository.cs @@ -0,0 +1,131 @@ +using System; +using System.Linq; +using System.Threading; +using AggregateSource; +using SqlStreamStore; +using SqlStreamStore.Streams; +using StreamStoreStore.Json; + +namespace SSS +{ + /// + /// Represents a virtual collection of . + /// + /// The type of the aggregate root in this collection. + public class Repository : IAsyncRepository where TAggregateRoot : IAggregateRootEntity + { + readonly Func _rootFactory; + readonly UnitOfWork _unitOfWork; + readonly IStreamStore _eventStore; + + /// + /// Initializes a new instance of the class. + /// + /// The aggregate root entity factory. + /// The unit of work to interact with. + /// The event store to use. + /// Thrown when the or or is null. + public Repository(Func rootFactory, UnitOfWork unitOfWork, IStreamStore eventStore) + { + if (rootFactory == null) throw new ArgumentNullException("rootFactory"); + if (unitOfWork == null) throw new ArgumentNullException("unitOfWork"); + if (eventStore == null) throw new ArgumentNullException("eventStore"); + _rootFactory = rootFactory; + _unitOfWork = unitOfWork; + _eventStore = eventStore; + } + + /// + /// Gets the aggregate root entity factory. + /// + /// + /// The aggregate root entity factory. + /// + public Func RootFactory + { + get { return _rootFactory; } + } + + /// + /// Gets the unit of work. + /// + /// + /// The unit of work. + /// + public UnitOfWork UnitOfWork + { + get { return _unitOfWork; } + } + + /// + /// Gets the event store to use. + /// + /// + /// The event store to use. + /// + public IStreamStore EventStore + { + get { return _eventStore; } + } + + /// + /// Gets the aggregate root entity associated with the specified aggregate identifier. + /// + /// The aggregate identifier. + /// An instance of . + /// Thrown when an aggregate is not found. + public async System.Threading.Tasks.Task GetAsync(string identifier) + { + var result = await GetOptionalAsync(identifier); + if (!result.HasValue) + throw new AggregateNotFoundException(identifier, typeof(TAggregateRoot)); + return result.Value; + } + + /// + /// Attempts to get the aggregate root entity associated with the aggregate identifier. + /// + /// The aggregate identifier. + /// The found , or empty if not found. + public async System.Threading.Tasks.Task> GetOptionalAsync(string identifier) + { + Aggregate aggregate; + if (UnitOfWork.TryGet(identifier, out aggregate)) + { + return new Optional((TAggregateRoot) aggregate.Root); + } + var page = await EventStore.ReadStreamForwards(identifier, StreamVersion.Start, 100); + + if (page.Status == PageReadStatus.StreamNotFound) + return Optional.Empty; + + var events = page.Messages.ToList(); + + while (!page.IsEnd) + { + events.AddRange(page.Messages); + } + + var root = RootFactory(); + var eventObjects = events.Select(message => + { + var eventType = Type.GetType(message.Type); + var eventData = message.GetJsonData().GetAwaiter().GetResult(); + return SimpleJson.DeserializeObject(eventData, eventType); + }); + root.Initialize(eventObjects); + UnitOfWork.Attach(new Aggregate(identifier, page.LastStreamVersion, root)); + return new Optional(root); + } + + /// + /// Adds the aggregate root entity to this collection using the specified aggregate identifier. + /// + /// The aggregate identifier. + /// The aggregate root entity. + public void Add(string identifier, TAggregateRoot root) + { + UnitOfWork.Attach(new Aggregate(identifier, 0, root)); + } + } +} diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore/Snapshots/SnapshotableRepository.cs b/src/SqlStreamStore/AggregateSource.SqlStreamStore/Snapshots/SnapshotableRepository.cs new file mode 100644 index 0000000..b1fd5f1 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore/Snapshots/SnapshotableRepository.cs @@ -0,0 +1,137 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using AggregateSource; +using SqlStreamStore; + +namespace SSS.Snapshots +{ + /// + /// Represents a virtual collection of . + /// + /// The type of the aggregate root in this collection. + public class SnapshotableRepository : IRepository + where TAggregateRoot : IAggregateRootEntity, ISnapshotable + { + /// + /// Initializes a new instance of the class. + /// + /// The aggregate root entity factory. + /// The unit of work to interact with. + /// The event store to use. + /// Thrown when the or or is null. + public SnapshotableRepository(Func rootFactory, UnitOfWork unitOfWork, IStreamStore eventStore) + { + RootFactory = rootFactory ?? throw new ArgumentNullException(nameof(rootFactory)); + UnitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork)); + Store = eventStore ?? throw new ArgumentNullException(nameof(eventStore)); + } + + /// + /// Gets the aggregate root entity factory. + /// + /// + /// The aggregate root entity factory. + /// + public Func RootFactory { get; } + + /// + /// Gets the unit of work. + /// + /// + /// The unit of work. + /// + public UnitOfWork UnitOfWork { get; } + + /// + /// Gets the event store to use. + /// + /// + /// The event store to use. + /// + public IStreamStore Store { get; } + + /// + /// Gets the aggregate root entity associated with the specified aggregate identifier. + /// + /// The aggregate identifier. + /// An instance of . + /// Thrown when an aggregate is not found. + public async Task GetAsync(string identifier) + { + var result = await GetOptionalAsync(identifier); + if (!result.HasValue) + throw new AggregateNotFoundException(identifier, typeof (TAggregateRoot)); + return result.Value; + } + + /// + /// Attempts to get the aggregate root entity associated with the aggregate identifier. + /// + /// The aggregate identifier. + /// The found , or empty if not found. + public async System.Threading.Tasks.Task> GetOptionalAsync(string identifier) + { + Aggregate aggregate; + if (UnitOfWork.TryGet(identifier, out aggregate)) + { + return new Optional((TAggregateRoot) aggregate.Root); + } + + var snapshot = Store.Advanced.GetSnapshot(identifier, Int32.MaxValue); + if (snapshot != null) + { + using (var stream = Store.OpenStream(snapshot, Int32.MaxValue)) + { + var root = RootFactory(); + root.RestoreSnapshot(snapshot.Payload); + root.Initialize(stream.CommittedEvents.Select(eventMessage => eventMessage.Body)); + UnitOfWork.Attach(new Aggregate(identifier, stream.StreamRevision, root)); + return new Optional(root); + } + } + using (var stream = Store.OpenStream(identifier, minRevision: 0)) + { + if (stream.StreamRevision == 0) + return Optional.Empty; + + var root = RootFactory(); + root.Initialize(stream.CommittedEvents.Select(eventMessage => eventMessage.Body)); + UnitOfWork.Attach(new Aggregate(identifier, stream.StreamRevision, root)); + return new Optional(root); + } + + //Aggregate aggregate; + //if (UnitOfWork.TryGet(identifier, out aggregate)) + //{ + // return new Optional((TAggregateRoot)aggregate.Root); + //} + //var page = await EventStore.ReadStreamForwards(identifier, StreamVersion.Start, 100); + + //if (page.Status == PageReadStatus.StreamNotFound) + // return Optional.Empty; + + //var events = page.Messages.ToList(); + + //while (!page.IsEnd) + //{ + // events.AddRange(page.Messages); + //} + + //var root = RootFactory(); + //root.Initialize(events.Select(message => message.GetJsonDataAs())); + //UnitOfWork.Attach(new Aggregate(identifier, page.LastStreamVersion, root)); + //return new Optional(root); + } + + /// + /// Adds the aggregate root entity to this collection using the specified aggregate identifier. + /// + /// The aggregate identifier. + /// The aggregate root entity. + public void Add(string identifier, TAggregateRoot root) + { + UnitOfWork.Attach(new Aggregate(identifier, 0, root)); + } + } +} \ No newline at end of file diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore/packages.config b/src/SqlStreamStore/AggregateSource.SqlStreamStore/packages.config new file mode 100644 index 0000000..17a74f8 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From a58d1198c0ffda8c3902ab0249aa3793ce7096ed Mon Sep 17 00:00:00 2001 From: Koen Metsu Date: Thu, 31 Aug 2017 14:23:26 +0200 Subject: [PATCH 3/6] Add sqlstreamstore to build scripts --- winbuild/build.proj | 4 +++- winbuild/run_me_first.proj | 2 ++ winbuild/test.proj | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/winbuild/build.proj b/winbuild/build.proj index 18d373c..6376e78 100644 --- a/winbuild/build.proj +++ b/winbuild/build.proj @@ -35,7 +35,9 @@ $(SourcePath)\EventStore\AggregateSource.EventStore.Tests\AggregateSource.EventStore.Tests.csproj; $(SourcePath)\EventStore\AggregateSource.EventStore.IntegratedTests\AggregateSource.EventStore.IntegratedTests.csproj; $(SourcePath)\NEventStore\AggregateSource.NEventStore\AggregateSource.NEventStore.csproj; - $(SourcePath)\NEventStore\AggregateSource.NEventStore.Tests\AggregateSource.NEventStore.Tests.csproj;" + $(SourcePath)\NEventStore\AggregateSource.NEventStore.Tests\AggregateSource.NEventStore.Tests.csproj; + $(SourcePath)\SqlStreamStore\AggregateSource.SqlStreamStore\AggregateSource.SqlStreamStore.csproj; + $(SourcePath)\SqlStreamStore\AggregateSource.SqlStreamStore.Tests\AggregateSource.SqlStreamStore.Tests.csproj;" BuildInParallel="true" Properties="Configuration=Release;OutputPath=$(MSBuildProjectDirectory)\output\net45" UnloadProjectsOnCompletion="true"/> diff --git a/winbuild/run_me_first.proj b/winbuild/run_me_first.proj index 23e5a34..a732011 100644 --- a/winbuild/run_me_first.proj +++ b/winbuild/run_me_first.proj @@ -22,6 +22,7 @@ + @@ -35,6 +36,7 @@ + diff --git a/winbuild/test.proj b/winbuild/test.proj index 902ef88..1b4d90a 100644 --- a/winbuild/test.proj +++ b/winbuild/test.proj @@ -20,6 +20,7 @@ $(MSBuildProjectDirectory)\output\net45\AggregateSource.EventStore.IntegratedTests.dll; $(MSBuildProjectDirectory)\output\net45\SampleSource.dll; $(MSBuildProjectDirectory)\output\net45\AggregateSource.NEventStore.Tests.dll; + $(MSBuildProjectDirectory)\output\net45\AggregateSource.SqlStreamStore.Tests.dll; "> From 7283504e025ba594c8ec643c391034684555115c Mon Sep 17 00:00:00 2001 From: Koen Metsu Date: Thu, 31 Aug 2017 14:26:14 +0200 Subject: [PATCH 4/6] Remove unused files --- ...ggregateSource.SqlStreamStore.Tests.csproj | 9 +- .../Framework/Catch.cs | 18 - .../Framework/RepositoryScenarioBuilder.cs | 2 - .../Framework/Snapshots/SnapshotStateStub.cs | 32 -- .../SnapshotableAggregateRootEntityStub.cs | 39 -- .../Snapshots/SnapshotableRepositoryTests.cs | 424 ------------------ .../AggregateSource.SqlStreamStore.csproj | 6 +- .../Snapshots/SnapshotableRepository.cs | 137 ------ 8 files changed, 4 insertions(+), 663 deletions(-) delete mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Catch.cs delete mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Snapshots/SnapshotStateStub.cs delete mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/Snapshots/SnapshotableAggregateRootEntityStub.cs delete mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Snapshots/SnapshotableRepositoryTests.cs delete mode 100644 src/SqlStreamStore/AggregateSource.SqlStreamStore/Snapshots/SnapshotableRepository.cs diff --git a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/AggregateSource.SqlStreamStore.Tests.csproj b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/AggregateSource.SqlStreamStore.Tests.csproj index 1ceafce..821d5ab 100644 --- a/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/AggregateSource.SqlStreamStore.Tests.csproj +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/AggregateSource.SqlStreamStore.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -58,11 +58,8 @@ Properties\SharedVersionInfo.cs - - - @@ -83,9 +80,7 @@ - - - +