From 841219795e1cfecb2bff15d3a831fa4040c8f634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Tue, 24 Aug 2021 17:29:54 +0200 Subject: [PATCH 1/3] Eager loading of related data Added extensions methods to configure eager loading of related data by appending .Include(..) / .ThenInclude(..) statements --- .../EntityFrameworkTestExtensions.cs | 9 ++ .../Model/TestDbContext.cs | 13 +++ .../MsSql/EfMsSqlReadStoreIncludeTests.cs | 98 +++++++++++++++++++ .../MsSql/IncludeTests/Address.cs | 20 ++++ .../MsSql/IncludeTests/AddressId.cs | 11 +++ .../Commands/AddAddressCommand.cs | 25 +++++ .../Commands/CreatePersonCommand.cs | 26 +++++ .../IncludeTests/Events/AddressAddedEvent.cs | 14 +++ .../IncludeTests/Events/PersonCreatedEvent.cs | 14 +++ .../MsSql/IncludeTests/Person.cs | 19 ++++ .../MsSql/IncludeTests/PersonAggregate.cs | 37 +++++++ .../MsSql/IncludeTests/PersonId.cs | 11 +++ .../IncludeTests/Queries/PersonGetQuery.cs | 38 +++++++ .../ReadModels/AddressReadModelEntity.cs | 28 ++++++ .../ReadModels/PersonReadModelEntity.cs | 54 ++++++++++ .../EntityFrameworkReadModelConfiguration.cs | 14 +++ ...ameworkReadModelConfigurationExtensions.cs | 98 +++++++++++++++++++ ...entFlowOptionsEntityFrameworkExtensions.cs | 73 ++++++++++++++ .../IApplyQueryableConfiguration.cs | 20 ++++ .../IApplyQueryableIncludeConfiguration.cs | 23 +++++ .../Includes/IncludeExpression.cs | 36 +++++++ .../Configuration/Includes/IncludeString.cs | 26 +++++ .../ThenIncludeEnumerableExpression.cs | 39 ++++++++ .../Includes/ThenIncludeExpression.cs | 38 +++++++ .../EntityFrameworkReadModelStore.cs | 41 +++++--- 25 files changed, 810 insertions(+), 15 deletions(-) create mode 100644 Source/EventFlow.EntityFramework.Tests/MsSql/EfMsSqlReadStoreIncludeTests.cs create mode 100644 Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Address.cs create mode 100644 Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/AddressId.cs create mode 100644 Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/AddAddressCommand.cs create mode 100644 Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/CreatePersonCommand.cs create mode 100644 Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/AddressAddedEvent.cs create mode 100644 Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/PersonCreatedEvent.cs create mode 100644 Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Person.cs create mode 100644 Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonAggregate.cs create mode 100644 Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonId.cs create mode 100644 Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Queries/PersonGetQuery.cs create mode 100644 Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/AddressReadModelEntity.cs create mode 100644 Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/PersonReadModelEntity.cs create mode 100644 Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfiguration.cs create mode 100644 Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfigurationExtensions.cs create mode 100644 Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableConfiguration.cs create mode 100644 Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableIncludeConfiguration.cs create mode 100644 Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeExpression.cs create mode 100644 Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeString.cs create mode 100644 Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeEnumerableExpression.cs create mode 100644 Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeExpression.cs diff --git a/Source/EventFlow.EntityFramework.Tests/EntityFrameworkTestExtensions.cs b/Source/EventFlow.EntityFramework.Tests/EntityFrameworkTestExtensions.cs index 1540d6269..274af2c65 100644 --- a/Source/EventFlow.EntityFramework.Tests/EntityFrameworkTestExtensions.cs +++ b/Source/EventFlow.EntityFramework.Tests/EntityFrameworkTestExtensions.cs @@ -23,6 +23,8 @@ using EventFlow.EntityFramework.Extensions; using EventFlow.EntityFramework.Tests.Model; +using EventFlow.EntityFramework.Tests.MsSql.IncludeTests.Queries; +using EventFlow.EntityFramework.Tests.MsSql.IncludeTests.ReadModels; using EventFlow.Extensions; using EventFlow.TestHelpers.Aggregates.Entities; @@ -53,5 +55,12 @@ public static IEventFlowOptions ConfigureForReadStoreTest(this IEventFlowOptions typeof(EfThingyGetVersionQueryHandler), typeof(EfThingyGetMessagesQueryHandler)); } + + public static IEventFlowOptions ConfigureForReadStoreIncludeTest(this IEventFlowOptions options) + { + return options + .UseEntityFrameworkReadModel(cfg => cfg.Include(x => x.Addresses)) + .AddQueryHandlers(typeof(PersonGetQueryHandler)); + } } } \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/Model/TestDbContext.cs b/Source/EventFlow.EntityFramework.Tests/Model/TestDbContext.cs index 6ca799796..81f4b769f 100644 --- a/Source/EventFlow.EntityFramework.Tests/Model/TestDbContext.cs +++ b/Source/EventFlow.EntityFramework.Tests/Model/TestDbContext.cs @@ -22,6 +22,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using EventFlow.EntityFramework.Extensions; +using EventFlow.EntityFramework.Tests.MsSql.IncludeTests.ReadModels; using Microsoft.EntityFrameworkCore; namespace EventFlow.EntityFramework.Tests.Model @@ -35,6 +36,10 @@ public TestDbContext(DbContextOptions options) : base(options) public DbSet Thingys { get; set; } public DbSet ThingyMessages { get; set; } + // Include tests + public DbSet Persons { get; set; } + public DbSet Addresses { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder @@ -48,6 +53,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() .Property(e => e.AggregateId) .ValueGeneratedOnAdd(); + + modelBuilder.Entity() + .Property(e => e.AggregateId) + .ValueGeneratedOnAdd(); + + modelBuilder.Entity() + .Property(e => e.AddressId) + .ValueGeneratedNever(); } } } \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/EfMsSqlReadStoreIncludeTests.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/EfMsSqlReadStoreIncludeTests.cs new file mode 100644 index 000000000..5e48e8a18 --- /dev/null +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/EfMsSqlReadStoreIncludeTests.cs @@ -0,0 +1,98 @@ +using System.Threading; +using System.Threading.Tasks; +using EventFlow.Configuration; +using EventFlow.EntityFramework.Extensions; +using EventFlow.EntityFramework.Tests.Model; +using EventFlow.EntityFramework.Tests.MsSql.IncludeTests; +using EventFlow.EntityFramework.Tests.MsSql.IncludeTests.Commands; +using EventFlow.EntityFramework.Tests.MsSql.IncludeTests.Queries; +using EventFlow.Extensions; +using EventFlow.TestHelpers; +using EventFlow.TestHelpers.MsSql; +using FluentAssertions; +using NUnit.Framework; + +namespace EventFlow.EntityFramework.Tests.MsSql +{ + [Category(Categories.Integration)] + public class EfMsSqlReadStoreIncludeTests : IntegrationTest + { + private IMsSqlDatabase _testDatabase; + + protected override IRootResolver CreateRootResolver(IEventFlowOptions eventFlowOptions) + { + _testDatabase = MsSqlHelpz.CreateDatabase("eventflow"); + + return eventFlowOptions + .RegisterServices(sr => sr.Register(c => _testDatabase.ConnectionString)) + .ConfigureEntityFramework(EntityFrameworkConfiguration.New) + .AddDbContextProvider() + .ConfigureForReadStoreIncludeTest() + .AddDefaults(typeof(EfMsSqlReadStoreIncludeTests).Assembly) + .CreateResolver(); + } + + [TearDown] + public void TearDown() + { + _testDatabase.DisposeSafe("Failed to delete database"); + } + + [Test] + public async Task ReadModelContainsPersonNameAfterCreation() + { + // Arrange + var id = PersonId.New; + + // Act + await CommandBus + .PublishAsync(new CreatePersonCommand(id, "Bob"), CancellationToken.None) + .ConfigureAwait(false); + + var readModel = await QueryProcessor + .ProcessAsync(new PersonGetQuery(id), CancellationToken.None) + .ConfigureAwait(false); + + // Assert + readModel.Should().NotBeNull(); + readModel.Name.Should().Be("Bob"); + readModel.Addresses.Should().BeNullOrEmpty(); + } + + [Test] + public async Task ReadModelContainsPersonAddressesAfterAdd() + { + // Arrange + var id = PersonId.New; + await CommandBus + .PublishAsync(new CreatePersonCommand(id, "Bob"), CancellationToken.None) + .ConfigureAwait(false); + + // Act + var address1 = new Address(AddressId.New, "Smith street 4.", "1234", "New York", "US"); + await CommandBus + .PublishAsync(new AddAddressCommand(id, + address1), + CancellationToken.None) + .ConfigureAwait(false); + + var address2 = new Address(AddressId.New, "Musterstraße 42.", "6541", "Berlin", "DE"); + await CommandBus + .PublishAsync(new AddAddressCommand(id, + address2), + CancellationToken.None) + .ConfigureAwait(false); + + var readModel = await QueryProcessor + .ProcessAsync(new PersonGetQuery(id), CancellationToken.None) + .ConfigureAwait(false); + + // Assert + readModel.Should().NotBeNull(); + readModel.NumberOfAddresses.Should().Be(2); + readModel.Addresses.Should().HaveCount(2); + readModel.Addresses.Should().ContainEquivalentOf(address1); + readModel.Addresses.Should().ContainEquivalentOf(address2); + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Address.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Address.cs new file mode 100644 index 000000000..e5b8a640a --- /dev/null +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Address.cs @@ -0,0 +1,20 @@ +using EventFlow.Entities; + +namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests +{ + public class Address : Entity + { + public string Street { get; set; } + public string PostalCode { get; set; } + public string City { get; set; } + public string Country { get; set; } + + public Address(AddressId id, string street, string postalCode, string city, string country) : base(id) + { + Street = street; + PostalCode = postalCode; + City = city; + Country = country; + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/AddressId.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/AddressId.cs new file mode 100644 index 000000000..87e68c3a0 --- /dev/null +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/AddressId.cs @@ -0,0 +1,11 @@ +using EventFlow.Core; + +namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests +{ + public class AddressId : Identity + { + public AddressId(string value) : base(value) + { + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/AddAddressCommand.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/AddAddressCommand.cs new file mode 100644 index 000000000..6a745c222 --- /dev/null +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/AddAddressCommand.cs @@ -0,0 +1,25 @@ +using System.Threading; +using System.Threading.Tasks; +using EventFlow.Commands; + +namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests.Commands +{ + public class AddAddressCommand : Command + { + public Address PersonAddress { get; } + + public AddAddressCommand(PersonId aggregateId, Address personAddress) : base(aggregateId) + { + PersonAddress = personAddress; + } + } + + public class AddAddressCommandHandler : CommandHandler + { + public override Task ExecuteAsync(PersonAggregate aggregate, AddAddressCommand command, CancellationToken cancellationToken) + { + aggregate.AddAddress(command.PersonAddress); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/CreatePersonCommand.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/CreatePersonCommand.cs new file mode 100644 index 000000000..437afd6e4 --- /dev/null +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/CreatePersonCommand.cs @@ -0,0 +1,26 @@ +using System.Threading; +using System.Threading.Tasks; +using EventFlow.Commands; + +namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests.Commands +{ + public class CreatePersonCommand : Command + { + public string Name { get; } + + public CreatePersonCommand(PersonId aggregateId, string name) + :base(aggregateId) + { + Name = name; + } + } + + public class CreatePersonCommandHandler : CommandHandler + { + public override Task ExecuteAsync(PersonAggregate aggregate, CreatePersonCommand command, CancellationToken cancellationToken) + { + aggregate.Create(command.Name); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/AddressAddedEvent.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/AddressAddedEvent.cs new file mode 100644 index 000000000..3a1ccd285 --- /dev/null +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/AddressAddedEvent.cs @@ -0,0 +1,14 @@ +using EventFlow.Aggregates; + +namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests.Events +{ + public class AddressAddedEvent : AggregateEvent + { + public Address Address { get; set; } + + public AddressAddedEvent(Address address) + { + Address = address; + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/PersonCreatedEvent.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/PersonCreatedEvent.cs new file mode 100644 index 000000000..d47e277e3 --- /dev/null +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/PersonCreatedEvent.cs @@ -0,0 +1,14 @@ +using EventFlow.Aggregates; + +namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests.Events +{ + public class PersonCreatedEvent : AggregateEvent + { + public string Name { get; set; } + + public PersonCreatedEvent(string name) + { + Name = name; + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Person.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Person.cs new file mode 100644 index 000000000..1bbb7c495 --- /dev/null +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Person.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using EventFlow.Entities; + +namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests +{ + public class Person : Entity + { + public string Name { get; } + public ICollection
Addresses { get; } + public int NumberOfAddresses { get; } + + public Person(PersonId id, string name, ICollection
addresses, int numberOfAddresses) : base(id) + { + Name = name; + Addresses = addresses; + NumberOfAddresses = numberOfAddresses; + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonAggregate.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonAggregate.cs new file mode 100644 index 000000000..bf2b871b3 --- /dev/null +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonAggregate.cs @@ -0,0 +1,37 @@ +using EventFlow.Aggregates; +using EventFlow.EntityFramework.Tests.MsSql.IncludeTests.Events; + +namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests +{ + [AggregateName("Person")] + public class PersonAggregate : AggregateRoot, + IEmit, + IEmit + { + public PersonAggregate(PersonId id) : base(id) + { + } + + public void Create(string name) + { + Emit(new PersonCreatedEvent(name)); + } + + public void AddAddress(Address address) + { + Emit(new AddressAddedEvent(address)); + } + + void IEmit.Apply(PersonCreatedEvent aggregateEvent) + { + // save name into field for later usage + // .. + } + + void IEmit.Apply(AddressAddedEvent aggregateEvent) + { + // save address into field for later usage + // .. + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonId.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonId.cs new file mode 100644 index 000000000..4f20de644 --- /dev/null +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonId.cs @@ -0,0 +1,11 @@ +using EventFlow.Core; + +namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests +{ + public class PersonId : Identity + { + public PersonId(string value) : base(value) + { + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Queries/PersonGetQuery.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Queries/PersonGetQuery.cs new file mode 100644 index 000000000..aebd7b65e --- /dev/null +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Queries/PersonGetQuery.cs @@ -0,0 +1,38 @@ +using System.Threading; +using System.Threading.Tasks; +using EventFlow.EntityFramework.Tests.Model; +using EventFlow.Queries; +using Microsoft.EntityFrameworkCore; + +namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests.Queries +{ + public class PersonGetQuery : IQuery + { + public PersonId PersonId { get; } + + public PersonGetQuery(PersonId personId) + { + PersonId = personId; + } + } + + public class PersonGetQueryHandler : IQueryHandler + { + private readonly IDbContextProvider _dbContextProvider; + + public PersonGetQueryHandler(IDbContextProvider dbContextProvider) + { + _dbContextProvider = dbContextProvider; + } + + public async Task ExecuteQueryAsync(PersonGetQuery query, CancellationToken cancellationToken) + { + await using var context = _dbContextProvider.CreateContext(); + var entity = await context.Persons + .Include(x => x.Addresses) + .SingleOrDefaultAsync(x => x.AggregateId == query.PersonId.Value, cancellationToken) + .ConfigureAwait(false); + return entity?.ToPerson(); + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/AddressReadModelEntity.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/AddressReadModelEntity.cs new file mode 100644 index 000000000..0696ac74e --- /dev/null +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/AddressReadModelEntity.cs @@ -0,0 +1,28 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests.ReadModels +{ + public class AddressReadModelEntity + { + [Key] + [StringLength(64)] + public string AddressId { get; set; } + + [StringLength(64)] + public string PersonId { get; set; } + + [ForeignKey(nameof(PersonId))] + public virtual PersonReadModelEntity Person { get; set; } + + public string Street { get; set; } + + public string PostalCode { get; set; } + + public string City { get; set; } + + public string Country { get; set; } + + public Address ToAddress() => new Address(IncludeTests.AddressId.With(AddressId), Street, PostalCode, City, Country); + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/PersonReadModelEntity.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/PersonReadModelEntity.cs new file mode 100644 index 000000000..e029da6fb --- /dev/null +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/PersonReadModelEntity.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using EventFlow.Aggregates; +using EventFlow.EntityFramework.Tests.MsSql.IncludeTests.Events; +using EventFlow.ReadStores; + +namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests.ReadModels +{ + public class PersonReadModelEntity : IReadModel, + IAmReadModelFor, + IAmReadModelFor + { + [Key] + [StringLength(64)] + public string AggregateId { get; set; } + + public string Name { get; set; } + + public int NumberOfAddresses { get; set; } + + public virtual ICollection Addresses { get; set; } = new List(); + + public void Apply(IReadModelContext context, + IDomainEvent domainEvent) + { + Name = domainEvent.AggregateEvent.Name; + } + + public void Apply(IReadModelContext context, + IDomainEvent domainEvent) + { + var address = domainEvent.AggregateEvent.Address; + Addresses.Add(new AddressReadModelEntity + { + AddressId = address.Id.Value, + PersonId = domainEvent.AggregateIdentity.Value, + Street = address.Street, + City = address.City, + PostalCode = address.PostalCode, + Country = address.Country + }); + + NumberOfAddresses = Addresses.Count; + } + + public Person ToPerson() => + new Person( + PersonId.With(AggregateId), + Name, + Addresses?.Select(x => x.ToAddress()).ToList(), + NumberOfAddresses); + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfiguration.cs b/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfiguration.cs new file mode 100644 index 000000000..531187d7b --- /dev/null +++ b/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfiguration.cs @@ -0,0 +1,14 @@ +using System.Linq; +using EventFlow.EntityFramework.ReadStores.Configuration; +using EventFlow.ReadStores; + +namespace EventFlow.EntityFramework +{ + public sealed class EntityFrameworkReadModelConfiguration : IApplyQueryableConfiguration + where TReadModel : class, IReadModel, new() + { + IQueryable + IApplyQueryableConfiguration.Apply(IQueryable queryable) => + queryable; + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfigurationExtensions.cs b/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfigurationExtensions.cs new file mode 100644 index 000000000..f4aea37ec --- /dev/null +++ b/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfigurationExtensions.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using EventFlow.EntityFramework.ReadStores.Configuration; +using EventFlow.EntityFramework.ReadStores.Configuration.Includes; +using EventFlow.ReadStores; + +namespace EventFlow.EntityFramework +{ + /// + /// Extensions methods to configure the ReadModel + /// + public static class EntityFrameworkReadModelConfigurationExtensions + { + /// + public static IncludeExpression + Include( + this IApplyQueryableConfiguration source, + Expression> navigationPropertyPath) + where TReadModel : class, IReadModel, new() + { + if (navigationPropertyPath == null) + { + throw new ArgumentNullException(nameof(navigationPropertyPath)); + } + + return new IncludeExpression( + source, + navigationPropertyPath); + } + + /// + public static IncludeString + Include( + this IApplyQueryableConfiguration source, + string navigationPropertyPath) + where TReadModel : class, IReadModel, new() + { + if (navigationPropertyPath == null) + { + throw new ArgumentNullException(nameof(navigationPropertyPath)); + } + + if (string.IsNullOrWhiteSpace(navigationPropertyPath)) + { + throw new ArgumentException("Must not be null or empty", nameof(navigationPropertyPath)); + } + + return new IncludeString( + source, + navigationPropertyPath); + } + + /// + public static ThenIncludeExpression + ThenInclude( + this IApplyQueryableIncludeConfiguration source, + Expression> navigationPropertyPath) + where TEntity : class, IReadModel, new() + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (navigationPropertyPath == null) + { + throw new ArgumentNullException(nameof(navigationPropertyPath)); + } + + return new ThenIncludeExpression( + source, + navigationPropertyPath); + } + + /// + public static ThenIncludeEnumerableExpression + ThenInclude( + this IApplyQueryableIncludeConfiguration> source, + Expression> navigationPropertyPath) + where TEntity : class, IReadModel, new() + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (navigationPropertyPath == null) + { + throw new ArgumentNullException(nameof(navigationPropertyPath)); + } + + return new ThenIncludeEnumerableExpression( + source, + navigationPropertyPath); + } + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework/Extensions/EventFlowOptionsEntityFrameworkExtensions.cs b/Source/EventFlow.EntityFramework/Extensions/EventFlowOptionsEntityFrameworkExtensions.cs index 836131e5a..2fe0a220c 100644 --- a/Source/EventFlow.EntityFramework/Extensions/EventFlowOptionsEntityFrameworkExtensions.cs +++ b/Source/EventFlow.EntityFramework/Extensions/EventFlowOptionsEntityFrameworkExtensions.cs @@ -25,6 +25,8 @@ using EventFlow.Configuration; using EventFlow.EntityFramework.EventStores; using EventFlow.EntityFramework.ReadStores; +using EventFlow.EntityFramework.ReadStores.Configuration; +using EventFlow.EntityFramework.ReadStores.Configuration.Includes; using EventFlow.EntityFramework.SnapshotStores; using EventFlow.Extensions; using EventFlow.ReadStores; @@ -68,12 +70,81 @@ public static IEventFlowOptions UseEntityFrameworkReadModel, EntityFrameworkReadModelStore>(); + f.Register>(_ => + new EntityFrameworkReadModelConfiguration(), Lifetime.Singleton); f.Register>(r => r.Resolver.Resolve>()); }) .UseReadStoreFor, TReadModel>(); } + /// + /// Configures the read model. Can be used for eager loading of related data by appending .Include(..) / .ThenInclude(..) statements. + /// + /// The read model's entity type + /// The database context type + /// + /// Function to configure eager loading of related data by appending .Include(..) / .ThenInclude(..) statements. + /// Avoid navigation properties if you create read models for both, the parent entity and the child entity. Otherwise there is a risk of a ordering problem when saving aggregates and updating read modules independently (FOREIGN-KEY constraint) + public static IEventFlowOptions UseEntityFrameworkReadModel( + this IEventFlowOptions eventFlowOptions, + Func,IApplyQueryableConfiguration> configure) + where TDbContext : DbContext + where TReadModel : class, IReadModel, new() + { + return eventFlowOptions + .RegisterServices(f => + { + f.Register, + EntityFrameworkReadModelStore>(); + f.Register(_ => + { + var readModelConfig = new EntityFrameworkReadModelConfiguration(); + return configure != null + ? configure(readModelConfig) + : readModelConfig; + + }, Lifetime.Singleton); + f.Register>(r => + r.Resolver.Resolve>()); + }) + .UseReadStoreFor, TReadModel>(); + } + + /// + /// Configures the read model. Can be used for eager loading of related data by appending .Include(..) / .ThenInclude(..) statements. + /// + /// The read model's entity type + /// The database context type + /// The read model locator type + /// + /// Function to configure eager loading of related data by appending .Include(..) / .ThenInclude(..) statements. + /// Avoid navigation properties if you create read models for both, the parent entity and the child entity. Otherwise there is a risk of a ordering problem when saving aggregates and updating read modules independently (FOREIGN-KEY constraint) + public static IEventFlowOptions UseEntityFrameworkReadModel( + this IEventFlowOptions eventFlowOptions, + Func,IApplyQueryableConfiguration> configure) + where TDbContext : DbContext + where TReadModel : class, IReadModel, new() + where TReadModelLocator : IReadModelLocator + { + return eventFlowOptions + .RegisterServices(f => + { + f.Register, + EntityFrameworkReadModelStore>(); + f.Register(_ => + { + var readModelConfig = new EntityFrameworkReadModelConfiguration(); + return configure != null + ? configure(readModelConfig) + : readModelConfig; + }, Lifetime.Singleton); + f.Register>(r => + r.Resolver.Resolve>()); + }) + .UseReadStoreFor, TReadModel, TReadModelLocator>(); + } + public static IEventFlowOptions UseEntityFrameworkReadModel( this IEventFlowOptions eventFlowOptions) where TDbContext : DbContext @@ -85,6 +156,8 @@ public static IEventFlowOptions UseEntityFrameworkReadModel, EntityFrameworkReadModelStore>(); + f.Register>(_ => + new EntityFrameworkReadModelConfiguration(), Lifetime.Singleton); f.Register>(r => r.Resolver.Resolve>()); }) diff --git a/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableConfiguration.cs b/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableConfiguration.cs new file mode 100644 index 000000000..67bb3d06f --- /dev/null +++ b/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableConfiguration.cs @@ -0,0 +1,20 @@ +using System.Linq; +using EventFlow.ReadStores; + +namespace EventFlow.EntityFramework.ReadStores.Configuration +{ + /// + /// Configures an IQueryable + /// + /// Entity type + public interface IApplyQueryableConfiguration + where TReadModel : class, IReadModel, new() + { + /// + /// Applies the expression to the IQueryable + /// + /// Source + /// The applied IQueryable + IQueryable Apply(IQueryable queryable); + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableIncludeConfiguration.cs b/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableIncludeConfiguration.cs new file mode 100644 index 000000000..4b78afbc1 --- /dev/null +++ b/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableIncludeConfiguration.cs @@ -0,0 +1,23 @@ +using System.Linq; +using EventFlow.ReadStores; +using Microsoft.EntityFrameworkCore.Query; + +namespace EventFlow.EntityFramework.ReadStores.Configuration +{ + /// + /// Configures an IQueryable + /// + /// Entity type + /// Property type + public interface IApplyQueryableIncludeConfiguration + : IApplyQueryableConfiguration + where TReadModel : class, IReadModel, new() + { + /// + /// Applies the include expression to the IQueryable + /// + /// Source + /// An IIncludableQueryable + new IIncludableQueryable Apply(IQueryable queryable); + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeExpression.cs b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeExpression.cs new file mode 100644 index 000000000..5dd450960 --- /dev/null +++ b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeExpression.cs @@ -0,0 +1,36 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using EventFlow.ReadStores; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; + +namespace EventFlow.EntityFramework.ReadStores.Configuration.Includes +{ + public sealed class IncludeExpression + : IApplyQueryableIncludeConfiguration + where TReadModel : class, IReadModel, new() + { + private readonly IApplyQueryableConfiguration _source; + private readonly Expression> _navigationPropertyPath; + + internal IncludeExpression( + IApplyQueryableConfiguration source, + Expression> navigationPropertyPath) + { + _source = source; + _navigationPropertyPath = navigationPropertyPath; + } + + IQueryable + IApplyQueryableConfiguration.Apply( + IQueryable queryable) => ApplyInternal(queryable); + + IIncludableQueryable + IApplyQueryableIncludeConfiguration.Apply( + IQueryable queryable) => ApplyInternal(queryable); + + private IIncludableQueryable ApplyInternal(IQueryable queryable) => + _source.Apply(queryable).Include(_navigationPropertyPath); + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeString.cs b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeString.cs new file mode 100644 index 000000000..1d0e7019e --- /dev/null +++ b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeString.cs @@ -0,0 +1,26 @@ +using System.Linq; +using EventFlow.ReadStores; +using Microsoft.EntityFrameworkCore; + +namespace EventFlow.EntityFramework.ReadStores.Configuration.Includes +{ + public sealed class IncludeString + : IApplyQueryableConfiguration + where TReadModel : class, IReadModel, new() + { + private readonly IApplyQueryableConfiguration _source; + private readonly string _navigationPropertyPath; + + internal IncludeString( + IApplyQueryableConfiguration source, + string navigationPropertyPath) + { + _source = source; + _navigationPropertyPath = navigationPropertyPath; + } + + IQueryable IApplyQueryableConfiguration.Apply( + IQueryable queryable) => + _source.Apply(queryable).Include(_navigationPropertyPath); + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeEnumerableExpression.cs b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeEnumerableExpression.cs new file mode 100644 index 000000000..c7142e5c5 --- /dev/null +++ b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeEnumerableExpression.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using EventFlow.ReadStores; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; + +namespace EventFlow.EntityFramework.ReadStores.Configuration.Includes +{ + public sealed class ThenIncludeEnumerableExpression + : IApplyQueryableIncludeConfiguration + where TReadModel : class, IReadModel, new() + { + private readonly IApplyQueryableIncludeConfiguration> _source; + private readonly Expression> _navigationPropertyPath; + + public ThenIncludeEnumerableExpression( + IApplyQueryableIncludeConfiguration> source, + Expression> navigationPropertyPath) + { + _source = source; + _navigationPropertyPath = navigationPropertyPath; + } + + IQueryable + IApplyQueryableConfiguration.Apply( + IQueryable queryable) => ApplyInternal(queryable); + + IIncludableQueryable + IApplyQueryableIncludeConfiguration.Apply( + IQueryable queryable) => ApplyInternal(queryable); + + private IIncludableQueryable ApplyInternal(IQueryable queryable) => + _source + .Apply(queryable) + .ThenInclude(_navigationPropertyPath); + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeExpression.cs b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeExpression.cs new file mode 100644 index 000000000..0d074ecb5 --- /dev/null +++ b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeExpression.cs @@ -0,0 +1,38 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using EventFlow.ReadStores; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; + +namespace EventFlow.EntityFramework.ReadStores.Configuration.Includes +{ + public sealed class ThenIncludeExpression + : IApplyQueryableIncludeConfiguration + where TReadModel : class, IReadModel, new() + { + private readonly IApplyQueryableIncludeConfiguration _source; + private readonly Expression> _navigationPropertyPath; + + internal ThenIncludeExpression( + IApplyQueryableIncludeConfiguration source, + Expression> navigationPropertyPath) + { + _source = source; + _navigationPropertyPath = navigationPropertyPath; + } + + IQueryable + IApplyQueryableConfiguration.Apply( + IQueryable queryable) => ApplyInternal(queryable); + + IIncludableQueryable + IApplyQueryableIncludeConfiguration.Apply( + IQueryable queryable) => ApplyInternal(queryable); + + private IIncludableQueryable ApplyInternal(IQueryable queryable) => + _source + .Apply(queryable) + .ThenInclude(_navigationPropertyPath); + } +} \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework/ReadStores/EntityFrameworkReadModelStore.cs b/Source/EventFlow.EntityFramework/ReadStores/EntityFrameworkReadModelStore.cs index ab08a2882..babaad656 100644 --- a/Source/EventFlow.EntityFramework/ReadStores/EntityFrameworkReadModelStore.cs +++ b/Source/EventFlow.EntityFramework/ReadStores/EntityFrameworkReadModelStore.cs @@ -31,6 +31,7 @@ using EventFlow.Core; using EventFlow.Core.RetryStrategies; using EventFlow.EntityFramework.Extensions; +using EventFlow.EntityFramework.ReadStores.Configuration; using EventFlow.Exceptions; using EventFlow.Extensions; using EventFlow.Logs; @@ -55,17 +56,20 @@ private static readonly ConcurrentDictionary Descripto private readonly IDbContextProvider _contextProvider; private readonly int _deletionBatchSize; private readonly IReadModelFactory _readModelFactory; + private readonly IApplyQueryableConfiguration _queryableConfiguration; private readonly ITransientFaultHandler _transientFaultHandler; public EntityFrameworkReadModelStore( IBulkOperationConfiguration bulkOperationConfiguration, ILog log, IReadModelFactory readModelFactory, + IApplyQueryableConfiguration queryableConfiguration, IDbContextProvider contextProvider, ITransientFaultHandler transientFaultHandler) : base(log) { _readModelFactory = readModelFactory; + _queryableConfiguration = queryableConfiguration; _contextProvider = contextProvider; _transientFaultHandler = transientFaultHandler; _deletionBatchSize = bulkOperationConfiguration.DeletionBatchSize; @@ -130,7 +134,7 @@ public override async Task DeleteAllAsync(CancellationToken cancellationToken) EntityDescriptor descriptor; using (var dbContext = _contextProvider.CreateContext()) { - descriptor = GetDescriptor(dbContext); + descriptor = GetDescriptor(dbContext, _queryableConfiguration); } var rowsAffected = await Bulk.Delete( @@ -161,7 +165,7 @@ private async Task> GetAsync(TDbContext dbContext, bool tracking = false) { var readModelType = typeof(TReadModel); - var descriptor = GetDescriptor(dbContext); + var descriptor = GetDescriptor(dbContext, _queryableConfiguration); var entity = await descriptor.Query(dbContext, id, cancellationToken, tracking) .ConfigureAwait(false); @@ -234,7 +238,7 @@ private async Task UpdateReadModelAsync(TDbContext dbContext, IReadModelContextF readModelEnvelope = updateResult.Envelope; entity = readModelEnvelope.ReadModel; - var descriptor = GetDescriptor(dbContext); + var descriptor = GetDescriptor(dbContext, _queryableConfiguration); var entry = isNew ? dbContext.Add(entity) : dbContext.Entry(entity); @@ -255,10 +259,12 @@ private async Task UpdateReadModelAsync(TDbContext dbContext, IReadModelContextF Log.Verbose(() => $"Updated Entity Framework read model {typeof(TReadModel).PrettyPrint()} with ID '{readModelId}' to version '{readModelEnvelope.Version}'"); } - private static EntityDescriptor GetDescriptor(DbContext context) + private static EntityDescriptor GetDescriptor( + DbContext context, + IApplyQueryableConfiguration queryableConfiguration) { return Descriptors.GetOrAdd(context.Database.ProviderName, s => - new EntityDescriptor(context)); + new EntityDescriptor(context, queryableConfiguration)); } private class EntityDescriptor @@ -268,13 +274,15 @@ private class EntityDescriptor private readonly Func> _queryByIdTracking; private readonly IProperty _version; - public EntityDescriptor(DbContext context) + public EntityDescriptor( + DbContext context, + IApplyQueryableConfiguration queryableConfiguration) { var entityType = context.Model.FindEntityType(typeof(TReadModel)); _key = GetKeyProperty(entityType); _version = GetVersionProperty(entityType); - _queryByIdTracking = CompileQueryById(true); - _queryByIdNoTracking = CompileQueryById(false); + _queryByIdTracking = CompileQueryById(queryableConfiguration, true); + _queryByIdNoTracking = CompileQueryById(queryableConfiguration, false); } public string Key => _key.Name; @@ -357,18 +365,21 @@ private IProperty GetVersionProperty(IEntityType entityType) return version; } - private Func> CompileQueryById(bool tracking) + private Func> CompileQueryById( + IApplyQueryableConfiguration queryableConfiguration, + bool tracking) { return tracking ? EF.CompileAsyncQuery((DbContext dbContext, CancellationToken t, string id) => - dbContext - .Set() - .AsTracking() + queryableConfiguration.Apply(dbContext + .Set() + .AsTracking()) .SingleOrDefault(e => EF.Property(e, Key) == id)) : EF.CompileAsyncQuery((DbContext dbContext, CancellationToken t, string id) => - dbContext - .Set() - .AsNoTracking() + queryableConfiguration.Apply( + dbContext + .Set() + .AsNoTracking()) .SingleOrDefault(e => EF.Property(e, Key) == id)); } } From 6c353f086354488b53641bfc6949bbcc7a8b6279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Thu, 26 Aug 2021 14:59:19 +0200 Subject: [PATCH 2/3] License text header added to EVERY SINGLE file --- .../MsSql/EfMsSqlReadStoreIncludeTests.cs | 23 +++++++++++++++++ .../MsSql/IncludeTests/Address.cs | 25 ++++++++++++++++++- .../MsSql/IncludeTests/AddressId.cs | 25 ++++++++++++++++++- .../Commands/AddAddressCommand.cs | 25 ++++++++++++++++++- .../Commands/CreatePersonCommand.cs | 25 ++++++++++++++++++- .../IncludeTests/Events/AddressAddedEvent.cs | 25 ++++++++++++++++++- .../IncludeTests/Events/PersonCreatedEvent.cs | 25 ++++++++++++++++++- .../MsSql/IncludeTests/Person.cs | 25 ++++++++++++++++++- .../MsSql/IncludeTests/PersonAggregate.cs | 25 ++++++++++++++++++- .../MsSql/IncludeTests/PersonId.cs | 25 ++++++++++++++++++- .../IncludeTests/Queries/PersonGetQuery.cs | 25 ++++++++++++++++++- .../ReadModels/AddressReadModelEntity.cs | 25 ++++++++++++++++++- .../ReadModels/PersonReadModelEntity.cs | 25 ++++++++++++++++++- .../EntityFrameworkReadModelConfiguration.cs | 25 ++++++++++++++++++- ...ameworkReadModelConfigurationExtensions.cs | 25 ++++++++++++++++++- .../IApplyQueryableConfiguration.cs | 25 ++++++++++++++++++- .../IApplyQueryableIncludeConfiguration.cs | 25 ++++++++++++++++++- .../Includes/IncludeExpression.cs | 25 ++++++++++++++++++- .../Configuration/Includes/IncludeString.cs | 25 ++++++++++++++++++- .../ThenIncludeEnumerableExpression.cs | 25 ++++++++++++++++++- .../Includes/ThenIncludeExpression.cs | 25 ++++++++++++++++++- 21 files changed, 503 insertions(+), 20 deletions(-) diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/EfMsSqlReadStoreIncludeTests.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/EfMsSqlReadStoreIncludeTests.cs index 5e48e8a18..b71ab940c 100644 --- a/Source/EventFlow.EntityFramework.Tests/MsSql/EfMsSqlReadStoreIncludeTests.cs +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/EfMsSqlReadStoreIncludeTests.cs @@ -1,3 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System.Threading; using System.Threading.Tasks; using EventFlow.Configuration; diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Address.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Address.cs index e5b8a640a..0127189d8 100644 --- a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Address.cs +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Address.cs @@ -1,4 +1,27 @@ -using EventFlow.Entities; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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.Entities; namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests { diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/AddressId.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/AddressId.cs index 87e68c3a0..0cce5133c 100644 --- a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/AddressId.cs +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/AddressId.cs @@ -1,4 +1,27 @@ -using EventFlow.Core; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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; namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests { diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/AddAddressCommand.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/AddAddressCommand.cs index 6a745c222..b2a193de1 100644 --- a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/AddAddressCommand.cs +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/AddAddressCommand.cs @@ -1,4 +1,27 @@ -using System.Threading; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System.Threading; using System.Threading.Tasks; using EventFlow.Commands; diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/CreatePersonCommand.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/CreatePersonCommand.cs index 437afd6e4..033a51295 100644 --- a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/CreatePersonCommand.cs +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Commands/CreatePersonCommand.cs @@ -1,4 +1,27 @@ -using System.Threading; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System.Threading; using System.Threading.Tasks; using EventFlow.Commands; diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/AddressAddedEvent.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/AddressAddedEvent.cs index 3a1ccd285..1a1dd5378 100644 --- a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/AddressAddedEvent.cs +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/AddressAddedEvent.cs @@ -1,4 +1,27 @@ -using EventFlow.Aggregates; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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; namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests.Events { diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/PersonCreatedEvent.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/PersonCreatedEvent.cs index d47e277e3..526502f97 100644 --- a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/PersonCreatedEvent.cs +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Events/PersonCreatedEvent.cs @@ -1,4 +1,27 @@ -using EventFlow.Aggregates; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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; namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests.Events { diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Person.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Person.cs index 1bbb7c495..ff7b4283a 100644 --- a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Person.cs +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Person.cs @@ -1,4 +1,27 @@ -using System.Collections.Generic; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System.Collections.Generic; using EventFlow.Entities; namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonAggregate.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonAggregate.cs index bf2b871b3..16fbeda6a 100644 --- a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonAggregate.cs +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonAggregate.cs @@ -1,4 +1,27 @@ -using EventFlow.Aggregates; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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.EntityFramework.Tests.MsSql.IncludeTests.Events; namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonId.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonId.cs index 4f20de644..18b8b5ed1 100644 --- a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonId.cs +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/PersonId.cs @@ -1,4 +1,27 @@ -using EventFlow.Core; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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; namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests { diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Queries/PersonGetQuery.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Queries/PersonGetQuery.cs index aebd7b65e..3a6e5173e 100644 --- a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Queries/PersonGetQuery.cs +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/Queries/PersonGetQuery.cs @@ -1,4 +1,27 @@ -using System.Threading; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System.Threading; using System.Threading.Tasks; using EventFlow.EntityFramework.Tests.Model; using EventFlow.Queries; diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/AddressReadModelEntity.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/AddressReadModelEntity.cs index 0696ac74e..1dfad9ef2 100644 --- a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/AddressReadModelEntity.cs +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/AddressReadModelEntity.cs @@ -1,4 +1,27 @@ -using System.ComponentModel.DataAnnotations; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace EventFlow.EntityFramework.Tests.MsSql.IncludeTests.ReadModels diff --git a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/PersonReadModelEntity.cs b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/PersonReadModelEntity.cs index e029da6fb..9cf43d3f7 100644 --- a/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/PersonReadModelEntity.cs +++ b/Source/EventFlow.EntityFramework.Tests/MsSql/IncludeTests/ReadModels/PersonReadModelEntity.cs @@ -1,4 +1,27 @@ -using System.Collections.Generic; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using EventFlow.Aggregates; diff --git a/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfiguration.cs b/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfiguration.cs index 531187d7b..2e0ba6b92 100644 --- a/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfiguration.cs +++ b/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfiguration.cs @@ -1,4 +1,27 @@ -using System.Linq; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System.Linq; using EventFlow.EntityFramework.ReadStores.Configuration; using EventFlow.ReadStores; diff --git a/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfigurationExtensions.cs b/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfigurationExtensions.cs index f4aea37ec..6b26ca518 100644 --- a/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfigurationExtensions.cs +++ b/Source/EventFlow.EntityFramework/EntityFrameworkReadModelConfigurationExtensions.cs @@ -1,4 +1,27 @@ -using System; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System; using System.Collections.Generic; using System.Linq.Expressions; using EventFlow.EntityFramework.ReadStores.Configuration; diff --git a/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableConfiguration.cs b/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableConfiguration.cs index 67bb3d06f..5e0dcf7d4 100644 --- a/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableConfiguration.cs +++ b/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableConfiguration.cs @@ -1,4 +1,27 @@ -using System.Linq; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System.Linq; using EventFlow.ReadStores; namespace EventFlow.EntityFramework.ReadStores.Configuration diff --git a/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableIncludeConfiguration.cs b/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableIncludeConfiguration.cs index 4b78afbc1..c34681761 100644 --- a/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableIncludeConfiguration.cs +++ b/Source/EventFlow.EntityFramework/ReadStores/Configuration/IApplyQueryableIncludeConfiguration.cs @@ -1,4 +1,27 @@ -using System.Linq; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System.Linq; using EventFlow.ReadStores; using Microsoft.EntityFrameworkCore.Query; diff --git a/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeExpression.cs b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeExpression.cs index 5dd450960..fee263ded 100644 --- a/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeExpression.cs +++ b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeExpression.cs @@ -1,4 +1,27 @@ -using System; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System; using System.Linq; using System.Linq.Expressions; using EventFlow.ReadStores; diff --git a/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeString.cs b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeString.cs index 1d0e7019e..7e573c14b 100644 --- a/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeString.cs +++ b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/IncludeString.cs @@ -1,4 +1,27 @@ -using System.Linq; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System.Linq; using EventFlow.ReadStores; using Microsoft.EntityFrameworkCore; diff --git a/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeEnumerableExpression.cs b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeEnumerableExpression.cs index c7142e5c5..a5c0fe086 100644 --- a/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeEnumerableExpression.cs +++ b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeEnumerableExpression.cs @@ -1,4 +1,27 @@ -using System; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; diff --git a/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeExpression.cs b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeExpression.cs index 0d074ecb5..6588ab5be 100644 --- a/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeExpression.cs +++ b/Source/EventFlow.EntityFramework/ReadStores/Configuration/Includes/ThenIncludeExpression.cs @@ -1,4 +1,27 @@ -using System; +// The MIT License (MIT) +// +// Copyright (c) 2015-2020 Rasmus Mikkelsen +// Copyright (c) 2015-2020 eBay Software Foundation +// 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 System; using System.Linq; using System.Linq.Expressions; using EventFlow.ReadStores; From 206c13ee9f468c47f94b45b91dab25e668d0b26f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Fri, 27 Aug 2021 10:51:49 +0200 Subject: [PATCH 3/3] Added release notes entry for 'eager loading' feature in package EventFlow.EntityFramework --- RELEASE_NOTES.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f3be87178..6e8990f52 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,18 @@ * Fix: Source IDs are now added to snapshots * Fix: InMemoryReadStore will not break on unmodified update result +* New: added extension methods to the `EventFlow.EntityFramework` package that allow + us to configure [eager loading of related data](https://docs.microsoft.com/en-us/ef/core/querying/related-data/eager). Example usage: + ```csharp + public static IEventFlowOptions Configure(this IEventFlowOptions options) + { + return options + .UseEntityFrameworkReadModel( + cfg => cfg.Include(x => x.SomeProperty) + .ThenInclude(y => y.SomeOtherProperty) + ); + } + ``` ### New in 0.81.4483 (released 2020-12-14)