From 8bffdb647e3e4cf298a66838a7e995b3b0cc7b24 Mon Sep 17 00:00:00 2001 From: JMolenkamp Date: Sun, 11 Jan 2026 01:02:26 +0100 Subject: [PATCH 1/3] test(mssql-read-model-store): add tests for when not deriving from IMssqlReadModel --- .../ReadStores/MsSqlReadModelStoreTests.cs | 6 +- .../ObsoleteMsSqlReadModelStoreTests.cs | 78 ++++++++++++++++++ .../MsSqlThingyGetQueryHandler.cs | 28 +------ .../MsSqlThingyGetQueryHandlerBase.cs | 58 +++++++++++++ .../MsSqlThingyGetVersionQueryHandler.cs | 27 +------ .../MsSqlThingyGetVersionQueryHandlerBase.cs | 57 +++++++++++++ .../ObsoleteMsSqlThingyGetQueryHandler.cs | 33 ++++++++ ...soleteMsSqlThingyGetVersionQueryHandler.cs | 34 ++++++++ .../ReadStores/ReadModels/IThingyReadModel.cs | 15 ++++ .../ReadModels/MsSqlThingyReadModel.cs | 14 ++-- .../ObsoleteMsSqlThingyReadModel.cs | 81 +++++++++++++++++++ 11 files changed, 372 insertions(+), 59 deletions(-) create mode 100644 Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ObsoleteMsSqlReadModelStoreTests.cs create mode 100644 Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetQueryHandlerBase.cs create mode 100644 Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetVersionQueryHandlerBase.cs create mode 100644 Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/ObsoleteMsSqlThingyGetQueryHandler.cs create mode 100644 Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/ObsoleteMsSqlThingyGetVersionQueryHandler.cs create mode 100644 Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/IThingyReadModel.cs create mode 100644 Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/ObsoleteMsSqlThingyReadModel.cs diff --git a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/MsSqlReadModelStoreTests.cs b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/MsSqlReadModelStoreTests.cs index 26c5b37d5..764129ea6 100644 --- a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/MsSqlReadModelStoreTests.cs +++ b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/MsSqlReadModelStoreTests.cs @@ -20,8 +20,6 @@ // 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; -using System.Threading; using EventFlow.Extensions; using EventFlow.MsSql.EventStores; using EventFlow.MsSql.Extensions; @@ -33,6 +31,8 @@ using EventFlow.TestHelpers.Suites; using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using System; +using System.Threading; namespace EventFlow.MsSql.Tests.IntegrationTests.ReadStores { @@ -46,7 +46,7 @@ public class MsSqlReadModelStoreTests : TestSuiteForReadModelStore protected override IServiceProvider Configure(IEventFlowOptions eventFlowOptions) { _testDatabase = MsSqlHelpz.CreateDatabase("eventflow"); - + eventFlowOptions .RegisterServices(sr => sr.AddTransient(typeof(ThingyMessageLocator))) .ConfigureMsSql(MsSqlConfiguration.New.SetConnectionString(_testDatabase.ConnectionString.Value)) diff --git a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ObsoleteMsSqlReadModelStoreTests.cs b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ObsoleteMsSqlReadModelStoreTests.cs new file mode 100644 index 000000000..fe9e9c530 --- /dev/null +++ b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ObsoleteMsSqlReadModelStoreTests.cs @@ -0,0 +1,78 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015-2025 Rasmus Mikkelsen +// https://github.com/eventflow/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 EventFlow.Extensions; +using EventFlow.MsSql.EventStores; +using EventFlow.MsSql.Extensions; +using EventFlow.MsSql.Tests.IntegrationTests.ReadStores.QueryHandlers; +using EventFlow.MsSql.Tests.IntegrationTests.ReadStores.ReadModels; +using EventFlow.TestHelpers; +using EventFlow.TestHelpers.Aggregates.Entities; +using EventFlow.TestHelpers.MsSql; +using EventFlow.TestHelpers.Suites; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using System; +using System.Threading; + +namespace EventFlow.MsSql.Tests.IntegrationTests.ReadStores +{ + [Category(Categories.Integration)] + public class ObsoleteMsSqlReadModelStoreTests : TestSuiteForReadModelStore + { + protected override Type ReadModelType { get; } = typeof(ObsoleteMsSqlThingyReadModel); + + private IMsSqlDatabase _testDatabase; + + protected override IServiceProvider Configure(IEventFlowOptions eventFlowOptions) + { + _testDatabase = MsSqlHelpz.CreateDatabase("eventflow"); + + eventFlowOptions + .RegisterServices(sr => sr.AddTransient(typeof(ThingyMessageLocator))) + .ConfigureMsSql(MsSqlConfiguration.New.SetConnectionString(_testDatabase.ConnectionString.Value)) + .UseMssqlReadModel() + .UseMssqlReadModel() + .AddQueryHandlers( + typeof(ObsoleteMsSqlThingyGetQueryHandler), + typeof(ObsoleteMsSqlThingyGetVersionQueryHandler), + typeof(MsSqlThingyGetMessagesQueryHandler)); + + var serviceProvider = base.Configure(eventFlowOptions); + + var databaseMigrator = serviceProvider.GetRequiredService(); + EventFlowEventStoresMsSql.MigrateDatabaseAsync(databaseMigrator, CancellationToken.None).Wait(); + databaseMigrator.MigrateDatabaseUsingEmbeddedScriptsAsync( + GetType().Assembly, + null /* TODO */, + CancellationToken.None).Wait(); + + return serviceProvider; + } + + [TearDown] + public void TearDown() + { + _testDatabase.DisposeSafe(Logger, "Failed to delete database"); + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetQueryHandler.cs b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetQueryHandler.cs index 412c3cfe7..63e5e3e54 100644 --- a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetQueryHandler.cs +++ b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetQueryHandler.cs @@ -20,38 +20,14 @@ // 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.Linq; -using System.Threading; -using System.Threading.Tasks; -using EventFlow.Core; using EventFlow.MsSql.Tests.IntegrationTests.ReadStores.ReadModels; -using EventFlow.Queries; -using EventFlow.Sql.Extensions; -using EventFlow.TestHelpers.Aggregates; -using EventFlow.TestHelpers.Aggregates.Queries; namespace EventFlow.MsSql.Tests.IntegrationTests.ReadStores.QueryHandlers { - public class MsSqlThingyGetQueryHandler : IQueryHandler + public class MsSqlThingyGetQueryHandler : MsSqlThingyGetQueryHandlerBase { - private readonly IMsSqlConnection _msSqlConnection; - - public MsSqlThingyGetQueryHandler( - IMsSqlConnection msSqlConnection) - { - _msSqlConnection = msSqlConnection; - } - - public async Task ExecuteQueryAsync(ThingyGetQuery query, CancellationToken cancellationToken) + public MsSqlThingyGetQueryHandler(IMsSqlConnection msSqlConnection) : base(msSqlConnection) { - var readModels = await _msSqlConnection.QueryAsync( - Label.Named("mssql-fetch-test-read-model"), - ReadModelExtensions.GetConnectionStringName(), - cancellationToken, - "SELECT * FROM [ReadModel-ThingyAggregate] WHERE AggregateId = @AggregateId", - new { AggregateId = query.ThingyId.Value }) - .ConfigureAwait(false); - return readModels.SingleOrDefault()?.ToThingy(); } } } diff --git a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetQueryHandlerBase.cs b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetQueryHandlerBase.cs new file mode 100644 index 000000000..e15363321 --- /dev/null +++ b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetQueryHandlerBase.cs @@ -0,0 +1,58 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015-2025 Rasmus Mikkelsen +// https://github.com/eventflow/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 EventFlow.Core; +using EventFlow.MsSql.Tests.IntegrationTests.ReadStores.ReadModels; +using EventFlow.Queries; +using EventFlow.Sql.Extensions; +using EventFlow.TestHelpers.Aggregates; +using EventFlow.TestHelpers.Aggregates.Queries; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace EventFlow.MsSql.Tests.IntegrationTests.ReadStores.QueryHandlers +{ + public class MsSqlThingyGetQueryHandlerBase : IQueryHandler + where TReadModel : class, IThingyReadModel + { + private readonly IMsSqlConnection _msSqlConnection; + + public MsSqlThingyGetQueryHandlerBase( + IMsSqlConnection msSqlConnection) + { + _msSqlConnection = msSqlConnection; + } + + public async Task ExecuteQueryAsync(ThingyGetQuery query, CancellationToken cancellationToken) + { + var readModels = await _msSqlConnection.QueryAsync( + Label.Named("mssql-fetch-test-read-model"), + ReadModelExtensions.GetConnectionStringName(), + cancellationToken, + "SELECT * FROM [ReadModel-ThingyAggregate] WHERE AggregateId = @AggregateId", + new { AggregateId = query.ThingyId.Value }) + .ConfigureAwait(false); + return readModels.SingleOrDefault()?.ToThingy(); + } + } +} diff --git a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetVersionQueryHandler.cs b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetVersionQueryHandler.cs index 963277486..62d7d056d 100644 --- a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetVersionQueryHandler.cs +++ b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetVersionQueryHandler.cs @@ -20,37 +20,14 @@ // 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.Linq; -using System.Threading; -using System.Threading.Tasks; -using EventFlow.Core; using EventFlow.MsSql.Tests.IntegrationTests.ReadStores.ReadModels; -using EventFlow.Queries; -using EventFlow.Sql.Extensions; -using EventFlow.TestHelpers.Aggregates.Queries; namespace EventFlow.MsSql.Tests.IntegrationTests.ReadStores.QueryHandlers { - public class MsSqlThingyGetVersionQueryHandler : IQueryHandler + public class MsSqlThingyGetVersionQueryHandler : MsSqlThingyGetVersionQueryHandlerBase { - private readonly IMsSqlConnection _msSqlConnection; - - public MsSqlThingyGetVersionQueryHandler( - IMsSqlConnection msSqlConnection) - { - _msSqlConnection = msSqlConnection; - } - - public async Task ExecuteQueryAsync(ThingyGetVersionQuery query, CancellationToken cancellationToken) + public MsSqlThingyGetVersionQueryHandler(IMsSqlConnection msSqlConnection) : base(msSqlConnection) { - var readModels = await _msSqlConnection.QueryAsync( - Label.Named("mssql-fetch-test-read-model"), - ReadModelExtensions.GetConnectionStringName(), - cancellationToken, - "SELECT * FROM [ReadModel-ThingyAggregate] WHERE AggregateId = @AggregateId", - new { AggregateId = query.ThingyId.Value }) - .ConfigureAwait(false); - return readModels.SingleOrDefault()?.LastAggregateSequenceNumber; } } } \ No newline at end of file diff --git a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetVersionQueryHandlerBase.cs b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetVersionQueryHandlerBase.cs new file mode 100644 index 000000000..3fe8f5985 --- /dev/null +++ b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/MsSqlThingyGetVersionQueryHandlerBase.cs @@ -0,0 +1,57 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015-2025 Rasmus Mikkelsen +// https://github.com/eventflow/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 EventFlow.Core; +using EventFlow.MsSql.Tests.IntegrationTests.ReadStores.ReadModels; +using EventFlow.Queries; +using EventFlow.Sql.Extensions; +using EventFlow.TestHelpers.Aggregates.Queries; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace EventFlow.MsSql.Tests.IntegrationTests.ReadStores.QueryHandlers +{ + public class MsSqlThingyGetVersionQueryHandlerBase : IQueryHandler + where TReadModel : class, IThingyReadModel + { + private readonly IMsSqlConnection _msSqlConnection; + + public MsSqlThingyGetVersionQueryHandlerBase( + IMsSqlConnection msSqlConnection) + { + _msSqlConnection = msSqlConnection; + } + + public async Task ExecuteQueryAsync(ThingyGetVersionQuery query, CancellationToken cancellationToken) + { + var readModels = await _msSqlConnection.QueryAsync( + Label.Named("mssql-fetch-test-read-model"), + ReadModelExtensions.GetConnectionStringName(), + cancellationToken, + "SELECT * FROM [ReadModel-ThingyAggregate] WHERE AggregateId = @AggregateId", + new { AggregateId = query.ThingyId.Value }) + .ConfigureAwait(false); + return readModels.SingleOrDefault()?.LastAggregateSequenceNumber; + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/ObsoleteMsSqlThingyGetQueryHandler.cs b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/ObsoleteMsSqlThingyGetQueryHandler.cs new file mode 100644 index 000000000..cd6c99562 --- /dev/null +++ b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/ObsoleteMsSqlThingyGetQueryHandler.cs @@ -0,0 +1,33 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015-2025 Rasmus Mikkelsen +// https://github.com/eventflow/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 EventFlow.MsSql.Tests.IntegrationTests.ReadStores.ReadModels; + +namespace EventFlow.MsSql.Tests.IntegrationTests.ReadStores.QueryHandlers +{ + public class ObsoleteMsSqlThingyGetQueryHandler : MsSqlThingyGetQueryHandlerBase + { + public ObsoleteMsSqlThingyGetQueryHandler(IMsSqlConnection msSqlConnection) : base(msSqlConnection) + { + } + } +} diff --git a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/ObsoleteMsSqlThingyGetVersionQueryHandler.cs b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/ObsoleteMsSqlThingyGetVersionQueryHandler.cs new file mode 100644 index 000000000..26a205f6c --- /dev/null +++ b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/QueryHandlers/ObsoleteMsSqlThingyGetVersionQueryHandler.cs @@ -0,0 +1,34 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015-2025 Rasmus Mikkelsen +// https://github.com/eventflow/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 EventFlow.MsSql.Tests.IntegrationTests.ReadStores.ReadModels; + +namespace EventFlow.MsSql.Tests.IntegrationTests.ReadStores.QueryHandlers +{ + public class ObsoleteMsSqlThingyGetVersionQueryHandler + : MsSqlThingyGetVersionQueryHandlerBase + { + public ObsoleteMsSqlThingyGetVersionQueryHandler(IMsSqlConnection msSqlConnection) : base(msSqlConnection) + { + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/IThingyReadModel.cs b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/IThingyReadModel.cs new file mode 100644 index 000000000..97689bb61 --- /dev/null +++ b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/IThingyReadModel.cs @@ -0,0 +1,15 @@ +using EventFlow.ReadStores; +using EventFlow.TestHelpers.Aggregates; +using System; + +namespace EventFlow.MsSql.Tests.IntegrationTests.ReadStores.ReadModels +{ + public interface IThingyReadModel : IReadModel + { + int LastAggregateSequenceNumber { get; } + public DateTimeOffset CreateTime { get; set; } + public DateTimeOffset UpdatedTime { get; set; } + + Thingy ToThingy(); + } +} diff --git a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/MsSqlThingyReadModel.cs b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/MsSqlThingyReadModel.cs index d1c7763fe..3773c11e3 100644 --- a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/MsSqlThingyReadModel.cs +++ b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/MsSqlThingyReadModel.cs @@ -20,25 +20,29 @@ // 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.ComponentModel.DataAnnotations.Schema; -using System.Threading; -using System.Threading.Tasks; using EventFlow.Aggregates; -using EventFlow.MsSql.ReadStores; using EventFlow.ReadStores; using EventFlow.TestHelpers.Aggregates; using EventFlow.TestHelpers.Aggregates.Events; +using System; +using System.ComponentModel.DataAnnotations.Schema; +using System.Threading; +using System.Threading.Tasks; #pragma warning disable 618 namespace EventFlow.MsSql.Tests.IntegrationTests.ReadStores.ReadModels { [Table("ReadModel-ThingyAggregate")] - public class MsSqlThingyReadModel : MssqlReadModel, + public class MsSqlThingyReadModel : IThingyReadModel, IAmReadModelFor, IAmReadModelFor, IAmReadModelFor { + public string AggregateId { get; set; } + public int LastAggregateSequenceNumber { get; set; } + public DateTimeOffset CreateTime { get; set; } + public DateTimeOffset UpdatedTime { get; set; } public bool DomainErrorAfterFirstReceived { get; set; } public int PingsReceived { get; set; } diff --git a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/ObsoleteMsSqlThingyReadModel.cs b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/ObsoleteMsSqlThingyReadModel.cs new file mode 100644 index 000000000..3525b6fc8 --- /dev/null +++ b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/ObsoleteMsSqlThingyReadModel.cs @@ -0,0 +1,81 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015-2025 Rasmus Mikkelsen +// https://github.com/eventflow/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 EventFlow.Aggregates; +using EventFlow.MsSql.ReadStores; +using EventFlow.ReadStores; +using EventFlow.TestHelpers.Aggregates; +using EventFlow.TestHelpers.Aggregates.Events; +using System.ComponentModel.DataAnnotations.Schema; +using System.Threading; +using System.Threading.Tasks; + +#pragma warning disable 618 + +namespace EventFlow.MsSql.Tests.IntegrationTests.ReadStores.ReadModels +{ + [Table("ReadModel-ThingyAggregate")] + public class ObsoleteMsSqlThingyReadModel : MssqlReadModel, + IThingyReadModel, + IAmReadModelFor, + IAmReadModelFor, + IAmReadModelFor + { + public bool DomainErrorAfterFirstReceived { get; set; } + public int PingsReceived { get; set; } + + public Task ApplyAsync( + IReadModelContext context, + IDomainEvent domainEvent, + CancellationToken cancellationToken) + { + PingsReceived++; + return Task.CompletedTask; + } + + public Task ApplyAsync( + IReadModelContext context, + IDomainEvent domainEvent, + CancellationToken cancellationToken) + { + DomainErrorAfterFirstReceived = true; + return Task.CompletedTask; + } + + public Task ApplyAsync( + IReadModelContext context, + IDomainEvent domainEvent, + CancellationToken cancellationToken) + { + context.MarkForDeletion(); + return Task.CompletedTask; + } + + public Thingy ToThingy() + { + return new Thingy( + ThingyId.With(AggregateId), + PingsReceived, + DomainErrorAfterFirstReceived); + } + } +} \ No newline at end of file From acc75a72a17651e46afff0fcdc22866165444127 Mon Sep 17 00:00:00 2001 From: JMolenkamp Date: Sun, 11 Jan 2026 01:04:19 +0100 Subject: [PATCH 2/3] fix(mssql-read-model-store): set identity for new read model instance --- .../ReadStores/MssqlReadModelStore.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Source/EventFlow.MsSql/ReadStores/MssqlReadModelStore.cs b/Source/EventFlow.MsSql/ReadStores/MssqlReadModelStore.cs index 9bde8b5cb..cdc512b2f 100644 --- a/Source/EventFlow.MsSql/ReadStores/MssqlReadModelStore.cs +++ b/Source/EventFlow.MsSql/ReadStores/MssqlReadModelStore.cs @@ -53,6 +53,7 @@ public class MssqlReadModelStore : private readonly ITransientFaultHandler _transientFaultHandler; private static readonly Func GetVersion; private static readonly Action SetVersion; + private static readonly Action SetIdentity; private static readonly string ReadModelNameLowerCase = typeof(TReadModel).Name.ToLowerInvariant(); private static readonly string ConnectionStringName = typeof(TReadModel).GetCustomAttribute()?.ConnectionStringName; @@ -78,6 +79,22 @@ static MssqlReadModelStore() GetVersion = rm => (int?)versionPropertyInfo.GetValue(rm); SetVersion = (rm, v) => versionPropertyInfo.SetValue(rm, v); } + + var identityPropertyInfo = propertyInfos + .SingleOrDefault(p => p.GetCustomAttributes().Any(a => a is SqlReadModelIdentityColumnAttribute)); + if (identityPropertyInfo == null) + { + identityPropertyInfo = propertyInfos.SingleOrDefault(p => p.Name == "AggregateId"); + } + + if (identityPropertyInfo == null) + { + SetIdentity = (rm, i) => { }; + } + else + { + SetIdentity = identityPropertyInfo.SetValue; + } } public MssqlReadModelStore( @@ -167,6 +184,7 @@ private async Task UpdateReadModelAsync( else { SetVersion(readModel, (int?) readModelEnvelope.Version); + SetIdentity(readModel, readModelId); } var sql = isNew From 4b5f9960dc26073d69a0274c14386ed245a4ca6b Mon Sep 17 00:00:00 2001 From: JMolenkamp Date: Sun, 11 Jan 2026 09:22:57 +0100 Subject: [PATCH 3/3] chore(tests): add license model to new file --- .../ReadStores/ReadModels/IThingyReadModel.cs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/IThingyReadModel.cs b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/IThingyReadModel.cs index 97689bb61..a4c735397 100644 --- a/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/IThingyReadModel.cs +++ b/Source/EventFlow.MsSql.Tests/IntegrationTests/ReadStores/ReadModels/IThingyReadModel.cs @@ -1,4 +1,26 @@ -using EventFlow.ReadStores; +// The MIT License (MIT) +// +// Copyright (c) 2015-2025 Rasmus Mikkelsen +// https://github.com/eventflow/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 EventFlow.ReadStores; using EventFlow.TestHelpers.Aggregates; using System;