Fix Dapper Provider (#4998)
* Update Dapper for better support and pagination The update includes a significant enhancement to the Dapper module. Improvements were made to support SQL Server and enhanced SQL query pagination. Additionally, table column size was increased for various fields to handle more extensive data. Minor refactoring was also done to improve code readability. * Toggle SQL Server usage in Elsa Server The previous setting had SQL Server usage in Elsa server enabled by default. This commit changes the useSqlServer constant in Elsa.Server.Web to false, effectively switching SQL Server off unless manually activated.
This commit is contained in:
parent
e48c5e2075
commit
330dc1a0d7
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
/// <inheritdoc />
|
||||
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()
|
||||
;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using FluentMigrator;
|
||||
using JetBrains.Annotations;
|
||||
using static System.Int32;
|
||||
|
||||
namespace Elsa.Dapper.Migrations.Runtime;
|
||||
|
||||
/// <inheritdoc />
|
||||
[Migration(20002, "Elsa:Runtime:AddKeyValueStore")]
|
||||
[PublicAPI]
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
public class V3_1 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -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
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string Skip(int count) => $"offset {count}";
|
||||
public virtual string Skip(int count) => $"offset {count} rows";
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string Take(int count) => $"limit {count}";
|
||||
public virtual string Take(int count) => $"fetch next {count} rows only";
|
||||
|
||||
/// <inheritdoc />
|
||||
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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Insert(string table, string[] fields, Func<string, string>? 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}"))});";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Elsa.Common.Entities;
|
||||
using Elsa.Common.Models;
|
||||
|
||||
namespace Elsa.Dapper.Contracts;
|
||||
|
||||
|
|
@ -96,6 +97,13 @@ public interface ISqlDialect
|
|||
/// <param name="count">The number of records to take.</param>
|
||||
string Take(int count);
|
||||
|
||||
/// <summary>
|
||||
/// Adds pagination to a SQL query.
|
||||
/// </summary>
|
||||
/// <param name="pageArgs">The pagination arguments.</param>
|
||||
/// <returns>The modified SQL query with pagination.</returns>
|
||||
string Page(PageArgs pageArgs);
|
||||
|
||||
/// <summary>
|
||||
/// Builds an INSERT query.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ namespace Elsa.Dapper.Dialects;
|
|||
public class SqlServerDialect : SqlDialectBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Skip(int count) => $"OFFSET {count} ROWS";
|
||||
public override string Skip(int count) => $"Offset {count} Rows";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Take(int count) => $"FETCH NEXT {count} ROWS ONLY";
|
||||
public override string Take(int count) => $"fetch next {count} rows only";
|
||||
}
|
||||
|
|
@ -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;
|
|||
/// </summary>
|
||||
public class SqliteDialect : SqlDialectBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Skip(int count) => $"offset {count}";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Take(int count) => $"limit {count}";
|
||||
|
||||
/// <inheritdoc />
|
||||
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<string, string>? 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});";
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <param name="table">The table.</param>
|
||||
/// <param name="primaryKey">The primary key.</param>
|
||||
/// <param name="innerQuery">The inner query.</param>
|
||||
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
|
|||
/// <param name="pageArgs">The page arguments.</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -18,11 +18,28 @@ public class DapperMigrationsFeature : FeatureBase
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures migrations to use SQLite.
|
||||
/// </summary>
|
||||
public DapperMigrationsFeature UseSqlite()
|
||||
{
|
||||
ConfigureRunner += runner => runner.AddSQLite();
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures migrations to use SQLite.
|
||||
/// </summary>
|
||||
public DapperMigrationsFeature UseSqlServer()
|
||||
{
|
||||
ConfigureRunner += runner => runner.AddSqlServer();
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a delegate to configure migrations.
|
||||
/// </summary>
|
||||
public Action<IMigrationRunnerBuilder> ConfigureRunner { get; set; } = runner => runner
|
||||
.AddSQLite()
|
||||
.WithGlobalConnectionString(sp => sp.GetRequiredService<IDbConnectionProvider>().GetConnectionString())
|
||||
.WithMigrationsIn(typeof(Initial).Assembly);
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SqliteDbConnectionProvider"/> class.
|
||||
|
|
@ -29,9 +38,9 @@ public class SqliteDbConnectionProvider : IDbConnectionProvider
|
|||
{
|
||||
_connectionString = connectionString;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetConnectionString() =>_connectionString;
|
||||
public string GetConnectionString() => _connectionString;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDbConnection GetConnection()
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ public class Store<T> 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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue