diff --git a/src/EventSourcingTests/Projections/CodeGeneration/ProjectionCodeGenerationTests.cs b/src/EventSourcingTests/Projections/CodeGeneration/ProjectionCodeGenerationTests.cs index 974dac1b4b..a96e2db6bc 100644 --- a/src/EventSourcingTests/Projections/CodeGeneration/ProjectionCodeGenerationTests.cs +++ b/src/EventSourcingTests/Projections/CodeGeneration/ProjectionCodeGenerationTests.cs @@ -5,6 +5,7 @@ using Marten; using Marten.Events.Aggregation; using Marten.Events.Projections; +using Marten.Internal.CodeGeneration; using Shouldly; using Xunit; @@ -13,7 +14,7 @@ namespace EventSourcingTests.Projections.CodeGeneration; public class ProjectionCodeGenerationTests { [Fact] - public void Snapshot_GeneratesCodeFile() + public void Snapshot_GeneratesCodeFiles() { var options = new StoreOptions(); options.Connection("Dummy"); @@ -28,11 +29,15 @@ public void Snapshot_GeneratesCodeFile() store.Events.As().BuildFiles() .OfType>() .ShouldHaveSingleItem(); - } + options.BuildFiles() + .OfType() + .Where(e => e.ProviderName == typeof(Something).ToSuffixedTypeName("Provider")) + .ShouldHaveSingleItem(); + } [Fact] - public void LiveStreamAggregation_GeneratesCodeFile() + public void LiveStreamAggregation_GeneratesCodeFiles() { var options = new StoreOptions(); options.Connection("Dummy"); @@ -47,18 +52,91 @@ public void LiveStreamAggregation_GeneratesCodeFile() store.Events.As().BuildFiles() .OfType>() .ShouldHaveSingleItem(); + + options.BuildFiles() + .OfType() + .Where(e => e.ProviderName == typeof(Something).ToSuffixedTypeName("Provider")) + .ShouldHaveSingleItem(); + } + + [Fact] + public void SingleStreamProjection_GeneratesCodeFiles() + { + var options = new StoreOptions(); + options.Connection("Dummy"); + + // Given + options.Projections.Add(ProjectionLifecycle.Inline); + + // When + var store = new DocumentStore(options); + + // Then + store.Events.As().BuildFiles() + .OfType>() + .ShouldHaveSingleItem(); + + options.BuildFiles() + .OfType() + .Where(e => e.ProviderName == typeof(SomethingElse).ToSuffixedTypeName("Provider")) + .ShouldHaveSingleItem(); + } + + [Fact] + public void MultiStreamProjection_GeneratesCodeFiles() + { + var options = new StoreOptions(); + options.Connection("Dummy"); + + // Given + options.Projections.Add(ProjectionLifecycle.Inline); + + // When + var store = new DocumentStore(options); + + // Then + store.Events.As().BuildFiles() + .OfType>() + .ShouldHaveSingleItem(); + + options.BuildFiles() + .OfType() + .Where(e => e.ProviderName == typeof(SomethingElse).ToSuffixedTypeName("Provider")) + .ShouldHaveSingleItem(); } - public record SomethingHappened(Guid SomethingId, string SomethingSomething); + public record SomethingHasHappened(Guid SomethingId, string SomethingSomething); - public record SomethingDifferentHappened(Guid SomethingId, string SomethingSomething); + public record SomethingElseHasHappened(Guid SomethingId, string SomethingSomething); public record Something(Guid Id, string SomethingSomething) { - public static Something Create(SomethingHappened @event) => + public static Something Create(SomethingHasHappened @event) => new Something(@event.SomethingId, @event.SomethingSomething); - public Something Apply(SomethingHappened @event) => + public Something Apply(SomethingElseHasHappened @event) => + this with { SomethingSomething = @event.SomethingSomething }; + } + + public record SomethingElse(Guid Id, string SomethingSomething) + { + public static SomethingElse Create(SomethingHasHappened @event) => + new SomethingElse(@event.SomethingId, @event.SomethingSomething); + + public SomethingElse Apply(SomethingElseHasHappened @event) => this with { SomethingSomething = @event.SomethingSomething }; } + + public class SomethingElseSingleStreamProjection: SingleStreamProjection + { + } + + public class SomethingElseMultiStreamProjection: MultiStreamProjection + { + public SomethingElseMultiStreamProjection() + { + Identity(e => e.SomethingId); + Identity(e => e.SomethingId); + } + } } diff --git a/src/Marten/Events/Aggregation/GeneratedAggregateProjectionBase.Validation.cs b/src/Marten/Events/Aggregation/GeneratedAggregateProjectionBase.Validation.cs index 11a4cf9252..c3015f95a0 100644 --- a/src/Marten/Events/Aggregation/GeneratedAggregateProjectionBase.Validation.cs +++ b/src/Marten/Events/Aggregation/GeneratedAggregateProjectionBase.Validation.cs @@ -19,11 +19,7 @@ protected virtual void specialAssertValid() { } internal override IEnumerable ValidateConfiguration(StoreOptions options) { - // Need to use an isolated DocumentMapping for live aggregations to prevent - // Marten from building empty tables for the aggregate type - var mapping = Lifecycle == ProjectionLifecycle.Live - ? new DocumentMapping(typeof(T), options) - : options.Storage.FindMapping(typeof(T)).Root.As(); + var mapping = options.Storage.FindMapping(typeof(T)).Root.As(); foreach (var p in validateDocumentIdentity(options, mapping)) yield return p; diff --git a/src/Marten/Events/Projections/ProjectionOptions.cs b/src/Marten/Events/Projections/ProjectionOptions.cs index 563c7e1189..83c04408a9 100644 --- a/src/Marten/Events/Projections/ProjectionOptions.cs +++ b/src/Marten/Events/Projections/ProjectionOptions.cs @@ -178,7 +178,7 @@ public MartenRegistry.DocumentMappingExpression LiveStreamAggregation( var expression = singleStreamProjection(ProjectionLifecycle.Live, asyncConfiguration); // Hack to address https://github.com/JasperFx/marten/issues/2610 - _options.Storage.RemoveBuilderFor(); + _options.Storage.MappingFor(typeof(T)).SkipSchemaGeneration = true; return expression; } diff --git a/src/Marten/Schema/DocumentMapping.cs b/src/Marten/Schema/DocumentMapping.cs index c949593d68..4ef43b0447 100644 --- a/src/Marten/Schema/DocumentMapping.cs +++ b/src/Marten/Schema/DocumentMapping.cs @@ -65,7 +65,6 @@ public class DocumentMapping: FieldMapping, IDocumentMapping, IDocumentType private string _alias; private string _databaseSchemaName; - private HiloSettings _hiloSettings; private MemberInfo _idMember; @@ -113,6 +112,9 @@ public HiloSettings HiloSettings } } + // TODO: This should be smarter, maybe nullable option for Schema or some other base type + internal bool SkipSchemaGeneration { get; set; } + public MemberInfo IdMember { get => _idMember; @@ -148,7 +150,6 @@ public MemberInfo IdMember public DocumentMetadataCollection Metadata { get; } - public bool UseOptimisticConcurrency { get; set; } public IList Indexes { get; } = new List(); @@ -191,6 +192,7 @@ public string Alias public bool StructuralTyped { get; set; } public string DdlTemplate { get; set; } + IReadOnlyHiloSettings IDocumentType.HiloSettings { get; } public TenancyStyle TenancyStyle { get; set; } = TenancyStyle.Single; @@ -199,7 +201,6 @@ public string Alias public DuplicatedField[] DuplicatedFields => fields().OfType().ToArray(); - public bool IsHierarchy() { return SubClasses.Any() || DocumentType.GetTypeInfo().IsAbstract || DocumentType.GetTypeInfo().IsInterface; @@ -210,7 +211,6 @@ public IEnumerable IndexesFor(string column) return Indexes.OfType().Where(x => x.Columns.Contains(column)); } - public string AliasFor(Type subclassType) { if (subclassType == DocumentType) @@ -330,7 +330,6 @@ private static PropertyInfo[] GetProperties(Type type) .OrderByDescending(x => x.DeclaringType == type).ToArray(); } - public DocumentIndex AddGinIndexToData() { var index = AddIndex("data"); diff --git a/src/Marten/Storage/MartenDatabase.DocumentCleaner.cs b/src/Marten/Storage/MartenDatabase.DocumentCleaner.cs index cf742b1662..90cf771d74 100644 --- a/src/Marten/Storage/MartenDatabase.DocumentCleaner.cs +++ b/src/Marten/Storage/MartenDatabase.DocumentCleaner.cs @@ -83,7 +83,7 @@ public async Task DeleteDocumentsByTypeAsync(Type documentType, CancellationToke public void DeleteDocumentsExcept(params Type[] documentTypes) { - var documentMappings = _options.Storage.AllDocumentMappings.Where(x => !documentTypes.Contains(x.DocumentType)); + var documentMappings = _options.Storage.DocumentMappingsWithSchema.Where(x => !documentTypes.Contains(x.DocumentType)); foreach (var mapping in documentMappings) { var storage = Providers.StorageFor(mapping.DocumentType); @@ -93,7 +93,7 @@ public void DeleteDocumentsExcept(params Type[] documentTypes) public async Task DeleteDocumentsExceptAsync(CancellationToken ct, params Type[] documentTypes) { - var documentMappings = _options.Storage.AllDocumentMappings.Where(x => !documentTypes.Contains(x.DocumentType)); + var documentMappings = _options.Storage.DocumentMappingsWithSchema.Where(x => !documentTypes.Contains(x.DocumentType)); foreach (var mapping in documentMappings) { var storage = Providers.StorageFor(mapping.DocumentType); diff --git a/src/Marten/Storage/StorageFeatures.cs b/src/Marten/Storage/StorageFeatures.cs index eb63919019..683c230f5f 100644 --- a/src/Marten/Storage/StorageFeatures.cs +++ b/src/Marten/Storage/StorageFeatures.cs @@ -54,6 +54,9 @@ internal StorageFeatures(StoreOptions options) internal IEnumerable AllDocumentMappings => _documentMappings.Value.Enumerate().Select(x => x.Value); + internal IEnumerable DocumentMappingsWithSchema => + _documentMappings.Value.Enumerate().Where(x => !x.Value.SkipSchemaGeneration).Select(x => x.Value); + void IFeatureSchema.WritePermissions(Migrator rules, TextWriter writer) { // Nothing @@ -173,7 +176,6 @@ internal DocumentMapping MappingFor(Type documentType) { if (!_documentMappings.Value.TryFind(documentType, out var value)) { - var buildingList = new List(); value = Build(documentType, _options); _documentMappings.Swap(d => d.AddOrUpdate(documentType, value)); } @@ -271,7 +273,7 @@ internal void PostProcessConfiguration() _mappings.Swap(d => d.AddOrUpdate(typeof(IEvent), new EventQueryMapping(_options))); - foreach (var mapping in _documentMappings.Value.Enumerate().Select(x => x.Value)) + foreach (var mapping in DocumentMappingsWithSchema) { foreach (var subClass in mapping.SubClasses) { @@ -290,8 +292,7 @@ internal IEnumerable AllActiveFeatures(IMartenDatabase database) MappingFor(typeof(DeadLetterEvent)).DatabaseSchemaName = _options.Events.DatabaseSchemaName; } - var mappings = _documentMappings.Value - .Enumerate().Select(x => x.Value) + var mappings = DocumentMappingsWithSchema .OrderBy(x => x.DocumentType.Name) .TopologicalSort(m => m.ReferencedTypes() .Select(MappingFor)); @@ -321,7 +322,7 @@ internal IEnumerable AllActiveFeatures(IMartenDatabase database) internal bool SequenceIsRequired() { - return _documentMappings.Value.Enumerate().Select(x => x.Value).Any(x => x.IdStrategy.RequiresSequences); + return DocumentMappingsWithSchema.Any(x => x.IdStrategy.RequiresSequences); } internal IEnumerable GetTypeDependencies(Type type) @@ -384,13 +385,4 @@ internal void IncludeDocumentMappingBuilders(StorageFeatures includedStorage) } } } - - internal void RemoveBuilderFor() - { - _builders.Swap(d => d.Remove(typeof(T))); - _documentMappings.Swap(d => d.Remove(typeof(T))); - _features.Swap(d => d.Remove(typeof(T))); - _mappings.Swap(d => d.Remove(typeof(T))); - - } }