diff --git a/src/bundles/Elsa.Server.Web/Program.cs b/src/bundles/Elsa.Server.Web/Program.cs
index ec8a8263b..cf1b19952 100644
--- a/src/bundles/Elsa.Server.Web/Program.cs
+++ b/src/bundles/Elsa.Server.Web/Program.cs
@@ -54,7 +54,13 @@ services
if (useDapper)
elsa.UseDapper(dapper =>
{
- dapper.UseMigrations();
+ dapper.UseMigrations(feature =>
+ {
+ if (useSqlServer)
+ feature.UseSqlServer();
+ else
+ feature.UseSqlite();
+ });
dapper.DbConnectionProvider = sp =>
{
if (useSqlServer)
@@ -108,8 +114,8 @@ services
else
ef.UseSqlite(sqliteConnectionString);
});
-
- if(useZipCompression)
+
+ if (useZipCompression)
management.SetCompressionAlgorithm(nameof(Zstd));
})
.UseWorkflowRuntime(runtime =>
@@ -138,10 +144,11 @@ services
});
}
- if(useMassTransit)
+ if (useMassTransit)
{
runtime.UseMassTransitDispatcher();
}
+
runtime.WorkflowInboxCleanupOptions = options => configuration.GetSection("Runtime:WorkflowInboxCleanup").Bind(options);
runtime.WorkflowDispatcherOptions = options => configuration.GetSection("Runtime:WorkflowDispatcher").Bind(options);
})
@@ -187,19 +194,29 @@ services
.UseEmail(email => email.ConfigureOptions = options => configuration.GetSection("Smtp").Bind(options))
.UseAlterations(alterations =>
{
- alterations.UseEntityFrameworkCore(ef =>
+ if (useMongoDb)
{
- if (useSqlServer)
- ef.UseSqlServer(sqlServerConnectionString);
- else
- ef.UseSqlite(sqliteConnectionString);
- });
+ // TODO: alterations.UseMongoDb();
+ }
+ else if (useDapper)
+ {
+ // TODO: alterations.UseDapper();
+ }
+ else
+ {
+ alterations.UseEntityFrameworkCore(ef =>
+ {
+ if (useSqlServer)
+ ef.UseSqlServer(sqlServerConnectionString);
+ else
+ ef.UseSqlite(sqliteConnectionString);
+ });
+ }
if (useMassTransit)
{
alterations.UseMassTransitDispatcher();
}
-
})
.UseWorkflowContexts();
diff --git a/src/modules/Elsa.Dapper.Migrations/Identity/Initial.cs b/src/modules/Elsa.Dapper.Migrations/Identity/Initial.cs
index cabb59edf..a01fd1cc4 100644
--- a/src/modules/Elsa.Dapper.Migrations/Identity/Initial.cs
+++ b/src/modules/Elsa.Dapper.Migrations/Identity/Initial.cs
@@ -1,5 +1,6 @@
using FluentMigrator;
using JetBrains.Annotations;
+using static System.Int32;
namespace Elsa.Dapper.Migrations.Identity;
@@ -17,13 +18,13 @@ public class Initial : Migration
.WithColumn("Name").AsString().NotNullable()
.WithColumn("HashedPassword").AsString().NotNullable()
.WithColumn("HashedPasswordSalt").AsString().NotNullable()
- .WithColumn("Roles").AsString().NotNullable();
+ .WithColumn("Roles").AsString(MaxValue).NotNullable();
Create
.Table("Roles")
.WithColumn("Id").AsString().PrimaryKey()
.WithColumn("Name").AsString().NotNullable()
- .WithColumn("Permissions").AsString().NotNullable();
+ .WithColumn("Permissions").AsString(MaxValue).NotNullable();
Create
.Table("Applications")
@@ -34,7 +35,7 @@ public class Initial : Migration
.WithColumn("Name").AsString().NotNullable()
.WithColumn("HashedApiKey").AsString().NotNullable()
.WithColumn("HashedApiKeySalt").AsString().NotNullable()
- .WithColumn("Roles").AsString().NotNullable();
+ .WithColumn("Roles").AsString(MaxValue).NotNullable();
}
///
diff --git a/src/modules/Elsa.Dapper.Migrations/Management/Initial.cs b/src/modules/Elsa.Dapper.Migrations/Management/Initial.cs
index 1e7a94e64..b7aa69db9 100644
--- a/src/modules/Elsa.Dapper.Migrations/Management/Initial.cs
+++ b/src/modules/Elsa.Dapper.Migrations/Management/Initial.cs
@@ -1,5 +1,6 @@
using FluentMigrator;
using JetBrains.Annotations;
+using static System.Int32;
namespace Elsa.Dapper.Migrations.Management;
@@ -18,14 +19,14 @@ public class Initial : Migration
.WithColumn("DefinitionId").AsString().NotNullable()
.WithColumn("Name").AsString().Nullable()
.WithColumn("ToolVersion").AsString().Nullable()
- .WithColumn("Description").AsString().Nullable()
+ .WithColumn("Description").AsString(MaxValue).Nullable()
.WithColumn("ProviderName").AsString().Nullable()
.WithColumn("MaterializerName").AsString().NotNullable()
- .WithColumn("MaterializerContext").AsString().Nullable()
- .WithColumn("Props").AsString().NotNullable()
+ .WithColumn("MaterializerContext").AsString(MaxValue).Nullable()
+ .WithColumn("Props").AsString(MaxValue).NotNullable()
.WithColumn("UsableAsActivity").AsBoolean().Nullable()
- .WithColumn("StringData").AsString().Nullable()
- .WithColumn("BinaryData").AsBinary().Nullable()
+ .WithColumn("StringData").AsString(MaxValue).Nullable()
+ .WithColumn("BinaryData").AsBinary(MaxValue).Nullable()
.WithColumn("CreatedAt").AsDateTimeOffset().NotNullable()
.WithColumn("Version").AsInt32().NotNullable()
.WithColumn("IsLatest").AsBoolean().NotNullable()
@@ -40,14 +41,14 @@ public class Initial : Migration
.WithColumn("DefinitionId").AsString().NotNullable()
.WithColumn("Name").AsString().Nullable()
.WithColumn("ToolVersion").AsString().Nullable()
- .WithColumn("Description").AsString().Nullable()
+ .WithColumn("Description").AsString(MaxValue).Nullable()
.WithColumn("ProviderName").AsString().Nullable()
.WithColumn("MaterializerName").AsString().NotNullable()
.WithColumn("MaterializerContext").AsString().Nullable()
- .WithColumn("Props").AsString().NotNullable()
+ .WithColumn("Props").AsString(MaxValue).NotNullable()
.WithColumn("UsableAsActivity").AsBoolean().Nullable()
- .WithColumn("StringData").AsString().Nullable()
- .WithColumn("BinaryData").AsBinary().Nullable()
+ .WithColumn("StringData").AsString(MaxValue).Nullable()
+ .WithColumn("BinaryData").AsBinary(MaxValue).Nullable()
.WithColumn("CreatedAt").AsDateTime2().NotNullable()
.WithColumn("Version").AsInt32().NotNullable()
.WithColumn("IsLatest").AsBoolean().NotNullable()
@@ -61,7 +62,7 @@ public class Initial : Migration
.WithColumn("DefinitionId").AsString().NotNullable()
.WithColumn("DefinitionVersionId").AsString().NotNullable()
.WithColumn("Version").AsInt32().NotNullable()
- .WithColumn("WorkflowState").AsString().NotNullable()
+ .WithColumn("WorkflowState").AsString(MaxValue).NotNullable()
.WithColumn("Status").AsString().NotNullable()
.WithColumn("SubStatus").AsString().NotNullable()
.WithColumn("CorrelationId").AsString().Nullable()
@@ -78,7 +79,7 @@ public class Initial : Migration
.WithColumn("DefinitionId").AsString().NotNullable()
.WithColumn("DefinitionVersionId").AsString().NotNullable()
.WithColumn("Version").AsInt32().NotNullable()
- .WithColumn("WorkflowState").AsString().NotNullable()
+ .WithColumn("WorkflowState").AsString(MaxValue).NotNullable()
.WithColumn("Status").AsString().NotNullable()
.WithColumn("SubStatus").AsString().NotNullable()
.WithColumn("CorrelationId").AsString().Nullable()
diff --git a/src/modules/Elsa.Dapper.Migrations/Runtime/Initial.cs b/src/modules/Elsa.Dapper.Migrations/Runtime/Initial.cs
index 04fe01593..221838fbd 100644
--- a/src/modules/Elsa.Dapper.Migrations/Runtime/Initial.cs
+++ b/src/modules/Elsa.Dapper.Migrations/Runtime/Initial.cs
@@ -1,5 +1,6 @@
using FluentMigrator;
using JetBrains.Annotations;
+using static System.Int32;
namespace Elsa.Dapper.Migrations.Runtime;
@@ -8,6 +9,7 @@ namespace Elsa.Dapper.Migrations.Runtime;
[PublicAPI]
public class Initial : Migration
{
+ private const int MaxNodeIdColumnLength = 2048;
///
public override void Up()
{
@@ -19,7 +21,7 @@ public class Initial : Migration
.WithColumn("Name").AsString().NotNullable().Indexed()
.WithColumn("ActivityId").AsString().NotNullable().Indexed()
.WithColumn("Hash").AsString().Nullable().Indexed()
- .WithColumn("SerializedPayload").AsString().Nullable();
+ .WithColumn("SerializedPayload").AsString(MaxValue).Nullable();
IfDatabase("SqlServer", "Oracle", "MySql", "Postgres")
.Create
@@ -30,8 +32,8 @@ public class Initial : Migration
.WithColumn("CorrelationId").AsString().Nullable().Indexed()
.WithColumn("ActivityInstanceId").AsString().NotNullable().Indexed()
.WithColumn("Hash").AsString().Nullable().Indexed()
- .WithColumn("SerializedPayload").AsString().Nullable()
- .WithColumn("SerializedMetadata").AsString().Nullable()
+ .WithColumn("SerializedPayload").AsString(MaxValue).Nullable()
+ .WithColumn("SerializedMetadata").AsString(MaxValue).Nullable()
.WithColumn("CreatedAt").AsDateTimeOffset().NotNullable().Indexed();
IfDatabase("Sqlite")
@@ -43,8 +45,8 @@ public class Initial : Migration
.WithColumn("CorrelationId").AsString().Nullable().Indexed()
.WithColumn("ActivityInstanceId").AsString().NotNullable().Indexed()
.WithColumn("Hash").AsString().Nullable().Indexed()
- .WithColumn("SerializedPayload").AsString().Nullable()
- .WithColumn("SerializedMetadata").AsString().Nullable()
+ .WithColumn("SerializedPayload").AsString(MaxValue).Nullable()
+ .WithColumn("SerializedMetadata").AsString(MaxValue).Nullable()
.WithColumn("CreatedAt").AsDateTime2().NotNullable().Indexed();
IfDatabase("SqlServer", "Oracle", "MySql", "Postgres")
@@ -61,13 +63,13 @@ public class Initial : Migration
.WithColumn("ActivityType").AsString().NotNullable().Indexed()
.WithColumn("ActivityTypeVersion").AsInt32().NotNullable().Indexed()
.WithColumn("ActivityName").AsString().Nullable().Indexed()
- .WithColumn("ActivityNodeId").AsString().NotNullable().Indexed()
+ .WithColumn("ActivityNodeId").AsString(MaxNodeIdColumnLength).NotNullable().Indexed()
.WithColumn("EventName").AsString().Nullable().Indexed()
- .WithColumn("Message").AsString().Nullable()
+ .WithColumn("Message").AsString(MaxValue).Nullable()
.WithColumn("Source").AsString().Nullable()
- .WithColumn("SerializedActivityState").AsString().Nullable()
- .WithColumn("SerializedPayload").AsString().Nullable()
- .WithColumn("SerializedException").AsString().Nullable()
+ .WithColumn("SerializedActivityState").AsString(MaxValue).Nullable()
+ .WithColumn("SerializedPayload").AsString(MaxValue).Nullable()
+ .WithColumn("SerializedException").AsString(MaxValue).Nullable()
.WithColumn("Timestamp").AsDateTimeOffset().NotNullable().Indexed()
.WithColumn("Sequence").AsInt64().NotNullable().Indexed()
;
@@ -86,14 +88,14 @@ public class Initial : Migration
.WithColumn("ActivityType").AsString().NotNullable().Indexed()
.WithColumn("ActivityTypeVersion").AsInt32().NotNullable().Indexed()
.WithColumn("ActivityName").AsString().Nullable().Indexed()
- .WithColumn("ActivityNodeId").AsString().NotNullable().Indexed()
+ .WithColumn("ActivityNodeId").AsString(MaxNodeIdColumnLength).NotNullable().Indexed()
.WithColumn("EventName").AsString().Nullable().Indexed()
- .WithColumn("Message").AsString().Nullable()
+ .WithColumn("Message").AsString(MaxValue).Nullable()
.WithColumn("Source").AsString().Nullable()
- .WithColumn("SerializedActivityState").AsString().Nullable()
- .WithColumn("SerializedPayload").AsString().Nullable()
- .WithColumn("SerializedOutputs").AsString().Nullable()
- .WithColumn("SerializedException").AsString().Nullable()
+ .WithColumn("SerializedActivityState").AsString(MaxValue).Nullable()
+ .WithColumn("SerializedPayload").AsString(MaxValue).Nullable()
+ .WithColumn("SerializedOutputs").AsString(MaxValue).Nullable()
+ .WithColumn("SerializedException").AsString(MaxValue).Nullable()
.WithColumn("Timestamp").AsDateTime2().NotNullable().Indexed()
.WithColumn("Sequence").AsInt64().NotNullable().Indexed()
;
@@ -104,14 +106,14 @@ public class Initial : Migration
.WithColumn("Id").AsString().PrimaryKey()
.WithColumn("WorkflowInstanceId").AsString().NotNullable().Indexed()
.WithColumn("ActivityId").AsString().NotNullable().Indexed()
- .WithColumn("ActivityNodeId").AsString().NotNullable().Indexed()
+ .WithColumn("ActivityNodeId").AsString(MaxNodeIdColumnLength).NotNullable().Indexed()
.WithColumn("ActivityType").AsString().NotNullable().Indexed()
.WithColumn("ActivityTypeVersion").AsInt32().NotNullable().Indexed()
.WithColumn("ActivityName").AsString().Nullable().Indexed()
- .WithColumn("SerializedActivityState").AsString().Nullable()
- .WithColumn("SerializedPayload").AsString().Nullable()
- .WithColumn("SerializedOutputs").AsString().Nullable()
- .WithColumn("SerializedException").AsString().Nullable()
+ .WithColumn("SerializedActivityState").AsString(MaxValue).Nullable()
+ .WithColumn("SerializedPayload").AsString(MaxValue).Nullable()
+ .WithColumn("SerializedOutputs").AsString(MaxValue).Nullable()
+ .WithColumn("SerializedException").AsString(MaxValue).Nullable()
.WithColumn("StartedAt").AsDateTimeOffset().NotNullable().Indexed()
.WithColumn("CompletedAt").AsDateTimeOffset().Nullable().Indexed()
.WithColumn("HasBookmarks").AsBoolean().NotNullable().Indexed()
@@ -124,14 +126,14 @@ public class Initial : Migration
.WithColumn("Id").AsString().PrimaryKey()
.WithColumn("WorkflowInstanceId").AsString().NotNullable().Indexed()
.WithColumn("ActivityId").AsString().NotNullable().Indexed()
- .WithColumn("ActivityNodeId").AsString().NotNullable().Indexed()
+ .WithColumn("ActivityNodeId").AsString(MaxNodeIdColumnLength).NotNullable().Indexed()
.WithColumn("ActivityType").AsString().NotNullable().Indexed()
.WithColumn("ActivityTypeVersion").AsInt32().NotNullable().Indexed()
.WithColumn("ActivityName").AsString().Nullable().Indexed()
- .WithColumn("SerializedActivityState").AsString().Nullable()
- .WithColumn("SerializedPayload").AsString().Nullable()
- .WithColumn("SerializedOutputs").AsString().Nullable()
- .WithColumn("SerializedException").AsString().Nullable()
+ .WithColumn("SerializedActivityState").AsString(MaxValue).Nullable()
+ .WithColumn("SerializedPayload").AsString(MaxValue).Nullable()
+ .WithColumn("SerializedOutputs").AsString(MaxValue).Nullable()
+ .WithColumn("SerializedException").AsString(MaxValue).Nullable()
.WithColumn("StartedAt").AsDateTime2().NotNullable().Indexed()
.WithColumn("CompletedAt").AsDateTime2().Nullable().Indexed()
.WithColumn("HasBookmarks").AsBoolean().NotNullable().Indexed()
@@ -147,8 +149,8 @@ public class Initial : Migration
.WithColumn("ActivityInstanceId").AsString().Nullable().Indexed()
.WithColumn("CorrelationId").AsString().Nullable().Indexed()
.WithColumn("Hash").AsString().NotNullable().Indexed()
- .WithColumn("SerializedBookmarkPayload").AsString()
- .WithColumn("SerializedInput").AsString().Nullable()
+ .WithColumn("SerializedBookmarkPayload").AsString(MaxValue)
+ .WithColumn("SerializedInput").AsString(MaxValue).Nullable()
.WithColumn("CreatedAt").AsDateTimeOffset().Indexed()
.WithColumn("ExpiresAt").AsDateTimeOffset().Indexed()
;
@@ -162,8 +164,8 @@ public class Initial : Migration
.WithColumn("ActivityInstanceId").AsString().Nullable().Indexed()
.WithColumn("CorrelationId").AsString().Nullable().Indexed()
.WithColumn("Hash").AsString().NotNullable().Indexed()
- .WithColumn("SerializedBookmarkPayload").AsString()
- .WithColumn("SerializedInput").AsString().Nullable()
+ .WithColumn("SerializedBookmarkPayload").AsString(MaxValue)
+ .WithColumn("SerializedInput").AsString(MaxValue).Nullable()
.WithColumn("CreatedAt").AsDateTime2().Indexed()
.WithColumn("ExpiresAt").AsDateTime2().Indexed()
;
diff --git a/src/modules/Elsa.Dapper.Migrations/Runtime/V3_1.cs b/src/modules/Elsa.Dapper.Migrations/Runtime/V3_1.cs
index 3cefe7ac2..e453a8b41 100644
--- a/src/modules/Elsa.Dapper.Migrations/Runtime/V3_1.cs
+++ b/src/modules/Elsa.Dapper.Migrations/Runtime/V3_1.cs
@@ -1,11 +1,14 @@
+using System.Diagnostics.CodeAnalysis;
using FluentMigrator;
using JetBrains.Annotations;
+using static System.Int32;
namespace Elsa.Dapper.Migrations.Runtime;
///
[Migration(20002, "Elsa:Runtime:AddKeyValueStore")]
[PublicAPI]
+[SuppressMessage("ReSharper", "InconsistentNaming")]
public class V3_1 : Migration
{
///
@@ -14,7 +17,7 @@ public class V3_1 : Migration
Create
.Table("KeyValuePairs")
.WithColumn("Key").AsString().PrimaryKey()
- .WithColumn("Value").AsString().NotNullable();
+ .WithColumn("Value").AsString(MaxValue).NotNullable();
}
///
diff --git a/src/modules/Elsa.Dapper/Abstractions/SqlDialectBase.cs b/src/modules/Elsa.Dapper/Abstractions/SqlDialectBase.cs
index f95831fa6..e14b57c5f 100644
--- a/src/modules/Elsa.Dapper/Abstractions/SqlDialectBase.cs
+++ b/src/modules/Elsa.Dapper/Abstractions/SqlDialectBase.cs
@@ -1,4 +1,6 @@
+using System.Text;
using Elsa.Common.Entities;
+using Elsa.Common.Models;
using Elsa.Dapper.Contracts;
namespace Elsa.Dapper.Abstractions;
@@ -53,10 +55,25 @@ public abstract class SqlDialectBase : ISqlDialect
}
///
- public virtual string Skip(int count) => $"offset {count}";
+ public virtual string Skip(int count) => $"offset {count} rows";
///
- public virtual string Take(int count) => $"limit {count}";
+ public virtual string Take(int count) => $"fetch next {count} rows only";
+
+ ///
+ public virtual string Page(PageArgs pageArgs)
+ {
+ var sb = new StringBuilder();
+
+ // Attention: the order is important here for SQL Server (OFFSET before FETCH NEXT).
+ if (pageArgs.Offset != null)
+ sb.AppendLine(Skip(pageArgs.Offset.Value));
+
+ if (pageArgs.Limit != null)
+ sb.AppendLine(Take(pageArgs.Limit.Value));
+
+ return sb.ToString();
+ }
///
public string Insert(string table, string[] fields, Func? getParamName = default)
@@ -75,6 +92,15 @@ public abstract class SqlDialectBase : ISqlDialect
var fieldList = string.Join(", ", fields);
var fieldParamNames = fields.Select(x => $"@{getParamName(x)}");
var fieldParamList = string.Join(", ", fieldParamNames);
- return $"INSERT OR REPLACE INTO {table} ({primaryKeyField}, {fieldList}) VALUES (@{getParamName(primaryKeyField)}, {fieldParamList});";
+ return @$"
+ MERGE INTO {table} WITH (HOLDLOCK) AS Target
+ USING (VALUES (@{getParamName(primaryKeyField)}, {fieldParamList}))
+ AS Source ({primaryKeyField}, {fieldList})
+ ON Target.{primaryKeyField} = Source.{primaryKeyField}
+ WHEN MATCHED THEN
+ UPDATE SET {string.Join(", ", fields.Select(x => $"{x} = Source.{x}"))}
+ WHEN NOT MATCHED THEN
+ INSERT ({primaryKeyField}, {fieldList})
+ VALUES (Source.{primaryKeyField}, {string.Join(", ", fields.Select(x => $"Source.{x}"))});";
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Contracts/ISqlDialect.cs b/src/modules/Elsa.Dapper/Contracts/ISqlDialect.cs
index 505451383..c83f4fc53 100644
--- a/src/modules/Elsa.Dapper/Contracts/ISqlDialect.cs
+++ b/src/modules/Elsa.Dapper/Contracts/ISqlDialect.cs
@@ -1,4 +1,5 @@
using Elsa.Common.Entities;
+using Elsa.Common.Models;
namespace Elsa.Dapper.Contracts;
@@ -96,6 +97,13 @@ public interface ISqlDialect
/// The number of records to take.
string Take(int count);
+ ///
+ /// Adds pagination to a SQL query.
+ ///
+ /// The pagination arguments.
+ /// The modified SQL query with pagination.
+ string Page(PageArgs pageArgs);
+
///
/// Builds an INSERT query.
///
diff --git a/src/modules/Elsa.Dapper/Dialects/SqlServerDialect.cs b/src/modules/Elsa.Dapper/Dialects/SqlServerDialect.cs
index 4c4760a9f..665de65c5 100644
--- a/src/modules/Elsa.Dapper/Dialects/SqlServerDialect.cs
+++ b/src/modules/Elsa.Dapper/Dialects/SqlServerDialect.cs
@@ -8,8 +8,8 @@ namespace Elsa.Dapper.Dialects;
public class SqlServerDialect : SqlDialectBase
{
///
- public override string Skip(int count) => $"OFFSET {count} ROWS";
+ public override string Skip(int count) => $"Offset {count} Rows";
///
- public override string Take(int count) => $"FETCH NEXT {count} ROWS ONLY";
+ public override string Take(int count) => $"fetch next {count} rows only";
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Dialects/SqliteDialect.cs b/src/modules/Elsa.Dapper/Dialects/SqliteDialect.cs
index d4d1caf0a..f852ec22b 100644
--- a/src/modules/Elsa.Dapper/Dialects/SqliteDialect.cs
+++ b/src/modules/Elsa.Dapper/Dialects/SqliteDialect.cs
@@ -1,3 +1,5 @@
+using System.Text;
+using Elsa.Common.Models;
using Elsa.Dapper.Abstractions;
namespace Elsa.Dapper.Dialects;
@@ -7,4 +9,33 @@ namespace Elsa.Dapper.Dialects;
///
public class SqliteDialect : SqlDialectBase
{
+ ///
+ public override string Skip(int count) => $"offset {count}";
+
+ ///
+ public override string Take(int count) => $"limit {count}";
+
+ ///
+ public override string Page(PageArgs pageArgs)
+ {
+ var sb = new StringBuilder();
+
+ // Attention: the order is important here for SQLite (LIMIT must come before OFFSET).
+ if (pageArgs.Limit != null)
+ sb.AppendLine(Take(pageArgs.Limit.Value));
+
+ if (pageArgs.Offset != null)
+ sb.AppendLine(Skip(pageArgs.Offset.Value));
+
+ return sb.ToString();
+ }
+
+ public override string Upsert(string table, string primaryKeyField, string[] fields, Func? getParamName = default)
+ {
+ getParamName ??= x => x;
+ var fieldList = string.Join(", ", fields);
+ var fieldParamNames = fields.Select(x => $"@{getParamName(x)}");
+ var fieldParamList = string.Join(", ", fieldParamNames);
+ return $"INSERT OR REPLACE INTO {table} ({primaryKeyField}, {fieldList}) VALUES (@{getParamName(primaryKeyField)}, {fieldParamList});";
+ }
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs b/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs
index ed20ce54a..e6dec0a26 100644
--- a/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs
+++ b/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs
@@ -1,3 +1,4 @@
+using System.Data;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using Elsa.Common.Entities;
@@ -75,11 +76,12 @@ public static class ParameterizedQueryBuilderExtensions
///
/// The query.
/// The table.
+ /// The primary key.
/// The inner query.
- public static ParameterizedQuery Delete(this ParameterizedQuery query, string table, ParameterizedQuery innerQuery)
+ public static ParameterizedQuery Delete(this ParameterizedQuery query, string table, string primaryKey, ParameterizedQuery innerQuery)
{
query.Sql.AppendLine(query.Dialect.Delete(table));
- query.Sql.AppendLine("and rowid in (");
+ query.Sql.AppendLine($"and {primaryKey} in (");
query.Sql.AppendLine(innerQuery.Sql.ToString());
query.Sql.AppendLine(")");
return query;
@@ -324,13 +326,7 @@ public static class ParameterizedQueryBuilderExtensions
/// The page arguments.
public static ParameterizedQuery Page(this ParameterizedQuery query, PageArgs pageArgs)
{
- // Attention: the order is important here for SQLite (LIMIT must come before OFFSET).
- if (pageArgs.Limit != null)
- query.Take(pageArgs.Limit.Value);
-
- if (pageArgs.Offset != null)
- query.Skip(pageArgs.Offset.Value);
-
+ query.Sql.AppendLine(query.Dialect.Page(pageArgs));
return query;
}
@@ -350,16 +346,20 @@ public static class ParameterizedQueryBuilderExtensions
.ToArray();
getParameterName ??= x => x;
-
+
query.Sql.AppendLine(query.Dialect.Upsert(table, primaryKeyField, fields, getParameterName));
-
+
var primaryKeyValue = record.GetType().GetProperty(primaryKeyField)?.GetValue(record);
query.Parameters.Add($"@{getParameterName(primaryKeyField)}", primaryKeyValue);
-
+
+ var recordType = record.GetType();
foreach (var field in fields)
{
- var value = record.GetType().GetProperty(field)?.GetValue(record);
- query.Parameters.Add($"@{getParameterName(field)}", value);
+ var prop = recordType.GetProperty(field)!;
+ var propType = prop.PropertyType;
+ var value = prop.GetValue(record);
+ var dbType = value == null ? GetDbType(propType) : default;
+ query.Parameters.Add($"@{getParameterName(field)}", value, dbType);
}
return query;
@@ -389,4 +389,10 @@ public static class ParameterizedQueryBuilderExtensions
return query;
}
+
+ private static DbType? GetDbType(Type type)
+ {
+ if (type == typeof(byte[])) return DbType.Binary;
+ return null;
+ }
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Dapper/Features/DapperFeature.cs b/src/modules/Elsa.Dapper/Features/DapperFeature.cs
index 90194cdd5..a9bb0c4fa 100644
--- a/src/modules/Elsa.Dapper/Features/DapperFeature.cs
+++ b/src/modules/Elsa.Dapper/Features/DapperFeature.cs
@@ -17,9 +17,7 @@ public class DapperFeature : FeatureBase
public DapperFeature(IModule module) : base(module)
{
// See: https://learn.microsoft.com/en-us/dotnet/standard/data/sqlite/dapper-limitations#data-types
- SqlMapper.AddTypeHandler(new DateTimeOffsetHandler());
SqlMapper.AddTypeHandler(new GuidHandler());
- SqlMapper.AddTypeHandler(new TimeSpanHandler());
}
///
diff --git a/src/modules/Elsa.Dapper/Features/DapperMigrationsFeature.cs b/src/modules/Elsa.Dapper/Features/DapperMigrationsFeature.cs
index 45856bc84..9cee0c44f 100644
--- a/src/modules/Elsa.Dapper/Features/DapperMigrationsFeature.cs
+++ b/src/modules/Elsa.Dapper/Features/DapperMigrationsFeature.cs
@@ -18,11 +18,28 @@ public class DapperMigrationsFeature : FeatureBase
{
}
+ ///
+ /// Configures migrations to use SQLite.
+ ///
+ public DapperMigrationsFeature UseSqlite()
+ {
+ ConfigureRunner += runner => runner.AddSQLite();
+ return this;
+ }
+
+ ///
+ /// Configures migrations to use SQLite.
+ ///
+ public DapperMigrationsFeature UseSqlServer()
+ {
+ ConfigureRunner += runner => runner.AddSqlServer();
+ return this;
+ }
+
///
/// Gets or sets a delegate to configure migrations.
///
public Action ConfigureRunner { get; set; } = runner => runner
- .AddSQLite()
.WithGlobalConnectionString(sp => sp.GetRequiredService().GetConnectionString())
.WithMigrationsIn(typeof(Initial).Assembly);
diff --git a/src/modules/Elsa.Dapper/Services/SqliteDbConnectionProvider.cs b/src/modules/Elsa.Dapper/Services/SqliteDbConnectionProvider.cs
index 61dfb5fce..26e4aa73b 100644
--- a/src/modules/Elsa.Dapper/Services/SqliteDbConnectionProvider.cs
+++ b/src/modules/Elsa.Dapper/Services/SqliteDbConnectionProvider.cs
@@ -1,6 +1,8 @@
using System.Data;
+using Dapper;
using Elsa.Dapper.Contracts;
using Elsa.Dapper.Dialects;
+using Elsa.Dapper.TypeHandlers.Sqlite;
using JetBrains.Annotations;
using Microsoft.Data.Sqlite;
@@ -12,7 +14,14 @@ namespace Elsa.Dapper.Services;
[PublicAPI]
public class SqliteDbConnectionProvider : IDbConnectionProvider
{
- private readonly string _connectionString = "Data Source=elsa.dapper.db";
+ private readonly string _connectionString = "Data Source=:memory:;Cache=Shared";
+
+ static SqliteDbConnectionProvider()
+ {
+ // See: https://learn.microsoft.com/en-us/dotnet/standard/data/sqlite/dapper-limitations#data-types
+ SqlMapper.AddTypeHandler(new DateTimeOffsetHandler());
+ SqlMapper.AddTypeHandler(new TimeSpanHandler());
+ }
///
/// Initializes a new instance of the class.
@@ -29,9 +38,9 @@ public class SqliteDbConnectionProvider : IDbConnectionProvider
{
_connectionString = connectionString;
}
-
+
///
- public string GetConnectionString() =>_connectionString;
+ public string GetConnectionString() => _connectionString;
///
public IDbConnection GetConnection()
diff --git a/src/modules/Elsa.Dapper/Services/Store.cs b/src/modules/Elsa.Dapper/Services/Store.cs
index eb6fde73c..0526cae05 100644
--- a/src/modules/Elsa.Dapper/Services/Store.cs
+++ b/src/modules/Elsa.Dapper/Services/Store.cs
@@ -264,7 +264,7 @@ public class Store where T : notnull
filter(selectQuery);
selectQuery = selectQuery.OrderBy(orderFields.ToArray()).Page(pageArgs);
- var deleteQuery = _dbConnectionProvider.CreateQuery().Delete(TableName, selectQuery);
+ var deleteQuery = _dbConnectionProvider.CreateQuery().Delete(TableName, primaryKey, selectQuery);
using var connection = _dbConnectionProvider.GetConnection();
return await deleteQuery.ExecuteAsync(connection);
}