From f1ddea1c95e2df323b758cd9c4d8512ec3398bc6 Mon Sep 17 00:00:00 2001 From: Raymond den Haan Date: Fri, 2 Feb 2024 11:21:29 +0100 Subject: [PATCH] Add key value store --- .../Runtime/AddKeyValueStore.cs | 25 ++ ...DapperWorkflowRuntimePersistenceFeature.cs | 2 + .../Runtime/Records/KeyValuePairRecord.cs | 7 + .../Modules/Runtime/Stores/KeyValueStore.cs | 64 +++ ...0240202095704_AddKeyValueStore.Designer.cs | 405 +++++++++++++++++ .../20240202095704_AddKeyValueStore.cs | 41 ++ .../RuntimeElsaDbContextModelSnapshot.cs | 22 +- ...0240202095953_AddKeyValueStore.Designer.cs | 408 ++++++++++++++++++ .../20240202095953_AddKeyValueStore.cs | 38 ++ .../RuntimeElsaDbContextModelSnapshot.cs | 22 +- .../ManagementElsaDbContextModelSnapshot.cs | 2 +- ...0240202100123_AddKeyValueStore.Designer.cs | 196 +++++++++ .../20240202100123_AddKeyValueStore.cs | 22 + .../ManagementElsaDbContextModelSnapshot.cs | 2 +- ...0240202100045_AddKeyValueStore.Designer.cs | 193 +++++++++ .../20240202100045_AddKeyValueStore.cs | 22 + .../Modules/Runtime/Configurations.cs | 9 +- .../Modules/Runtime/DbContext.cs | 8 + .../Modules/Runtime/KeyValueStore.cs | 36 ++ .../WorkflowRuntimePersistenceFeature.cs | 2 + .../Modules/Runtime/CreateIndices.cs | 20 +- .../Modules/Runtime/KeyValueStore.cs | 39 ++ .../WorkflowRuntimePersistenceFeature.cs | 4 + .../Contracts/IKeyValueStore.cs | 25 ++ .../Entities/SerializedKeyValuePair.cs | 7 + .../Features/WorkflowRuntimeFeature.cs | 6 + .../Stores/MemoryKeyValueStore.cs | 42 ++ 27 files changed, 1663 insertions(+), 6 deletions(-) create mode 100644 src/modules/Elsa.Dapper.Migrations/Runtime/AddKeyValueStore.cs create mode 100644 src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs create mode 100644 src/modules/Elsa.Dapper/Modules/Runtime/Stores/KeyValueStore.cs create mode 100644 src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240202095704_AddKeyValueStore.Designer.cs create mode 100644 src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240202095704_AddKeyValueStore.cs create mode 100644 src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240202095953_AddKeyValueStore.Designer.cs create mode 100644 src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240202095953_AddKeyValueStore.cs create mode 100644 src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240202100123_AddKeyValueStore.Designer.cs create mode 100644 src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240202100123_AddKeyValueStore.cs create mode 100644 src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240202100045_AddKeyValueStore.Designer.cs create mode 100644 src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240202100045_AddKeyValueStore.cs create mode 100644 src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/KeyValueStore.cs create mode 100644 src/modules/Elsa.MongoDb/Modules/Runtime/KeyValueStore.cs create mode 100644 src/modules/Elsa.Workflows.Runtime/Contracts/IKeyValueStore.cs create mode 100644 src/modules/Elsa.Workflows.Runtime/Entities/SerializedKeyValuePair.cs create mode 100644 src/modules/Elsa.Workflows.Runtime/Stores/MemoryKeyValueStore.cs diff --git a/src/modules/Elsa.Dapper.Migrations/Runtime/AddKeyValueStore.cs b/src/modules/Elsa.Dapper.Migrations/Runtime/AddKeyValueStore.cs new file mode 100644 index 000000000..4baae4147 --- /dev/null +++ b/src/modules/Elsa.Dapper.Migrations/Runtime/AddKeyValueStore.cs @@ -0,0 +1,25 @@ +using FluentMigrator; +using JetBrains.Annotations; + +namespace Elsa.Dapper.Migrations.Runtime; + +/// +[Migration(20002, "Elsa:Runtime:AddKeyValueStore")] +[PublicAPI] +public class AddKeyValueStore : Migration +{ + /// + public override void Up() + { + Create + .Table("KeyValuePairs") + .WithColumn("Key").AsString().PrimaryKey() + .WithColumn("Value").AsString().NotNullable(); + } + + /// + public override void Down() + { + Delete.Table("KeyValuePairs"); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Features/DapperWorkflowRuntimePersistenceFeature.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Features/DapperWorkflowRuntimePersistenceFeature.cs index bfa802419..43888dd14 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Features/DapperWorkflowRuntimePersistenceFeature.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Features/DapperWorkflowRuntimePersistenceFeature.cs @@ -31,6 +31,7 @@ public class DapperWorkflowRuntimePersistenceFeature : FeatureBase feature.WorkflowInboxStore = sp => sp.GetRequiredService(); feature.WorkflowExecutionLogStore = sp => sp.GetRequiredService(); feature.ActivityExecutionLogStore = sp => sp.GetRequiredService(); + feature.KeyValueStore = sp => sp.GetRequiredService(); }); } @@ -44,5 +45,6 @@ public class DapperWorkflowRuntimePersistenceFeature : FeatureBase Services.AddScoped(); Services.AddScoped(); Services.AddScoped(); + Services.AddScoped(); } } \ No newline at end of file diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs new file mode 100644 index 000000000..64730f781 --- /dev/null +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs @@ -0,0 +1,7 @@ +namespace Elsa.Dapper.Modules.Runtime.Records; + +public class KeyValuePairRecord +{ + public string Key { get; set; } = default!; + public string Value { get; set; } = default!; +} \ No newline at end of file diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/KeyValueStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/KeyValueStore.cs new file mode 100644 index 000000000..1c0c2e392 --- /dev/null +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/KeyValueStore.cs @@ -0,0 +1,64 @@ +using Elsa.Dapper.Contracts; +using Elsa.Dapper.Extensions; +using Elsa.Dapper.Modules.Runtime.Records; +using Elsa.Dapper.Services; +using Elsa.Workflows.Runtime.Contracts; +using Elsa.Workflows.Runtime.Entities; + +namespace Elsa.Dapper.Modules.Runtime.Stores; + +/// +/// A Dapper implementation of . +/// +public class DapperKeyValueStore : IKeyValueStore +{ + private const string TableName = "Users"; + private const string PrimaryKeyName = "Key"; + private readonly Store _store; + + /// + /// Initializes a new instance of . + /// + public DapperKeyValueStore(IDbConnectionProvider dbConnectionProvider) + { + _store = new Store(dbConnectionProvider, TableName, PrimaryKeyName); + } + + /// + public Task SaveAsync(SerializedKeyValuePair keyValuePair, CancellationToken cancellationToken) + { + var record = Map(keyValuePair); + return _store.SaveAsync(record, PrimaryKeyName, cancellationToken); + } + + /// + public async Task GetValue(string key, CancellationToken cancellationToken) + { + var record = await _store.FindAsync(query => query.Is(nameof(KeyValuePairRecord.Key), key), cancellationToken); + return record == null ? null : Map(record); + } + + /// + public Task DeleteAsync(string key, CancellationToken cancellationToken) + { + return _store.DeleteAsync(query => query.Is(nameof(KeyValuePairRecord.Key), key), cancellationToken); + } + + private KeyValuePairRecord Map(SerializedKeyValuePair kvp) + { + return new() + { + Key = kvp.Key, + Value = kvp.SerializedValue + }; + } + + private SerializedKeyValuePair Map(KeyValuePairRecord kvp) + { + return new() + { + Key = kvp.Key, + SerializedValue = kvp.Value + }; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240202095704_AddKeyValueStore.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240202095704_AddKeyValueStore.Designer.cs new file mode 100644 index 000000000..c15199c95 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240202095704_AddKeyValueStore.Designer.cs @@ -0,0 +1,405 @@ +// +using System; +using Elsa.EntityFrameworkCore.Modules.Runtime; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.MySql.Migrations.Runtime +{ + [DbContext(typeof(RuntimeElsaDbContext))] + [Migration("20240202095704_AddKeyValueStore")] + partial class AddKeyValueStore + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Elsa") + .HasAnnotation("ProductVersion", "7.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.ActivityExecutionRecord", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ActivityId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ActivityName") + .HasColumnType("varchar(255)"); + + b.Property("ActivityNodeId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ActivityType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ActivityTypeVersion") + .HasColumnType("int"); + + b.Property("CompletedAt") + .HasColumnType("datetime(6)"); + + b.Property("HasBookmarks") + .HasColumnType("tinyint(1)"); + + b.Property("SerializedActivityState") + .HasColumnType("longtext"); + + b.Property("SerializedException") + .HasColumnType("longtext"); + + b.Property("SerializedOutputs") + .HasColumnType("longtext"); + + b.Property("SerializedPayload") + .HasColumnType("longtext"); + + b.Property("StartedAt") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("ActivityId") + .HasDatabaseName("IX_ActivityExecutionRecord_ActivityId"); + + b.HasIndex("ActivityName") + .HasDatabaseName("IX_ActivityExecutionRecord_ActivityName"); + + b.HasIndex("ActivityNodeId") + .HasDatabaseName("IX_ActivityExecutionRecord_ActivityNodeId"); + + b.HasIndex("ActivityType") + .HasDatabaseName("IX_ActivityExecutionRecord_ActivityType"); + + b.HasIndex("ActivityTypeVersion") + .HasDatabaseName("IX_ActivityExecutionRecord_ActivityTypeVersion"); + + b.HasIndex("CompletedAt") + .HasDatabaseName("IX_ActivityExecutionRecord_CompletedAt"); + + b.HasIndex("HasBookmarks") + .HasDatabaseName("IX_ActivityExecutionRecord_HasBookmarks"); + + b.HasIndex("StartedAt") + .HasDatabaseName("IX_ActivityExecutionRecord_StartedAt"); + + b.HasIndex("Status") + .HasDatabaseName("IX_ActivityExecutionRecord_Status"); + + b.HasIndex("WorkflowInstanceId") + .HasDatabaseName("IX_ActivityExecutionRecord_WorkflowInstanceId"); + + b.HasIndex("ActivityType", "ActivityTypeVersion") + .HasDatabaseName("IX_ActivityExecutionRecord_ActivityType_ActivityTypeVersion"); + + b.ToTable("ActivityExecutionRecords", "Elsa"); + }); + + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.SerializedKeyValuePair", b => + { + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("SerializedValue") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Key"); + + b.ToTable("KeyValuePairs", "Elsa"); + }); + + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.StoredBookmark", b => + { + b.Property("BookmarkId") + .HasColumnType("varchar(255)"); + + b.Property("ActivityInstanceId") + .HasColumnType("varchar(255)"); + + b.Property("ActivityTypeName") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("CorrelationId") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("SerializedMetadata") + .HasColumnType("longtext"); + + b.Property("SerializedPayload") + .HasColumnType("longtext"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("BookmarkId"); + + b.HasIndex(new[] { "ActivityInstanceId" }, "IX_StoredBookmark_ActivityInstanceId"); + + b.HasIndex(new[] { "ActivityTypeName" }, "IX_StoredBookmark_ActivityTypeName"); + + b.HasIndex(new[] { "ActivityTypeName", "Hash" }, "IX_StoredBookmark_ActivityTypeName_Hash"); + + b.HasIndex(new[] { "ActivityTypeName", "Hash", "WorkflowInstanceId" }, "IX_StoredBookmark_ActivityTypeName_Hash_WorkflowInstanceId"); + + b.HasIndex(new[] { "CreatedAt" }, "IX_StoredBookmark_CreatedAt"); + + b.HasIndex(new[] { "Hash" }, "IX_StoredBookmark_Hash"); + + b.HasIndex(new[] { "WorkflowInstanceId" }, "IX_StoredBookmark_WorkflowInstanceId"); + + b.ToTable("Bookmarks", "Elsa"); + }); + + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.StoredTrigger", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ActivityId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Hash") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("SerializedPayload") + .HasColumnType("longtext"); + + b.Property("WorkflowDefinitionId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("WorkflowDefinitionVersionId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("Hash") + .HasDatabaseName("IX_StoredTrigger_Hash"); + + b.HasIndex("Name") + .HasDatabaseName("IX_StoredTrigger_Name"); + + b.HasIndex("WorkflowDefinitionId") + .HasDatabaseName("IX_StoredTrigger_WorkflowDefinitionId"); + + b.HasIndex("WorkflowDefinitionVersionId") + .HasDatabaseName("IX_StoredTrigger_WorkflowDefinitionVersionId"); + + b.ToTable("Triggers", "Elsa"); + }); + + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.WorkflowExecutionLogRecord", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ActivityId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ActivityInstanceId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ActivityName") + .HasColumnType("varchar(255)"); + + b.Property("ActivityNodeId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ActivityType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ActivityTypeVersion") + .HasColumnType("int"); + + b.Property("EventName") + .HasColumnType("varchar(255)"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("ParentActivityInstanceId") + .HasColumnType("varchar(255)"); + + b.Property("Sequence") + .HasColumnType("bigint"); + + b.Property("SerializedActivityState") + .HasColumnType("longtext"); + + b.Property("SerializedPayload") + .HasColumnType("longtext"); + + b.Property("Source") + .HasColumnType("longtext"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.Property("WorkflowDefinitionId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("WorkflowDefinitionVersionId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("WorkflowVersion") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ActivityId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityId"); + + b.HasIndex("ActivityInstanceId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityInstanceId"); + + b.HasIndex("ActivityName") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityName"); + + b.HasIndex("ActivityNodeId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityNodeId"); + + b.HasIndex("ActivityType") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityType"); + + b.HasIndex("ActivityTypeVersion") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityTypeVersion"); + + b.HasIndex("EventName") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_EventName"); + + b.HasIndex("ParentActivityInstanceId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ParentActivityInstanceId"); + + b.HasIndex("Sequence") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_Sequence"); + + b.HasIndex("Timestamp") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_Timestamp"); + + b.HasIndex("WorkflowDefinitionId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_WorkflowDefinitionId"); + + b.HasIndex("WorkflowDefinitionVersionId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_WorkflowDefinitionVersionId"); + + b.HasIndex("WorkflowInstanceId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_WorkflowInstanceId"); + + b.HasIndex("WorkflowVersion") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_WorkflowVersion"); + + b.HasIndex("ActivityType", "ActivityTypeVersion") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityType_ActivityTypeVersion"); + + b.HasIndex("Timestamp", "Sequence") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_Timestamp_Sequence"); + + b.ToTable("WorkflowExecutionLogRecords", "Elsa"); + }); + + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.WorkflowInboxMessage", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ActivityInstanceId") + .HasColumnType("varchar(255)"); + + b.Property("ActivityTypeName") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("CorrelationId") + .HasColumnType("varchar(255)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("SerializedBookmarkPayload") + .HasColumnType("longtext"); + + b.Property("SerializedInput") + .HasColumnType("longtext"); + + b.Property("WorkflowInstanceId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex(new[] { "ActivityInstanceId" }, "IX_WorkflowInboxMessage_ActivityInstanceId"); + + b.HasIndex(new[] { "ActivityTypeName" }, "IX_WorkflowInboxMessage_ActivityTypeName"); + + b.HasIndex(new[] { "CorrelationId" }, "IX_WorkflowInboxMessage_CorrelationId"); + + b.HasIndex(new[] { "CreatedAt" }, "IX_WorkflowInboxMessage_CreatedAt"); + + b.HasIndex(new[] { "ExpiresAt" }, "IX_WorkflowInboxMessage_ExpiresAt"); + + b.HasIndex(new[] { "Hash" }, "IX_WorkflowInboxMessage_Hash"); + + b.HasIndex(new[] { "WorkflowInstanceId" }, "IX_WorkflowInboxMessage_WorkflowInstanceId"); + + b.ToTable("WorkflowInboxMessages", "Elsa"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240202095704_AddKeyValueStore.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240202095704_AddKeyValueStore.cs new file mode 100644 index 000000000..075cb919c --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240202095704_AddKeyValueStore.cs @@ -0,0 +1,41 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.MySql.Migrations.Runtime +{ + /// + public partial class AddKeyValueStore : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "KeyValuePairs", + schema: "Elsa", + columns: table => new + { + Key = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + SerializedValue = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_KeyValuePairs", x => x.Key); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "KeyValuePairs", + schema: "Elsa"); + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs index 86665b528..dd910f205 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Runtime #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("Elsa") - .HasAnnotation("ProductVersion", "7.0.11") + .HasAnnotation("ProductVersion", "7.0.2") .HasAnnotation("Relational:MaxIdentifierLength", 64); modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.ActivityExecutionRecord", b => @@ -110,6 +110,26 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Runtime b.ToTable("ActivityExecutionRecords", "Elsa"); }); + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.SerializedKeyValuePair", b => + { + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("SerializedValue") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Key"); + + b.ToTable("KeyValuePairs", "Elsa"); + }); + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.StoredBookmark", b => { b.Property("BookmarkId") diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240202095953_AddKeyValueStore.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240202095953_AddKeyValueStore.Designer.cs new file mode 100644 index 000000000..7aae79840 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240202095953_AddKeyValueStore.Designer.cs @@ -0,0 +1,408 @@ +// +using System; +using Elsa.EntityFrameworkCore.Modules.Runtime; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Runtime +{ + [DbContext(typeof(RuntimeElsaDbContext))] + [Migration("20240202095953_AddKeyValueStore")] + partial class AddKeyValueStore + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Elsa") + .HasAnnotation("ProductVersion", "7.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.ActivityExecutionRecord", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ActivityId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ActivityName") + .HasColumnType("text"); + + b.Property("ActivityNodeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ActivityType") + .IsRequired() + .HasColumnType("text"); + + b.Property("ActivityTypeVersion") + .HasColumnType("integer"); + + b.Property("CompletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("HasBookmarks") + .HasColumnType("boolean"); + + b.Property("SerializedActivityState") + .HasColumnType("text"); + + b.Property("SerializedException") + .HasColumnType("text"); + + b.Property("SerializedOutputs") + .HasColumnType("text"); + + b.Property("SerializedPayload") + .HasColumnType("text"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ActivityId") + .HasDatabaseName("IX_ActivityExecutionRecord_ActivityId"); + + b.HasIndex("ActivityName") + .HasDatabaseName("IX_ActivityExecutionRecord_ActivityName"); + + b.HasIndex("ActivityNodeId") + .HasDatabaseName("IX_ActivityExecutionRecord_ActivityNodeId"); + + b.HasIndex("ActivityType") + .HasDatabaseName("IX_ActivityExecutionRecord_ActivityType"); + + b.HasIndex("ActivityTypeVersion") + .HasDatabaseName("IX_ActivityExecutionRecord_ActivityTypeVersion"); + + b.HasIndex("CompletedAt") + .HasDatabaseName("IX_ActivityExecutionRecord_CompletedAt"); + + b.HasIndex("HasBookmarks") + .HasDatabaseName("IX_ActivityExecutionRecord_HasBookmarks"); + + b.HasIndex("StartedAt") + .HasDatabaseName("IX_ActivityExecutionRecord_StartedAt"); + + b.HasIndex("Status") + .HasDatabaseName("IX_ActivityExecutionRecord_Status"); + + b.HasIndex("WorkflowInstanceId") + .HasDatabaseName("IX_ActivityExecutionRecord_WorkflowInstanceId"); + + b.HasIndex("ActivityType", "ActivityTypeVersion") + .HasDatabaseName("IX_ActivityExecutionRecord_ActivityType_ActivityTypeVersion"); + + b.ToTable("ActivityExecutionRecords", "Elsa"); + }); + + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.SerializedKeyValuePair", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SerializedValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Key"); + + b.ToTable("KeyValuePairs", "Elsa"); + }); + + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.StoredBookmark", b => + { + b.Property("BookmarkId") + .HasColumnType("text"); + + b.Property("ActivityInstanceId") + .HasColumnType("text"); + + b.Property("ActivityTypeName") + .IsRequired() + .HasColumnType("text"); + + b.Property("CorrelationId") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("text"); + + b.Property("SerializedMetadata") + .HasColumnType("text"); + + b.Property("SerializedPayload") + .HasColumnType("text"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("BookmarkId"); + + b.HasIndex(new[] { "ActivityInstanceId" }, "IX_StoredBookmark_ActivityInstanceId"); + + b.HasIndex(new[] { "ActivityTypeName" }, "IX_StoredBookmark_ActivityTypeName"); + + b.HasIndex(new[] { "ActivityTypeName", "Hash" }, "IX_StoredBookmark_ActivityTypeName_Hash"); + + b.HasIndex(new[] { "ActivityTypeName", "Hash", "WorkflowInstanceId" }, "IX_StoredBookmark_ActivityTypeName_Hash_WorkflowInstanceId"); + + b.HasIndex(new[] { "CreatedAt" }, "IX_StoredBookmark_CreatedAt"); + + b.HasIndex(new[] { "Hash" }, "IX_StoredBookmark_Hash"); + + b.HasIndex(new[] { "WorkflowInstanceId" }, "IX_StoredBookmark_WorkflowInstanceId"); + + b.ToTable("Bookmarks", "Elsa"); + }); + + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.StoredTrigger", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ActivityId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Hash") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SerializedPayload") + .HasColumnType("text"); + + b.Property("WorkflowDefinitionId") + .IsRequired() + .HasColumnType("text"); + + b.Property("WorkflowDefinitionVersionId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Hash") + .HasDatabaseName("IX_StoredTrigger_Hash"); + + b.HasIndex("Name") + .HasDatabaseName("IX_StoredTrigger_Name"); + + b.HasIndex("WorkflowDefinitionId") + .HasDatabaseName("IX_StoredTrigger_WorkflowDefinitionId"); + + b.HasIndex("WorkflowDefinitionVersionId") + .HasDatabaseName("IX_StoredTrigger_WorkflowDefinitionVersionId"); + + b.ToTable("Triggers", "Elsa"); + }); + + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.WorkflowExecutionLogRecord", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ActivityId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ActivityInstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ActivityName") + .HasColumnType("text"); + + b.Property("ActivityNodeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ActivityType") + .IsRequired() + .HasColumnType("text"); + + b.Property("ActivityTypeVersion") + .HasColumnType("integer"); + + b.Property("EventName") + .HasColumnType("text"); + + b.Property("Message") + .HasColumnType("text"); + + b.Property("ParentActivityInstanceId") + .HasColumnType("text"); + + b.Property("Sequence") + .HasColumnType("bigint"); + + b.Property("SerializedActivityState") + .HasColumnType("text"); + + b.Property("SerializedPayload") + .HasColumnType("text"); + + b.Property("Source") + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("WorkflowDefinitionId") + .IsRequired() + .HasColumnType("text"); + + b.Property("WorkflowDefinitionVersionId") + .IsRequired() + .HasColumnType("text"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("WorkflowVersion") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ActivityId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityId"); + + b.HasIndex("ActivityInstanceId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityInstanceId"); + + b.HasIndex("ActivityName") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityName"); + + b.HasIndex("ActivityNodeId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityNodeId"); + + b.HasIndex("ActivityType") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityType"); + + b.HasIndex("ActivityTypeVersion") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityTypeVersion"); + + b.HasIndex("EventName") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_EventName"); + + b.HasIndex("ParentActivityInstanceId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ParentActivityInstanceId"); + + b.HasIndex("Sequence") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_Sequence"); + + b.HasIndex("Timestamp") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_Timestamp"); + + b.HasIndex("WorkflowDefinitionId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_WorkflowDefinitionId"); + + b.HasIndex("WorkflowDefinitionVersionId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_WorkflowDefinitionVersionId"); + + b.HasIndex("WorkflowInstanceId") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_WorkflowInstanceId"); + + b.HasIndex("WorkflowVersion") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_WorkflowVersion"); + + b.HasIndex("ActivityType", "ActivityTypeVersion") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_ActivityType_ActivityTypeVersion"); + + b.HasIndex("Timestamp", "Sequence") + .HasDatabaseName("IX_WorkflowExecutionLogRecord_Timestamp_Sequence"); + + b.ToTable("WorkflowExecutionLogRecords", "Elsa"); + }); + + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.WorkflowInboxMessage", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ActivityInstanceId") + .HasColumnType("text"); + + b.Property("ActivityTypeName") + .IsRequired() + .HasColumnType("text"); + + b.Property("CorrelationId") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("text"); + + b.Property("SerializedBookmarkPayload") + .HasColumnType("text"); + + b.Property("SerializedInput") + .HasColumnType("text"); + + b.Property("WorkflowInstanceId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex(new[] { "ActivityInstanceId" }, "IX_WorkflowInboxMessage_ActivityInstanceId"); + + b.HasIndex(new[] { "ActivityTypeName" }, "IX_WorkflowInboxMessage_ActivityTypeName"); + + b.HasIndex(new[] { "CorrelationId" }, "IX_WorkflowInboxMessage_CorrelationId"); + + b.HasIndex(new[] { "CreatedAt" }, "IX_WorkflowInboxMessage_CreatedAt"); + + b.HasIndex(new[] { "ExpiresAt" }, "IX_WorkflowInboxMessage_ExpiresAt"); + + b.HasIndex(new[] { "Hash" }, "IX_WorkflowInboxMessage_Hash"); + + b.HasIndex(new[] { "WorkflowInstanceId" }, "IX_WorkflowInboxMessage_WorkflowInstanceId"); + + b.ToTable("WorkflowInboxMessages", "Elsa"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240202095953_AddKeyValueStore.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240202095953_AddKeyValueStore.cs new file mode 100644 index 000000000..4e6f1ff59 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240202095953_AddKeyValueStore.cs @@ -0,0 +1,38 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Runtime +{ + /// + public partial class AddKeyValueStore : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "KeyValuePairs", + schema: "Elsa", + columns: table => new + { + Key = table.Column(type: "text", nullable: false), + SerializedValue = table.Column(type: "text", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_KeyValuePairs", x => x.Key); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "KeyValuePairs", + schema: "Elsa"); + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs index fed171fef..61f543a75 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/RuntimeElsaDbContextModelSnapshot.cs @@ -18,7 +18,7 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Runtime #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("Elsa") - .HasAnnotation("ProductVersion", "7.0.11") + .HasAnnotation("ProductVersion", "7.0.14") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -113,6 +113,26 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Runtime b.ToTable("ActivityExecutionRecords", "Elsa"); }); + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.SerializedKeyValuePair", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SerializedValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Key"); + + b.ToTable("KeyValuePairs", "Elsa"); + }); + modelBuilder.Entity("Elsa.Workflows.Runtime.Entities.StoredBookmark", b => { b.Property("BookmarkId") diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/ManagementElsaDbContextModelSnapshot.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/ManagementElsaDbContextModelSnapshot.cs index 29d7599be..defa876d8 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/ManagementElsaDbContextModelSnapshot.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/ManagementElsaDbContextModelSnapshot.cs @@ -18,7 +18,7 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Management #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("Elsa") - .HasAnnotation("ProductVersion", "7.0.11") + .HasAnnotation("ProductVersion", "7.0.14") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240202100123_AddKeyValueStore.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240202100123_AddKeyValueStore.Designer.cs new file mode 100644 index 000000000..5d7d27d93 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240202100123_AddKeyValueStore.Designer.cs @@ -0,0 +1,196 @@ +// +using System; +using Elsa.EntityFrameworkCore.Modules.Management; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Runtime +{ + [DbContext(typeof(ManagementElsaDbContext))] + [Migration("20240202100123_AddKeyValueStore")] + partial class AddKeyValueStore + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Elsa") + .HasAnnotation("ProductVersion", "7.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Elsa.Workflows.Management.Entities.WorkflowDefinition", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("BinaryData") + .HasColumnType("varbinary(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Data") + .HasColumnType("nvarchar(max)"); + + b.Property("DefinitionId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("IsLatest") + .HasColumnType("bit"); + + b.Property("IsPublished") + .HasColumnType("bit"); + + b.Property("IsReadonly") + .HasColumnType("bit"); + + b.Property("MaterializerContext") + .HasColumnType("nvarchar(max)"); + + b.Property("MaterializerName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderName") + .HasColumnType("nvarchar(max)"); + + b.Property("StringData") + .HasColumnType("nvarchar(max)"); + + b.Property("ToolVersion") + .HasColumnType("nvarchar(max)"); + + b.Property("UsableAsActivity") + .HasColumnType("bit"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("IsLatest") + .HasDatabaseName("IX_WorkflowDefinition_IsLatest"); + + b.HasIndex("IsPublished") + .HasDatabaseName("IX_WorkflowDefinition_IsPublished"); + + b.HasIndex("Name") + .HasDatabaseName("IX_WorkflowDefinition_Name"); + + b.HasIndex("UsableAsActivity") + .HasDatabaseName("IX_WorkflowDefinition_UsableAsActivity"); + + b.HasIndex("Version") + .HasDatabaseName("IX_WorkflowDefinition_Version"); + + b.HasIndex("DefinitionId", "Version") + .IsUnique() + .HasDatabaseName("IX_WorkflowDefinition_DefinitionId_Version"); + + b.ToTable("WorkflowDefinitions", "Elsa"); + }); + + modelBuilder.Entity("Elsa.Workflows.Management.Entities.WorkflowInstance", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("CorrelationId") + .HasColumnType("nvarchar(450)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Data") + .HasColumnType("nvarchar(max)"); + + b.Property("DefinitionId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("DefinitionVersionId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FinishedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IncidentCount") + .HasColumnType("int"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("SubStatus") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("UpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CorrelationId") + .HasDatabaseName("IX_WorkflowInstance_CorrelationId"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_WorkflowInstance_CreatedAt"); + + b.HasIndex("DefinitionId") + .HasDatabaseName("IX_WorkflowInstance_DefinitionId"); + + b.HasIndex("FinishedAt") + .HasDatabaseName("IX_WorkflowInstance_FinishedAt"); + + b.HasIndex("Name") + .HasDatabaseName("IX_WorkflowInstance_Name"); + + b.HasIndex("Status") + .HasDatabaseName("IX_WorkflowInstance_Status"); + + b.HasIndex("SubStatus") + .HasDatabaseName("IX_WorkflowInstance_SubStatus"); + + b.HasIndex("UpdatedAt") + .HasDatabaseName("IX_WorkflowInstance_UpdatedAt"); + + b.HasIndex("Status", "DefinitionId") + .HasDatabaseName("IX_WorkflowInstance_Status_DefinitionId"); + + b.HasIndex("Status", "SubStatus") + .HasDatabaseName("IX_WorkflowInstance_Status_SubStatus"); + + b.HasIndex("SubStatus", "DefinitionId") + .HasDatabaseName("IX_WorkflowInstance_SubStatus_DefinitionId"); + + b.HasIndex("Status", "SubStatus", "DefinitionId", "Version") + .HasDatabaseName("IX_WorkflowInstance_Status_SubStatus_DefinitionId_Version"); + + b.ToTable("WorkflowInstances", "Elsa"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240202100123_AddKeyValueStore.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240202100123_AddKeyValueStore.cs new file mode 100644 index 000000000..d76ed03a7 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240202100123_AddKeyValueStore.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Runtime +{ + /// + public partial class AddKeyValueStore : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/ManagementElsaDbContextModelSnapshot.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/ManagementElsaDbContextModelSnapshot.cs index 5fcd8c784..22d9a56aa 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/ManagementElsaDbContextModelSnapshot.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/ManagementElsaDbContextModelSnapshot.cs @@ -15,7 +15,7 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Management protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "7.0.11"); + modelBuilder.HasAnnotation("ProductVersion", "7.0.14"); modelBuilder.Entity("Elsa.Workflows.Management.Entities.WorkflowDefinition", b => { diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240202100045_AddKeyValueStore.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240202100045_AddKeyValueStore.Designer.cs new file mode 100644 index 000000000..2370a6707 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240202100045_AddKeyValueStore.Designer.cs @@ -0,0 +1,193 @@ +// +using System; +using Elsa.EntityFrameworkCore.Modules.Management; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Runtime +{ + [DbContext(typeof(ManagementElsaDbContext))] + [Migration("20240202100045_AddKeyValueStore")] + partial class AddKeyValueStore + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.14"); + + modelBuilder.Entity("Elsa.Workflows.Management.Entities.WorkflowDefinition", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BinaryData") + .HasColumnType("BLOB"); + + b.Property("CreatedAt") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DefinitionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("IsLatest") + .HasColumnType("INTEGER"); + + b.Property("IsPublished") + .HasColumnType("INTEGER"); + + b.Property("IsReadonly") + .HasColumnType("INTEGER"); + + b.Property("MaterializerContext") + .HasColumnType("TEXT"); + + b.Property("MaterializerName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("ProviderName") + .HasColumnType("TEXT"); + + b.Property("StringData") + .HasColumnType("TEXT"); + + b.Property("ToolVersion") + .HasColumnType("TEXT"); + + b.Property("UsableAsActivity") + .HasColumnType("INTEGER"); + + b.Property("Version") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("IsLatest") + .HasDatabaseName("IX_WorkflowDefinition_IsLatest"); + + b.HasIndex("IsPublished") + .HasDatabaseName("IX_WorkflowDefinition_IsPublished"); + + b.HasIndex("Name") + .HasDatabaseName("IX_WorkflowDefinition_Name"); + + b.HasIndex("UsableAsActivity") + .HasDatabaseName("IX_WorkflowDefinition_UsableAsActivity"); + + b.HasIndex("Version") + .HasDatabaseName("IX_WorkflowDefinition_Version"); + + b.HasIndex("DefinitionId", "Version") + .IsUnique() + .HasDatabaseName("IX_WorkflowDefinition_DefinitionId_Version"); + + b.ToTable("WorkflowDefinitions"); + }); + + modelBuilder.Entity("Elsa.Workflows.Management.Entities.WorkflowInstance", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DefinitionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DefinitionVersionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FinishedAt") + .HasColumnType("TEXT"); + + b.Property("IncidentCount") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SubStatus") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Version") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CorrelationId") + .HasDatabaseName("IX_WorkflowInstance_CorrelationId"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_WorkflowInstance_CreatedAt"); + + b.HasIndex("DefinitionId") + .HasDatabaseName("IX_WorkflowInstance_DefinitionId"); + + b.HasIndex("FinishedAt") + .HasDatabaseName("IX_WorkflowInstance_FinishedAt"); + + b.HasIndex("Name") + .HasDatabaseName("IX_WorkflowInstance_Name"); + + b.HasIndex("Status") + .HasDatabaseName("IX_WorkflowInstance_Status"); + + b.HasIndex("SubStatus") + .HasDatabaseName("IX_WorkflowInstance_SubStatus"); + + b.HasIndex("UpdatedAt") + .HasDatabaseName("IX_WorkflowInstance_UpdatedAt"); + + b.HasIndex("Status", "DefinitionId") + .HasDatabaseName("IX_WorkflowInstance_Status_DefinitionId"); + + b.HasIndex("Status", "SubStatus") + .HasDatabaseName("IX_WorkflowInstance_Status_SubStatus"); + + b.HasIndex("SubStatus", "DefinitionId") + .HasDatabaseName("IX_WorkflowInstance_SubStatus_DefinitionId"); + + b.HasIndex("Status", "SubStatus", "DefinitionId", "Version") + .HasDatabaseName("IX_WorkflowInstance_Status_SubStatus_DefinitionId_Version"); + + b.ToTable("WorkflowInstances"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240202100045_AddKeyValueStore.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240202100045_AddKeyValueStore.cs new file mode 100644 index 000000000..1c17505c2 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240202100045_AddKeyValueStore.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Runtime +{ + /// + public partial class AddKeyValueStore : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/Configurations.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/Configurations.cs index 0691c0678..edc79a0e9 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/Configurations.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/Configurations.cs @@ -12,7 +12,8 @@ public class Configurations : IEntityTypeConfiguration, IEntityTypeConfiguration, IEntityTypeConfiguration, - IEntityTypeConfiguration + IEntityTypeConfiguration, + IEntityTypeConfiguration { /// public void Configure(EntityTypeBuilder builder) @@ -109,4 +110,10 @@ public class Configurations : builder.HasIndex(x => x.CreatedAt, $"IX_{nameof(WorkflowInboxMessage)}_{nameof(WorkflowInboxMessage.CreatedAt)}"); builder.HasIndex(x => x.ExpiresAt, $"IX_{nameof(WorkflowInboxMessage)}_{nameof(WorkflowInboxMessage.ExpiresAt)}"); } + + /// + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Key); + } } \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/DbContext.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/DbContext.cs index 650137b36..3e229cae7 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/DbContext.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/DbContext.cs @@ -38,6 +38,11 @@ public class RuntimeElsaDbContext : ElsaDbContextBase /// The workflow inbox messages. /// public DbSet WorkflowInboxMessages { get; set; } = default!; + + /// + /// The generic key value pairs. + /// + public DbSet KeyValuePairs { get; set; } = default!; /// protected override void ApplyEntityConfigurations(ModelBuilder modelBuilder) @@ -48,6 +53,7 @@ public class RuntimeElsaDbContext : ElsaDbContextBase modelBuilder.ApplyConfiguration(config); modelBuilder.ApplyConfiguration(config); modelBuilder.ApplyConfiguration(config); + modelBuilder.ApplyConfiguration(config); } /// @@ -71,5 +77,7 @@ public class RuntimeElsaDbContext : ElsaDbContextBase modelBuilder.Entity().Property("SerializedInput").HasColumnType("NCLOB"); modelBuilder.Entity().Property("SerializedBookmarkPayload").HasColumnType("NCLOB"); + + modelBuilder.Entity().Property("Value").HasColumnType("NCLOB"); } } \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/KeyValueStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/KeyValueStore.cs new file mode 100644 index 000000000..ee692344b --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/KeyValueStore.cs @@ -0,0 +1,36 @@ +using Elsa.EntityFrameworkCore.Common; +using Elsa.Workflows.Runtime.Contracts; +using Elsa.Workflows.Runtime.Entities; + +namespace Elsa.EntityFrameworkCore.Modules.Runtime; + +/// +/// Entity Framework implementation of the +/// +public class EFCoreKeyValueStore : IKeyValueStore +{ + private readonly Store _store; + + public EFCoreKeyValueStore(Store store) + { + _store = store; + } + + /// + public Task SaveAsync(SerializedKeyValuePair keyValuePair, CancellationToken cancellationToken) + { + return _store.SaveAsync(keyValuePair, x => x.Key, cancellationToken); + } + + /// + public Task GetValue(string key, CancellationToken cancellationToken) + { + return _store.FindAsync(x => x.Key == key, cancellationToken); + } + + /// + public Task DeleteAsync(string key, CancellationToken cancellationToken) + { + return _store.DeleteWhereAsync(x => x.Key == key, cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs index 5c019e4f2..0543af3c4 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs @@ -28,6 +28,7 @@ public class EFCoreWorkflowRuntimePersistenceFeature : PersistenceFeatureBase sp.GetRequiredService(); feature.WorkflowExecutionLogStore = sp => sp.GetRequiredService(); feature.ActivityExecutionLogStore = sp => sp.GetRequiredService(); + feature.KeyValueStore = sp => sp.GetRequiredService(); }); } @@ -41,5 +42,6 @@ public class EFCoreWorkflowRuntimePersistenceFeature : PersistenceFeatureBase(); AddEntityStore(); AddEntityStore(); + AddStore(); } } \ No newline at end of file diff --git a/src/modules/Elsa.MongoDb/Modules/Runtime/CreateIndices.cs b/src/modules/Elsa.MongoDb/Modules/Runtime/CreateIndices.cs index e3752ee16..2c79b087e 100644 --- a/src/modules/Elsa.MongoDb/Modules/Runtime/CreateIndices.cs +++ b/src/modules/Elsa.MongoDb/Modules/Runtime/CreateIndices.cs @@ -1,3 +1,4 @@ +using Elsa.Common.Entities; using Elsa.MongoDb.Helpers; using Elsa.Workflows.Runtime.Entities; using Elsa.Workflows.State; @@ -23,7 +24,8 @@ internal class CreateIndices : IHostedService CreateActivityExecutionLogIndices(cancellationToken), CreateWorkflowBookmarkIndices(cancellationToken), CreateWorkflowTriggerIndices(cancellationToken), - CreateWorkflowInboxIndices(cancellationToken) + CreateWorkflowInboxIndices(cancellationToken), + CreateKeyValueIndices(cancellationToken) ); } @@ -181,4 +183,20 @@ internal class CreateIndices : IHostedService }, cancellationToken)); } + + private Task CreateKeyValueIndices(CancellationToken cancellationToken) + { + var keyValuePairCollection = _serviceProvider.GetService>(); + if (keyValuePairCollection == null) return Task.CompletedTask; + + return IndexHelpers.CreateAsync( + keyValuePairCollection, + async (collection, indexBuilder) => + await collection.Indexes.CreateManyAsync( + new List> + { + new(indexBuilder.Ascending(x => x.Key)) + }, + cancellationToken)); + } } \ No newline at end of file diff --git a/src/modules/Elsa.MongoDb/Modules/Runtime/KeyValueStore.cs b/src/modules/Elsa.MongoDb/Modules/Runtime/KeyValueStore.cs new file mode 100644 index 000000000..14b409606 --- /dev/null +++ b/src/modules/Elsa.MongoDb/Modules/Runtime/KeyValueStore.cs @@ -0,0 +1,39 @@ +using Elsa.MongoDb.Common; +using Elsa.Workflows.Runtime.Contracts; +using Elsa.Workflows.Runtime.Entities; + +namespace Elsa.MongoDb.Modules.Runtime; + +/// +/// A MongoDB based store for s. +/// +public class MongoKeyValueStore : IKeyValueStore +{ + private readonly MongoDbStore _keyValueMongoDbStore; + + /// + /// Initializes a new instance of the class. + /// + public MongoKeyValueStore(MongoDbStore keyValueMongoDbStore) + { + _keyValueMongoDbStore = keyValueMongoDbStore; + } + + /// + public Task SaveAsync(SerializedKeyValuePair keyValuePair, CancellationToken cancellationToken) + { + return _keyValueMongoDbStore.SaveAsync(keyValuePair, cancellationToken); + } + + /// + public Task GetValue(string key, CancellationToken cancellationToken) + { + return _keyValueMongoDbStore.FindAsync(x => x.Key == key, cancellationToken); + } + + /// + public Task DeleteAsync(string key, CancellationToken cancellationToken) + { + return _keyValueMongoDbStore.DeleteWhereAsync(x => x.Key == key, cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.MongoDb/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs b/src/modules/Elsa.MongoDb/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs index a7af08291..671b94393 100644 --- a/src/modules/Elsa.MongoDb/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs +++ b/src/modules/Elsa.MongoDb/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs @@ -1,3 +1,4 @@ +using Elsa.Common.Entities; using Elsa.Features.Attributes; using Elsa.Features.Services; using Elsa.MongoDb.Common; @@ -28,6 +29,7 @@ public class MongoWorkflowRuntimePersistenceFeature : PersistenceFeatureBase feature.WorkflowExecutionLogStore = sp => sp.GetRequiredService(); feature.ActivityExecutionLogStore = sp => sp.GetRequiredService(); feature.WorkflowInboxStore = sp => sp.GetRequiredService(); + feature.KeyValueStore = sp => sp.GetRequiredService(); }); } @@ -41,12 +43,14 @@ public class MongoWorkflowRuntimePersistenceFeature : PersistenceFeatureBase AddCollection("workflow_execution_logs"); AddCollection("activity_execution_logs"); AddCollection("workflow_inbox_messages"); + AddCollection("key_value_pairs"); AddStore(); AddStore(); AddStore(); AddStore(); AddStore(); + AddStore(); Services.AddHostedService(); } diff --git a/src/modules/Elsa.Workflows.Runtime/Contracts/IKeyValueStore.cs b/src/modules/Elsa.Workflows.Runtime/Contracts/IKeyValueStore.cs new file mode 100644 index 000000000..d3543d4a3 --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/Contracts/IKeyValueStore.cs @@ -0,0 +1,25 @@ +using Elsa.Workflows.Runtime.Entities; + +namespace Elsa.Workflows.Runtime.Contracts; + +/// +/// Store that holds key value entities not fit to store in specific stores. +/// +public interface IKeyValueStore +{ + /// + /// Saves the key value pair. + /// + Task SaveAsync(SerializedKeyValuePair keyValuePair, CancellationToken cancellationToken); + + /// + /// Retrieves the key value pair from the store. + /// + /// if the key is found, otherwise null. + Task GetValue(string key, CancellationToken cancellationToken); + + /// + /// If the key is found it deletes the record from the store. + /// + Task DeleteAsync(string key, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Entities/SerializedKeyValuePair.cs b/src/modules/Elsa.Workflows.Runtime/Entities/SerializedKeyValuePair.cs new file mode 100644 index 000000000..999100f23 --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/Entities/SerializedKeyValuePair.cs @@ -0,0 +1,7 @@ +namespace Elsa.Workflows.Runtime.Entities; + +public class SerializedKeyValuePair +{ + public string Key { get; set; } + public string SerializedValue { get; set; } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs index 0d7488fb4..89183206f 100644 --- a/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs +++ b/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs @@ -79,6 +79,11 @@ public class WorkflowRuntimeFeature : FeatureBase /// public Func WorkflowInboxStore { get; set; } = sp => sp.GetRequiredService(); + /// + /// A factory that instantiates an . + /// + public Func KeyValueStore { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + /// /// A factory that instantiates an . /// @@ -174,6 +179,7 @@ public class WorkflowRuntimeFeature : FeatureBase .AddScoped(WorkflowExecutionLogStore) .AddScoped(ActivityExecutionLogStore) .AddScoped(WorkflowInboxStore) + .AddScoped(KeyValueStore) .AddScoped(WorkflowExecutionContextStore) .AddSingleton(RunTaskDispatcher) .AddSingleton(BackgroundActivityScheduler) diff --git a/src/modules/Elsa.Workflows.Runtime/Stores/MemoryKeyValueStore.cs b/src/modules/Elsa.Workflows.Runtime/Stores/MemoryKeyValueStore.cs new file mode 100644 index 000000000..0d62b183c --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/Stores/MemoryKeyValueStore.cs @@ -0,0 +1,42 @@ +using Elsa.Common.Services; +using Elsa.Workflows.Runtime.Contracts; +using Elsa.Workflows.Runtime.Entities; + +namespace Elsa.Workflows.Runtime.Stores; + +/// +/// Stores key value records in memory. +/// +public class MemoryKeyValueStore : IKeyValueStore +{ + private readonly MemoryStore _store; + + /// + /// Initializes a new instance of the class. + /// + public MemoryKeyValueStore(MemoryStore store) + { + _store = store; + } + + /// + public Task SaveAsync(SerializedKeyValuePair keyValuePair, CancellationToken cancellationToken) + { + _store.Save(keyValuePair, kv => kv.Key); + return Task.CompletedTask; + } + + /// + public Task GetValue(string key, CancellationToken cancellationToken) + { + var result = _store.Find(x => x.Key == key); + return Task.FromResult(result); + } + + /// + public Task DeleteAsync(string key, CancellationToken cancellationToken) + { + _store.DeleteWhere(x => x.Key == key); + return Task.CompletedTask; + } +} \ No newline at end of file