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/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..821d5ab --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/AggregateSource.SqlStreamStore.Tests.csproj @@ -0,0 +1,92 @@ + + + + + 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/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..27cef5d --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore.Tests/Framework/RepositoryScenarioBuilder.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using AggregateSource; +using SqlStreamStore; +using SqlStreamStore.Streams; +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, // Uses AssemblyQualifiedName for simplicity, use a map instead. + jsonData: SimpleJson.SerializeObject(o))) + .ToList(); + + store.AppendToStream(new StreamId(stream), ExpectedVersion.Any, messages.ToArray(), CancellationToken.None).GetAwaiter().GetResult(); + }); + 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); + } + + 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/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/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..73b6e69 --- /dev/null +++ b/src/SqlStreamStore/AggregateSource.SqlStreamStore/AggregateSource.SqlStreamStore.csproj @@ -0,0 +1,76 @@ + + + + + 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..e4a5461 --- /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); // Uses AssemblyQualifiedName for simplicity, use a map instead. + 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/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 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 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; ">