diff --git a/Documentation/Metadata.md b/Documentation/Metadata.md index beb567fe8..8b687f121 100644 --- a/Documentation/Metadata.md +++ b/Documentation/Metadata.md @@ -50,7 +50,8 @@ some of its NuGet packages. is replace with the name of the header. E.g. the `request_header[Connection]` might contain the value `Keep-Alive`. * **AddUriMetadataProvider** - * `request_uri` - Adds the OWIN request URI. + * `request_uri` - OWIN request URI. + * `request_method` - OWIN request method. * **AddUserHostAddressMetadataProvider** * `user_host_address` - The provider tries to find the correct user host address by inspecting request headers, i.e., if you have diff --git a/README.md b/README.md index 100f71932..e729c79c3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ # EventFlow + +[![NuGet Status](http://img.shields.io/nuget/v/EventFlow.svg?style=flat)](https://www.nuget.org/packages/EventFlow/) +![Build status](https://ci.appveyor.com/api/projects/status/51yvhvbd909e4o82/branch/develop?svg=true) +![License](https://img.shields.io/github/license/rasmus/eventflow.svg) + EventFlow is a basic CQRS+ES framework designed to be easy to use. Have a look at our [Getting started guide](./Documentation/GettingStarted.md). @@ -63,6 +68,7 @@ using (var resolver = EventFlowOptions.New var testReadModel = await readModelStore.GetAsync(id); } ``` + ## Useful links * [CQRS Journey by Microsoft](https://msdn.microsoft.com/en-us/library/jj554200.aspx) diff --git a/Source/EventFlow.EventStores.MsSql/MssqlEventStore.cs b/Source/EventFlow.EventStores.MsSql/MssqlEventStore.cs index 57ea9111a..b22228b79 100644 --- a/Source/EventFlow.EventStores.MsSql/MssqlEventStore.cs +++ b/Source/EventFlow.EventStores.MsSql/MssqlEventStore.cs @@ -67,6 +67,11 @@ protected override async Task> Commit IReadOnlyCollection serializedEvents, CancellationToken cancellationToken) { + if (!serializedEvents.Any()) + { + return new ICommittedDomainEvent[] {}; + } + var batchId = Guid.NewGuid(); var aggregateType = typeof(TAggregate); var aggregateName = aggregateType.Name.Replace("Aggregate", string.Empty); diff --git a/Source/EventFlow.Owin/MetadataProviders/AddUriMetadataProvider.cs b/Source/EventFlow.Owin/MetadataProviders/AddUriMetadataProvider.cs index 552fd3ba5..7baf8f503 100644 --- a/Source/EventFlow.Owin/MetadataProviders/AddUriMetadataProvider.cs +++ b/Source/EventFlow.Owin/MetadataProviders/AddUriMetadataProvider.cs @@ -46,6 +46,7 @@ public IEnumerable> ProvideMetadata( // TODO: Handle X-Forwarded-Proto header yield return new KeyValuePair("request_uri", _owinContext.Request.Uri.ToString()); + yield return new KeyValuePair("request_method", _owinContext.Request.Method.ToUpperInvariant()); } } } diff --git a/Source/EventFlow.TestHelpers/Aggregates/Test/Commands/DoesNothingCommand.cs b/Source/EventFlow.TestHelpers/Aggregates/Test/Commands/DoesNothingCommand.cs new file mode 100644 index 000000000..c9bdbb02b --- /dev/null +++ b/Source/EventFlow.TestHelpers/Aggregates/Test/Commands/DoesNothingCommand.cs @@ -0,0 +1,43 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Rasmus Mikkelsen +// https://github.com/rasmus/EventFlow +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using System.Threading; +using System.Threading.Tasks; +using EventFlow.Commands; + +namespace EventFlow.TestHelpers.Aggregates.Test.Commands +{ + public class DoesNothingCommand : Command + { + public DoesNothingCommand(string id) : base(id) + { + } + } + + public class DoesNothingCommandHandler : CommandHandler + { + public override Task ExecuteAsync(TestAggregate aggregate, DoesNothingCommand command, CancellationToken cancellationToken) + { + return Task.FromResult(0); + } + } +} diff --git a/Source/EventFlow.TestHelpers/EventFlow.TestHelpers.csproj b/Source/EventFlow.TestHelpers/EventFlow.TestHelpers.csproj index da4a06d51..ea69a84da 100644 --- a/Source/EventFlow.TestHelpers/EventFlow.TestHelpers.csproj +++ b/Source/EventFlow.TestHelpers/EventFlow.TestHelpers.csproj @@ -61,6 +61,7 @@ + diff --git a/Source/EventFlow.TestHelpers/Suites/EventStoreSuite.cs b/Source/EventFlow.TestHelpers/Suites/EventStoreSuite.cs index feb7f1fed..cf803be93 100644 --- a/Source/EventFlow.TestHelpers/Suites/EventStoreSuite.cs +++ b/Source/EventFlow.TestHelpers/Suites/EventStoreSuite.cs @@ -135,6 +135,17 @@ public async Task AggregateEventStreamsAreSeperate() aggregate2.Version.Should().Be(2); } + [Test] + public async Task NoEventsEmittedIsOk() + { + // Arrange + var id = A(); + var aggregate = await EventStore.LoadAggregateAsync(id, CancellationToken.None).ConfigureAwait(false); + + // Act + await aggregate.CommitAsync(EventStore, CancellationToken.None).ConfigureAwait(false); + } + [Test] public async Task OptimisticConcurrency() { diff --git a/Source/EventFlow/EventStores/EventStore.cs b/Source/EventFlow/EventStores/EventStore.cs index ba80bbf27..efb474da4 100644 --- a/Source/EventFlow/EventStores/EventStore.cs +++ b/Source/EventFlow/EventStores/EventStore.cs @@ -63,6 +63,11 @@ public virtual async Task> StoreAsync