diff --git a/src/Squidex.Domain.Apps.Core.Model/SquidexCoreModel.cs b/src/Squidex.Domain.Apps.Core.Model/SquidexCoreModel.cs index 860a9421a1..83b058a779 100644 --- a/src/Squidex.Domain.Apps.Core.Model/SquidexCoreModel.cs +++ b/src/Squidex.Domain.Apps.Core.Model/SquidexCoreModel.cs @@ -7,6 +7,8 @@ using System.Reflection; +#pragma warning disable RECS0014 // If all fields, properties and methods members are static, the class can be made static. + namespace Squidex.Domain.Apps.Core { public sealed class SquidexCoreModel diff --git a/src/Squidex.Domain.Apps.Core.Operations/SquidexCoreOperations.cs b/src/Squidex.Domain.Apps.Core.Operations/SquidexCoreOperations.cs index 51c11b47d1..38e3c0b0c4 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/SquidexCoreOperations.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/SquidexCoreOperations.cs @@ -7,6 +7,8 @@ using System.Reflection; +#pragma warning disable RECS0014 // If all fields, properties and methods members are static, the class can be made static. + namespace Squidex.Domain.Apps.Core { public static class SquidexCoreOperations diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs index 189f080523..3c51b334bc 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Threading; using System.Threading.Tasks; using MongoDB.Driver; using Squidex.Domain.Apps.Entities.Assets.State; @@ -18,7 +19,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets { public sealed partial class MongoAssetRepository : ISnapshotStore { - public async Task<(AssetState Value, long Version)> ReadAsync(Guid key) + async Task<(AssetState Value, long Version)> ISnapshotStore.ReadAsync(Guid key) { using (Profiler.TraceMethod()) { @@ -35,7 +36,7 @@ await Collection.Find(x => x.Id == key) } } - public async Task WriteAsync(Guid key, AssetState value, long oldVersion, long newVersion) + async Task ISnapshotStore.WriteAsync(Guid key, AssetState value, long oldVersion, long newVersion) { using (Profiler.TraceMethod()) { @@ -48,7 +49,7 @@ public async Task WriteAsync(Guid key, AssetState value, long oldVersion, long n } } - Task ISnapshotStore.ReadAllAsync(Func callback) + Task ISnapshotStore.ReadAllAsync(Func callback, CancellationToken ct) { throw new NotSupportedException(); } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs index f3c95069c3..c7db4ccaf5 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using MongoDB.Bson; using MongoDB.Driver; using NodaTime; using Squidex.Domain.Apps.Core.Contents; @@ -170,6 +171,18 @@ await Collection.Find(x => x.Id == key) return (null, EtagVersion.NotFound); } + public Task ReadAllAsync(Func callback, Func> getSchema, CancellationToken ct = default) + { + return Collection.Find(new BsonDocument()).ForEachPipelineAsync(async contentEntity => + { + var schema = await getSchema(contentEntity.IndexedAppId, contentEntity.IndexedSchemaId); + + contentEntity.ParseData(schema.SchemaDef, Serializer); + + await callback(SimpleMapper.Map(contentEntity, new ContentState()), contentEntity.Version); + }, ct); + } + public Task CleanupAsync(Guid id) { return Collection.UpdateManyAsync( diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs index 31b46bc647..c45d01aa5d 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Threading; using System.Threading.Tasks; using Squidex.Domain.Apps.Entities.Contents.State; using Squidex.Domain.Apps.Entities.Schemas; @@ -18,7 +19,23 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents { public partial class MongoContentRepository : ISnapshotStore { - public async Task<(ContentState Value, long Version)> ReadAsync(Guid key) + async Task ISnapshotStore.RemoveAsync(Guid key) + { + using (Profiler.TraceMethod()) + { + await contents.RemoveAsync(key); + } + } + + async Task ISnapshotStore.ReadAllAsync(Func callback, CancellationToken ct) + { + using (Profiler.TraceMethod()) + { + await contents.ReadAllAsync(callback, GetSchemaAsync, ct); + } + } + + async Task<(ContentState Value, long Version)> ISnapshotStore.ReadAsync(Guid key) { using (Profiler.TraceMethod()) { @@ -26,7 +43,7 @@ public partial class MongoContentRepository : ISnapshotStore } } - public async Task WriteAsync(Guid key, ContentState value, long oldVersion, long newVersion) + async Task ISnapshotStore.WriteAsync(Guid key, ContentState value, long oldVersion, long newVersion) { using (Profiler.TraceMethod()) { @@ -81,15 +98,5 @@ private async Task GetSchemaAsync(Guid appId, Guid schemaId) return schema; } - - Task ISnapshotStore.RemoveAsync(Guid key) - { - throw new NotSupportedException(); - } - - Task ISnapshotStore.ReadAllAsync(Func callback) - { - throw new NotSupportedException(); - } } } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs b/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs index c1ee425574..d91a5ff4f1 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs @@ -369,11 +369,6 @@ private static AppContributorAssigned CreateInitialOwner(RefToken actor) return new AppContributorAssigned { ContributorId = actor.Identifier, Role = Role.Owner }; } - protected override AppState OnEvent(Envelope @event) - { - return Snapshot.Apply(@event); - } - public Task> GetStateAsync() { return J.AsTask(Snapshot); diff --git a/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs b/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs index 36d8ce960b..ff4f8c2933 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs @@ -142,7 +142,7 @@ protected void On(AppArchived @event) IsArchived = true; } - public AppState Apply(Envelope @event) + public override AppState Apply(Envelope @event) { var payload = (SquidexEvent)@event.Payload; diff --git a/src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs b/src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs index 8173494e3c..7ba5ea4ab2 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs @@ -165,11 +165,6 @@ private void VerifyNotDeleted() } } - protected override AssetState OnEvent(Envelope @event) - { - return Snapshot.Apply(@event); - } - public Task> GetStateAsync(long version = EtagVersion.Any) { return J.AsTask(GetSnapshot(version)); diff --git a/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs b/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs index 6e6ca7dae2..928fc292d8 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs @@ -77,7 +77,7 @@ public override async Task RestoreAsync(Guid appId, BackupReader reader) { await RestoreTagsAsync(appId, reader); - await RebuildManyAsync(assetIds, id => RebuildAsync(id, (e, s) => s.Apply(e))); + await RebuildManyAsync(assetIds, id => RebuildAsync(id)); } private async Task RestoreTagsAsync(Guid appId, BackupReader reader) @@ -110,7 +110,7 @@ private Task ReadAssetAsync(Guid assetId, long fileVersion, BackupReader reader) { try { - await assetStore.UploadAsync(assetId.ToString(), fileVersion, null, stream); + await assetStore.UploadAsync(assetId.ToString(), fileVersion, null, stream, true); } catch (AssetAlreadyExistsException) { diff --git a/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs b/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs index e9ebd33ec3..278c4e6667 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs @@ -89,7 +89,7 @@ protected void On(AssetDeleted @event) IsDeleted = true; } - public AssetState Apply(Envelope @event) + public override AssetState Apply(Envelope @event) { var payload = (SquidexEvent)@event.Payload; diff --git a/src/Squidex.Domain.Apps.Entities/Backup/BackupHandlerWithStore.cs b/src/Squidex.Domain.Apps.Entities/Backup/BackupHandlerWithStore.cs index 3c2cb43548..6e711347e6 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/BackupHandlerWithStore.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/BackupHandlerWithStore.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.States; namespace Squidex.Domain.Apps.Entities.Backup @@ -39,7 +38,7 @@ protected async Task RebuildManyAsync(IEnumerable ids, Func ac } } - protected async Task RebuildAsync(Guid key, Func, TState, TState> func) where TState : IDomainState, new() + protected async Task RebuildAsync(Guid key) where TState : IDomainState, new() { var state = new TState { @@ -48,7 +47,7 @@ protected async Task RebuildManyAsync(IEnumerable ids, Func ac var persistence = store.WithSnapshotsAndEventSourcing(typeof(TGrain), key, (TState s) => state = s, e => { - state = func(e, state); + state = state.Apply(e); state.Version++; }); diff --git a/src/Squidex.Domain.Apps.Entities/Comments/State/CommentsState.cs b/src/Squidex.Domain.Apps.Entities/Comments/State/CommentsState.cs index c5e8184ac2..b9f9eb62e0 100644 --- a/src/Squidex.Domain.Apps.Entities/Comments/State/CommentsState.cs +++ b/src/Squidex.Domain.Apps.Entities/Comments/State/CommentsState.cs @@ -5,9 +5,15 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Squidex.Infrastructure.EventSourcing; + namespace Squidex.Domain.Apps.Entities.Comments.State { public sealed class CommentsState : DomainObjectState { + public override CommentsState Apply(Envelope @event) + { + return this; + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs b/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs index 5f4bef81d1..d62e9c54c4 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs @@ -50,7 +50,7 @@ public override Task RestoreAsync(Guid appId, BackupReader reader) { var contentIds = contentIdsBySchemaId.Values.SelectMany(x => x); - return RebuildManyAsync(contentIds, id => RebuildAsync(id, (e, s) => s.Apply(e))); + return RebuildManyAsync(contentIds, id => RebuildAsync(id)); } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs index f934c0deba..5adf762365 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs @@ -296,11 +296,6 @@ private void VerifyNotDeleted() } } - protected override ContentState OnEvent(Envelope @event) - { - return Snapshot.Apply(@event); - } - private async Task CreateContext(Guid appId, Guid schemaId, Guid contentId, Func message) { var operationContext = diff --git a/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs b/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs index 56936bb0a7..fd375704bb 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs @@ -111,7 +111,7 @@ protected void On(ContentDeleted @event) IsDeleted = true; } - public ContentState Apply(Envelope @event) + public override ContentState Apply(Envelope @event) { var payload = (SquidexEvent)@event.Payload; diff --git a/src/Squidex.Domain.Apps.Entities/Contents/Text/GrainTextIndexer.cs b/src/Squidex.Domain.Apps.Entities/Contents/Text/GrainTextIndexer.cs index 4dc53ce675..e5bcfa60c3 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/Text/GrainTextIndexer.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/Text/GrainTextIndexer.cs @@ -12,7 +12,6 @@ using Orleans; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Entities.Apps; -using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Log; diff --git a/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs b/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs index 58b6341b51..7bafa9c445 100644 --- a/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs +++ b/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs @@ -10,11 +10,12 @@ using NodaTime; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; +using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Entities { public abstract class DomainObjectState : Cloneable, - IDomainState, + IDomainState, IEntity, IEntityWithCreatedBy, IEntityWithLastModifiedBy, @@ -42,6 +43,8 @@ public abstract class DomainObjectState : Cloneable, [DataMember] public long Version { get; set; } = EtagVersion.Empty; + public abstract T Apply(Envelope @event); + public T Clone() { return Clone(x => { }); diff --git a/src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs b/src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs index 248b095426..f6073a4f6f 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs @@ -123,11 +123,6 @@ private void VerifyNotDeleted() } } - protected override RuleState OnEvent(Envelope @event) - { - return Snapshot.Apply(@event); - } - public Task> GetStateAsync() { return J.AsTask(Snapshot); diff --git a/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs b/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs index a12f9a744b..48e3543285 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs @@ -64,7 +64,7 @@ protected void On(RuleDeleted @event) IsDeleted = true; } - public RuleState Apply(Envelope @event) + public override RuleState Apply(Envelope @event) { var payload = (SquidexEvent)@event.Payload; diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs index 167971a341..5905b66fdb 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs @@ -381,11 +381,6 @@ private void VerifyNotDeleted() } } - protected override SchemaState OnEvent(Envelope @event) - { - return Snapshot.Apply(@event); - } - public Task> GetStateAsync() { return J.AsTask(Snapshot); diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs index 800a41687c..922da11a32 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs @@ -137,7 +137,7 @@ protected void On(SchemaDeleted @event) IsDeleted = true; } - public SchemaState Apply(Envelope @event) + public override SchemaState Apply(Envelope @event) { var payload = (SquidexEvent)@event.Payload; diff --git a/src/Squidex.Domain.Apps.Entities/SquidexDomainObjectGrain.cs b/src/Squidex.Domain.Apps.Entities/SquidexDomainObjectGrain.cs index bf9c583278..6d452a60fe 100644 --- a/src/Squidex.Domain.Apps.Entities/SquidexDomainObjectGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/SquidexDomainObjectGrain.cs @@ -14,7 +14,7 @@ namespace Squidex.Domain.Apps.Entities { - public abstract class SquidexDomainObjectGrain : DomainObjectGrain where T : IDomainState, new() + public abstract class SquidexDomainObjectGrain : DomainObjectGrain where T : IDomainState, new() { protected SquidexDomainObjectGrain(IStore store, ISemanticLog log) : base(store, log) diff --git a/src/Squidex.Domain.Apps.Entities/SquidexDomainObjectGrainLogSnapshots.cs b/src/Squidex.Domain.Apps.Entities/SquidexDomainObjectGrainLogSnapshots.cs index 425bdc4d6a..56df2b4a06 100644 --- a/src/Squidex.Domain.Apps.Entities/SquidexDomainObjectGrainLogSnapshots.cs +++ b/src/Squidex.Domain.Apps.Entities/SquidexDomainObjectGrainLogSnapshots.cs @@ -14,7 +14,7 @@ namespace Squidex.Domain.Apps.Entities { - public abstract class SquidexDomainObjectGrainLogSnapshots : LogSnapshotDomainObjectGrain where T : IDomainState, new() + public abstract class SquidexDomainObjectGrainLogSnapshots : LogSnapshotDomainObjectGrain where T : IDomainState, new() { protected SquidexDomainObjectGrainLogSnapshots(IStore store, ISemanticLog log) : base(store, log) diff --git a/src/Squidex.Domain.Apps.Entities/SquidexEntities.cs b/src/Squidex.Domain.Apps.Entities/SquidexEntities.cs index 8d82fce421..4bf944913f 100644 --- a/src/Squidex.Domain.Apps.Entities/SquidexEntities.cs +++ b/src/Squidex.Domain.Apps.Entities/SquidexEntities.cs @@ -7,6 +7,8 @@ using System.Reflection; +#pragma warning disable RECS0014 // If all fields, properties and methods members are static, the class can be made static. + namespace Squidex.Domain.Apps.Entities { public static class SquidexEntities diff --git a/src/Squidex.Domain.Apps.Events/SquidexEvents.cs b/src/Squidex.Domain.Apps.Events/SquidexEvents.cs index 7f85deb57c..e90815bfd8 100644 --- a/src/Squidex.Domain.Apps.Events/SquidexEvents.cs +++ b/src/Squidex.Domain.Apps.Events/SquidexEvents.cs @@ -7,6 +7,8 @@ using System.Reflection; +#pragma warning disable RECS0014 // If all fields, properties and methods members are static, the class can be made static. + namespace Squidex.Domain.Apps.Events { public sealed class SquidexEvents diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs index 631727d921..ec0a29528d 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs @@ -22,6 +22,16 @@ public static class MongoExtensions { private static readonly UpdateOptions Upsert = new UpdateOptions { IsUpsert = true }; + public static async Task CollectionExistsAsync(this IMongoDatabase database, string collectionName) + { + var options = new ListCollectionNamesOptions + { + Filter = new BsonDocument("name", collectionName) + }; + + return (await database.ListCollectionNamesAsync(options)).Any(); + } + public static async Task InsertOneIfNotExistsAsync(this IMongoCollection collection, T document) { try diff --git a/src/Squidex.Infrastructure.MongoDb/States/MongoSnapshotStore.cs b/src/Squidex.Infrastructure.MongoDb/States/MongoSnapshotStore.cs index 2b6cd2643e..61afdd1cfd 100644 --- a/src/Squidex.Infrastructure.MongoDb/States/MongoSnapshotStore.cs +++ b/src/Squidex.Infrastructure.MongoDb/States/MongoSnapshotStore.cs @@ -7,6 +7,7 @@ using System; using System.Linq; +using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Driver; @@ -60,11 +61,11 @@ public async Task WriteAsync(TKey key, T value, long oldVersion, long newVersion } } - public async Task ReadAllAsync(Func callback) + public async Task ReadAllAsync(Func callback, CancellationToken ct = default) { using (Profiler.TraceMethod>()) { - await Collection.Find(new BsonDocument()).ForEachAsync(x => callback(x.Doc, x.Version)); + await Collection.Find(new BsonDocument()).ForEachPipelineAsync(x => callback(x.Doc, x.Version), ct); } } diff --git a/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs b/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs index 16da5a476b..21d4a74368 100644 --- a/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs +++ b/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs @@ -13,7 +13,7 @@ namespace Squidex.Infrastructure.Commands { - public abstract class DomainObjectGrain : DomainObjectGrainBase where T : IDomainState, new() + public abstract class DomainObjectGrain : DomainObjectGrainBase where T : IDomainState, new() { private readonly IStore store; private T snapshot = new T { Version = EtagVersion.Empty }; @@ -66,6 +66,9 @@ protected sealed override async Task WriteAsync(Envelope[] events, long } } - protected abstract T OnEvent(Envelope @event); + protected T OnEvent(Envelope @event) + { + return Snapshot.Apply(@event); + } } } \ No newline at end of file diff --git a/src/Squidex.Infrastructure/Commands/DomainObjectGrainBase.cs b/src/Squidex.Infrastructure/Commands/DomainObjectGrainBase.cs index 61501f7aca..5c3b0d717a 100644 --- a/src/Squidex.Infrastructure/Commands/DomainObjectGrainBase.cs +++ b/src/Squidex.Infrastructure/Commands/DomainObjectGrainBase.cs @@ -15,7 +15,7 @@ namespace Squidex.Infrastructure.Commands { - public abstract class DomainObjectGrainBase : GrainOfGuid, IDomainObjectGrain where T : IDomainState, new() + public abstract class DomainObjectGrainBase : GrainOfGuid, IDomainObjectGrain where T : IDomainState, new() { private readonly List> uncomittedEvents = new List>(); private readonly ISemanticLog log; diff --git a/src/Squidex.Infrastructure/Commands/IDomainState.cs b/src/Squidex.Infrastructure/Commands/IDomainState.cs index ee6891fe43..f20f14ce82 100644 --- a/src/Squidex.Infrastructure/Commands/IDomainState.cs +++ b/src/Squidex.Infrastructure/Commands/IDomainState.cs @@ -5,10 +5,14 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Squidex.Infrastructure.EventSourcing; + namespace Squidex.Infrastructure.Commands { - public interface IDomainState + public interface IDomainState { long Version { get; set; } + + T Apply(Envelope @event); } } diff --git a/src/Squidex.Infrastructure/Commands/LogSnapshotDomainObjectGrain.cs b/src/Squidex.Infrastructure/Commands/LogSnapshotDomainObjectGrain.cs index 10ab25586b..8e820282fe 100644 --- a/src/Squidex.Infrastructure/Commands/LogSnapshotDomainObjectGrain.cs +++ b/src/Squidex.Infrastructure/Commands/LogSnapshotDomainObjectGrain.cs @@ -15,7 +15,7 @@ namespace Squidex.Infrastructure.Commands { - public abstract class LogSnapshotDomainObjectGrain : DomainObjectGrainBase where T : IDomainState, new() + public abstract class LogSnapshotDomainObjectGrain : DomainObjectGrainBase where T : IDomainState, new() { private readonly IStore store; private readonly List snapshots = new List { new T { Version = EtagVersion.Empty } }; @@ -88,6 +88,9 @@ protected sealed override void RestorePreviousSnapshot(T previousSnapshot, long } } - protected abstract T OnEvent(Envelope @event); + protected T OnEvent(Envelope @event) + { + return Snapshot.Apply(@event); + } } } \ No newline at end of file diff --git a/src/Squidex.Infrastructure/SquidexInfrastructure.cs b/src/Squidex.Infrastructure/SquidexInfrastructure.cs index 20444d1388..a76b688dc7 100644 --- a/src/Squidex.Infrastructure/SquidexInfrastructure.cs +++ b/src/Squidex.Infrastructure/SquidexInfrastructure.cs @@ -7,6 +7,8 @@ using System.Reflection; +#pragma warning disable RECS0014 // If all fields, properties and methods members are static, the class can be made static. + namespace Squidex.Infrastructure { public sealed class SquidexInfrastructure diff --git a/src/Squidex.Infrastructure/States/ISnapshotStore.cs b/src/Squidex.Infrastructure/States/ISnapshotStore.cs index 38646e64fe..68243db743 100644 --- a/src/Squidex.Infrastructure/States/ISnapshotStore.cs +++ b/src/Squidex.Infrastructure/States/ISnapshotStore.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Threading; using System.Threading.Tasks; namespace Squidex.Infrastructure.States @@ -20,6 +21,6 @@ public interface ISnapshotStore Task RemoveAsync(TKey key); - Task ReadAllAsync(Func callback); + Task ReadAllAsync(Func callback, CancellationToken ct = default); } } diff --git a/src/Squidex/Config/Domain/EntitiesServices.cs b/src/Squidex/Config/Domain/EntitiesServices.cs index 26d62ef321..d29a8c2564 100644 --- a/src/Squidex/Config/Domain/EntitiesServices.cs +++ b/src/Squidex/Config/Domain/EntitiesServices.cs @@ -249,6 +249,9 @@ public static void AddMyMigrationServices(this IServiceCollection services) services.AddTransientAs() .As(); + services.AddTransientAs() + .As(); + services.AddTransientAs() .As(); diff --git a/src/Squidex/Config/Domain/StoreServices.cs b/src/Squidex/Config/Domain/StoreServices.cs index 5f61cf9d49..218f124a0a 100644 --- a/src/Squidex/Config/Domain/StoreServices.cs +++ b/src/Squidex/Config/Domain/StoreServices.cs @@ -10,8 +10,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Migrate_01.Migrations; +using Migrate_01.Migrations.MongoDb; using MongoDB.Driver; using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Assets.Repositories; @@ -66,6 +65,9 @@ public static void AddMyStoreServices(this IServiceCollection services, IConfigu services.AddTransientAs(c => new DeleteContentCollections(c.GetRequiredService().GetDatabase(mongoContentDatabaseName))) .As(); + services.AddTransientAs(c => new RestructureContentCollection(c.GetRequiredService().GetDatabase(mongoContentDatabaseName))) + .As(); + services.AddSingletonAs() .As(); diff --git a/tests/Squidex.Domain.Users.Tests/DefaultXmlRepositoryTests.cs b/tests/Squidex.Domain.Users.Tests/DefaultXmlRepositoryTests.cs index 9ab5ba0eef..eb3720cc7e 100644 --- a/tests/Squidex.Domain.Users.Tests/DefaultXmlRepositoryTests.cs +++ b/tests/Squidex.Domain.Users.Tests/DefaultXmlRepositoryTests.cs @@ -7,6 +7,7 @@ using System; using System.Linq; +using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; using FakeItEasy; @@ -38,8 +39,8 @@ public void Should_write_new_item_to_store_with_friendly_name() [Fact] public void Should_return_items_from_store() { - A.CallTo(() => store.ReadAllAsync(A>.Ignored)) - .Invokes((Func callback) => + A.CallTo(() => store.ReadAllAsync(A>.Ignored, A.Ignored)) + .Invokes((Func callback, CancellationToken ct) => { callback(new DefaultXmlRepository.State { Xml = "" }, EtagVersion.Any); callback(new DefaultXmlRepository.State { Xml = "" }, EtagVersion.Any); diff --git a/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs b/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs index 4810221d34..dcca158c09 100644 --- a/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Commands/DomainObjectGrainTests.cs @@ -26,38 +26,6 @@ public class DomainObjectGrainTests private readonly Guid id = Guid.NewGuid(); private readonly MyDomainObject sut; - public sealed class MyDomainState : IDomainState - { - public long Version { get; set; } - - public int Value { get; set; } - } - - public sealed class ValueChanged : IEvent - { - public int Value { get; set; } - } - - public sealed class CreateAuto : MyCommand - { - public int Value { get; set; } - } - - public sealed class CreateCustom : MyCommand - { - public int Value { get; set; } - } - - public sealed class UpdateAuto : MyCommand - { - public int Value { get; set; } - } - - public sealed class UpdateCustom : MyCommand - { - public int Value { get; set; } - } - public sealed class MyDomainObject : DomainObjectGrain { public MyDomainObject(IStore store) @@ -100,11 +68,6 @@ protected override Task ExecuteAsync(IAggregateCommand command) return Task.FromResult(null); } - - protected override MyDomainState OnEvent(Envelope @event) - { - return new MyDomainState { Value = ((ValueChanged)@event.Payload).Value }; - } } public DomainObjectGrainTests() diff --git a/tests/Squidex.Infrastructure.Tests/Commands/LogSnapshotDomainObjectGrainTests.cs b/tests/Squidex.Infrastructure.Tests/Commands/LogSnapshotDomainObjectGrainTests.cs index 68ed4cf068..2940c5bf39 100644 --- a/tests/Squidex.Infrastructure.Tests/Commands/LogSnapshotDomainObjectGrainTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Commands/LogSnapshotDomainObjectGrainTests.cs @@ -26,36 +26,11 @@ public class LogSnapshotDomainObjectGrainTests private readonly ISnapshotStore snapshotStore = A.Fake>(); private readonly IPersistence persistence = A.Fake(); private readonly Guid id = Guid.NewGuid(); - private readonly MyDomainObject sut; + private readonly MyLogDomainObject sut; - public sealed class ValueChanged : IEvent + public sealed class MyLogDomainObject : LogSnapshotDomainObjectGrain { - public int Value { get; set; } - } - - public sealed class CreateAuto : MyCommand - { - public int Value { get; set; } - } - - public sealed class CreateCustom : MyCommand - { - public int Value { get; set; } - } - - public sealed class UpdateAuto : MyCommand - { - public int Value { get; set; } - } - - public sealed class UpdateCustom : MyCommand - { - public int Value { get; set; } - } - - public sealed class MyDomainObject : LogSnapshotDomainObjectGrain - { - public MyDomainObject(IStore store) + public MyLogDomainObject(IStore store) : base(store, A.Dummy()) { } @@ -95,22 +70,17 @@ protected override Task ExecuteAsync(IAggregateCommand command) return Task.FromResult(null); } - - protected override MyDomainState OnEvent(Envelope @event) - { - return new MyDomainState { Value = ((ValueChanged)@event.Payload).Value }; - } } public LogSnapshotDomainObjectGrainTests() { - A.CallTo(() => store.WithEventSourcing(typeof(MyDomainObject), id, A.Ignored)) + A.CallTo(() => store.WithEventSourcing(typeof(MyLogDomainObject), id, A.Ignored)) .Returns(persistence); A.CallTo(() => store.GetSnapshotStore()) .Returns(snapshotStore); - sut = new MyDomainObject(store); + sut = new MyLogDomainObject(store); } [Fact] diff --git a/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainObject.cs b/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainObject.cs index e1ba8453f3..2202d2b1bf 100644 --- a/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainObject.cs +++ b/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainObject.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using FakeItEasy; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Log; using Squidex.Infrastructure.States; @@ -17,31 +16,6 @@ namespace Squidex.Infrastructure.TestHelpers { public sealed class MyDomainObject : DomainObjectGrain { - public sealed class ValueChanged : IEvent - { - public int Value { get; set; } - } - - public sealed class CreateAuto : MyCommand - { - public int Value { get; set; } - } - - public sealed class CreateCustom : MyCommand - { - public int Value { get; set; } - } - - public sealed class UpdateAuto : MyCommand - { - public int Value { get; set; } - } - - public sealed class UpdateCustom : MyCommand - { - public int Value { get; set; } - } - public MyDomainObject(IStore store) : base(store, A.Dummy()) { @@ -82,10 +56,25 @@ protected override Task ExecuteAsync(IAggregateCommand command) return Task.FromResult(null); } + } - protected override MyDomainState OnEvent(Envelope @event) - { - return new MyDomainState { Value = ((ValueChanged)@event.Payload).Value }; - } + public sealed class CreateAuto : MyCommand + { + public int Value { get; set; } + } + + public sealed class CreateCustom : MyCommand + { + public int Value { get; set; } + } + + public sealed class UpdateAuto : MyCommand + { + public int Value { get; set; } + } + + public sealed class UpdateCustom : MyCommand + { + public int Value { get; set; } } } diff --git a/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs b/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs index 2ca95b0989..7af864c866 100644 --- a/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs +++ b/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs @@ -6,13 +6,24 @@ // ========================================================================== using Squidex.Infrastructure.Commands; +using Squidex.Infrastructure.EventSourcing; namespace Squidex.Infrastructure.TestHelpers { - public sealed class MyDomainState : IDomainState + public sealed class MyDomainState : IDomainState { public long Version { get; set; } public int Value { get; set; } + + public MyDomainState Apply(Envelope @event) + { + return new MyDomainState { Value = ((ValueChanged)@event.Payload).Value }; + } + } + + public sealed class ValueChanged : IEvent + { + public int Value { get; set; } } } diff --git a/tests/Squidex.Infrastructure.Tests/TestHelpers/MyGrain.cs b/tests/Squidex.Infrastructure.Tests/TestHelpers/MyGrain.cs index 3bec4b7c62..6f9c0717ca 100644 --- a/tests/Squidex.Infrastructure.Tests/TestHelpers/MyGrain.cs +++ b/tests/Squidex.Infrastructure.Tests/TestHelpers/MyGrain.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using FakeItEasy; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Log; using Squidex.Infrastructure.States; @@ -26,10 +25,5 @@ protected override Task ExecuteAsync(IAggregateCommand command) { return Task.FromResult(null); } - - protected override MyDomainState OnEvent(Envelope @event) - { - return Snapshot; - } } } diff --git a/tools/Migrate_01/MigrationPath.cs b/tools/Migrate_01/MigrationPath.cs index b67e2f6538..4879a20d41 100644 --- a/tools/Migrate_01/MigrationPath.cs +++ b/tools/Migrate_01/MigrationPath.cs @@ -10,6 +10,7 @@ using System.Linq; using Microsoft.Extensions.DependencyInjection; using Migrate_01.Migrations; +using Migrate_01.Migrations.MongoDb; using Squidex.Infrastructure.Migrations; namespace Migrate_01 @@ -72,8 +73,7 @@ private IEnumerable ResolveMigrators(int version) } // Version 11: Introduce content drafts. - // Version 15: Introduce custom full text search actors. - if (version < 15) + if (version < 11) { yield return serviceProvider.GetService(); yield return serviceProvider.GetRequiredService(); @@ -97,6 +97,13 @@ private IEnumerable ResolveMigrators(int version) yield return serviceProvider.GetRequiredService(); } + // Version 15: Introduce custom full text search actors. + if (version < 15) + { + yield return serviceProvider.GetService(); + yield return serviceProvider.GetService(); + } + yield return serviceProvider.GetRequiredService(); } } diff --git a/tools/Migrate_01/Migrations/BuildFullTextIndices.cs b/tools/Migrate_01/Migrations/BuildFullTextIndices.cs new file mode 100644 index 0000000000..b4f2dbb7a0 --- /dev/null +++ b/tools/Migrate_01/Migrations/BuildFullTextIndices.cs @@ -0,0 +1,50 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Threading.Tasks; +using System.Threading.Tasks.Dataflow; +using Squidex.Domain.Apps.Entities.Contents.State; +using Squidex.Domain.Apps.Entities.Contents.Text; +using Squidex.Infrastructure.Migrations; +using Squidex.Infrastructure.States; + +namespace Migrate_01.Migrations +{ + public sealed class BuildFullTextIndices : IMigration + { + private readonly ITextIndexer textIndexer; + private readonly IStore store; + + public BuildFullTextIndices(ITextIndexer textIndexer, IStore store) + { + this.textIndexer = textIndexer; + + this.store = store; + } + + public async Task UpdateAsync() + { + var snapshotStore = store.GetSnapshotStore(); + + var worker = new ActionBlock(state => + { + return textIndexer.IndexAsync(state.SchemaId.Id, state.Id, state.Data, state.DataDraft); + }, + new ExecutionDataflowBlockOptions + { + MaxDegreeOfParallelism = Environment.ProcessorCount * 2 + }); + + await snapshotStore.ReadAllAsync((state, version) => worker.SendAsync(state)); + + worker.Complete(); + + await worker.Completion; + } + } +} diff --git a/tools/Migrate_01/Migrations/ConvertOldSnapshotStores.cs b/tools/Migrate_01/Migrations/MongoDb/ConvertOldSnapshotStores.cs similarity index 97% rename from tools/Migrate_01/Migrations/ConvertOldSnapshotStores.cs rename to tools/Migrate_01/Migrations/MongoDb/ConvertOldSnapshotStores.cs index 58c3f86432..2fd0cdf3fb 100644 --- a/tools/Migrate_01/Migrations/ConvertOldSnapshotStores.cs +++ b/tools/Migrate_01/Migrations/MongoDb/ConvertOldSnapshotStores.cs @@ -14,7 +14,7 @@ using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.Tasks; -namespace Migrate_01.Migrations +namespace Migrate_01.Migrations.MongoDb { public sealed class ConvertOldSnapshotStores : IMigration { diff --git a/tools/Migrate_01/Migrations/ConvertRuleEventsJson.cs b/tools/Migrate_01/Migrations/MongoDb/ConvertRuleEventsJson.cs similarity index 97% rename from tools/Migrate_01/Migrations/ConvertRuleEventsJson.cs rename to tools/Migrate_01/Migrations/MongoDb/ConvertRuleEventsJson.cs index 4a287e975c..220e019424 100644 --- a/tools/Migrate_01/Migrations/ConvertRuleEventsJson.cs +++ b/tools/Migrate_01/Migrations/MongoDb/ConvertRuleEventsJson.cs @@ -10,7 +10,7 @@ using MongoDB.Driver; using Squidex.Infrastructure.Migrations; -namespace Migrate_01.Migrations +namespace Migrate_01.Migrations.MongoDb { public sealed class ConvertRuleEventsJson : IMigration { diff --git a/tools/Migrate_01/Migrations/DeleteContentCollections.cs b/tools/Migrate_01/Migrations/MongoDb/DeleteContentCollections.cs similarity index 96% rename from tools/Migrate_01/Migrations/DeleteContentCollections.cs rename to tools/Migrate_01/Migrations/MongoDb/DeleteContentCollections.cs index b731e0d574..cafa08eac8 100644 --- a/tools/Migrate_01/Migrations/DeleteContentCollections.cs +++ b/tools/Migrate_01/Migrations/MongoDb/DeleteContentCollections.cs @@ -9,7 +9,7 @@ using MongoDB.Driver; using Squidex.Infrastructure.Migrations; -namespace Migrate_01.Migrations +namespace Migrate_01.Migrations.MongoDb { public sealed class DeleteContentCollections : IMigration { diff --git a/tools/Migrate_01/Migrations/MongoDb/RestructureContentCollection.cs b/tools/Migrate_01/Migrations/MongoDb/RestructureContentCollection.cs new file mode 100644 index 0000000000..38987dc2be --- /dev/null +++ b/tools/Migrate_01/Migrations/MongoDb/RestructureContentCollection.cs @@ -0,0 +1,39 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Driver; +using Squidex.Infrastructure.Migrations; +using Squidex.Infrastructure.MongoDb; + +namespace Migrate_01.Migrations.MongoDb +{ + public sealed class RestructureContentCollection : IMigration + { + private readonly IMongoDatabase contentDatabase; + + public RestructureContentCollection(IMongoDatabase contentDatabase) + { + this.contentDatabase = contentDatabase; + } + + public async Task UpdateAsync() + { + if (await contentDatabase.CollectionExistsAsync("State_Content_Draft")) + { + await contentDatabase.DropCollectionAsync("State_Contents"); + await contentDatabase.DropCollectionAsync("State_Content_Published"); + await contentDatabase.RenameCollectionAsync("State_Content_Draft", "State_Contents"); + + var collection = contentDatabase.GetCollection("State_Content"); + + await collection.UpdateManyAsync(new BsonDocument(), Builders.Update.Unset("dt")); + } + } + } +} diff --git a/tools/Migrate_01/Rebuilder.cs b/tools/Migrate_01/Rebuilder.cs index c7a034effe..7a3a81811b 100644 --- a/tools/Migrate_01/Rebuilder.cs +++ b/tools/Migrate_01/Rebuilder.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; using Squidex.Domain.Apps.Entities.Apps; @@ -44,97 +43,82 @@ public Rebuilder( this.store = store; } - public async Task RebuildAppsAsync() + public Task RebuildAppsAsync() { - await store.GetSnapshotStore().ClearAsync(); - - await RebuildManyAsync("^app\\-", id => RebuildAsync(id, (e, s) => s.Apply(e))); + return RebuildManyAsync("^app\\-"); } - public async Task RebuildSchemasAsync() + public Task RebuildSchemasAsync() { - await store.GetSnapshotStore().ClearAsync(); - - await RebuildManyAsync("^schema\\-", id => RebuildAsync(id, (e, s) => s.Apply(e))); + return RebuildManyAsync("^schema\\-"); } - public async Task RebuildRulesAsync() + public Task RebuildRulesAsync() { - await store.GetSnapshotStore().ClearAsync(); - - await RebuildManyAsync("^rule\\-", id => RebuildAsync(id, (e, s) => s.Apply(e))); + return RebuildManyAsync("^rule\\-"); } - public async Task RebuildAssetsAsync() + public Task RebuildAssetsAsync() { - await store.GetSnapshotStore().ClearAsync(); + return RebuildManyAsync("^asset\\-"); + } - await RebuildManyAsync("^asset\\-", id => RebuildAsync(id, (e, s) => s.Apply(e))); + public Task RebuildContentAsync() + { + return RebuildManyAsync("^content\\-"); } - public async Task RebuildContentAsync() + private async Task RebuildManyAsync(string filter) where TState : IDomainState, new() { - using (localCache.StartContext()) - { - await store.GetSnapshotStore().ClearAsync(); + var handledIds = new HashSet(); - await RebuildManyAsync("^content\\-", async id => + var worker = new ActionBlock(async id => { try { - await RebuildAsync(id, (e, s) => s.Apply(e)); + var state = new TState + { + Version = EtagVersion.Empty + }; + + var persistence = store.WithSnapshotsAndEventSourcing(typeof(TGrain), id, (TState s) => state = s, e => + { + state = state.Apply(e); + + state.Version++; + }); + + await persistence.ReadAsync(); + await persistence.WriteSnapshotAsync(state); } catch (DomainObjectNotFoundException) { return; } - }); - } - } - - private async Task RebuildManyAsync(string filter, Func action) - { - var handledIds = new HashSet(); - - var worker = new ActionBlock(action, + }, new ExecutionDataflowBlockOptions { - MaxDegreeOfParallelism = 32 + MaxDegreeOfParallelism = Environment.ProcessorCount * 2 }); - await eventStore.QueryAsync(async storedEvent => + using (localCache.StartContext()) { - var headers = storedEvent.Data.Headers; + await store.GetSnapshotStore().ClearAsync(); - var id = headers.AggregateId(); - - if (handledIds.Add(id)) + await eventStore.QueryAsync(async storedEvent => { - await worker.SendAsync(id); - } - }, filter, ct: CancellationToken.None); - - worker.Complete(); - - await worker.Completion; - } + var id = storedEvent.Data.Headers.AggregateId(); - private async Task RebuildAsync(Guid key, Func, TState, TState> func) where TState : IDomainState, new() - { - var state = new TState - { - Version = EtagVersion.Empty - }; - - var persistence = store.WithSnapshotsAndEventSourcing(typeof(TGrain), key, (TState s) => state = s, e => - { - state = func(e, state); + if (handledIds.Add(id)) + { + await worker.SendAsync(id); + } + }, filter); - state.Version++; - }); + worker.Complete(); - await persistence.ReadAsync(); - await persistence.WriteSnapshotAsync(state); + await worker.Completion; + } } } } \ No newline at end of file diff --git a/tools/Migrate_01/SquidexMigrations.cs b/tools/Migrate_01/SquidexMigrations.cs index 87fe65348a..9ca97bdc63 100644 --- a/tools/Migrate_01/SquidexMigrations.cs +++ b/tools/Migrate_01/SquidexMigrations.cs @@ -7,6 +7,8 @@ using System.Reflection; +#pragma warning disable RECS0014 // If all fields, properties and methods members are static, the class can be made static. + namespace Migrate_01 { public sealed class SquidexMigrations