Fix Dapper persistence provider (#4381)

This commit is contained in:
Sipke Schoorstra 2023-08-30 22:01:25 +02:00 committed by GitHub
parent 9d4add767b
commit fe02011c8b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 198 additions and 60 deletions

View file

@ -9,6 +9,8 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\modules\Elsa.Dapper.Migrations\Elsa.Dapper.Migrations.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Dapper\Elsa.Dapper.csproj" />
<ProjectReference Include="..\..\modules\Elsa.EntityFrameworkCore.SqlServer\Elsa.EntityFrameworkCore.SqlServer.csproj" />
<ProjectReference Include="..\Elsa\Elsa.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Elasticsearch\Elsa.Elasticsearch.csproj" />

View file

@ -1,5 +1,6 @@
using System.Text;
using System.Text.Encodings.Web;
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Services;
using Elsa.EntityFrameworkCore.Extensions;
using Elsa.EntityFrameworkCore.Modules.Identity;
using Elsa.EntityFrameworkCore.Modules.Management;
@ -11,13 +12,13 @@ using Elsa.MongoDb.Modules.Identity;
using Elsa.MongoDb.Modules.Management;
using Elsa.MongoDb.Modules.Runtime;
using Elsa.WorkflowServer.Web;
using Fluid;
using Microsoft.Data.Sqlite;
using Proto.Persistence.Sqlite;
using Proto.Persistence.SqlServer;
const bool useMongoDb = false;
const bool useSqlServer = false;
const bool useDapper = true;
const bool useProtoActor = true;
const bool useHangfire = false;
@ -26,8 +27,8 @@ var services = builder.Services;
var configuration = builder.Configuration;
var identitySection = configuration.GetSection("Identity");
var identityTokenSection = identitySection.GetSection("Tokens");
var sqliteConnectionString = configuration.GetConnectionString("Sqlite");
var sqlServerConnectionString = configuration.GetConnectionString("SqlServer");
var sqliteConnectionString = configuration.GetConnectionString("Sqlite")!;
var sqlServerConnectionString = configuration.GetConnectionString("SqlServer")!;
var mongoDbConnectionString = configuration.GetConnectionString("MongoDb")!;
// Add Elsa services.
@ -37,6 +38,19 @@ services
if(useMongoDb)
elsa.UseMongoDb(mongoDbConnectionString);
if(useDapper)
elsa.UseDapper(dapper =>
{
dapper.UseMigrations();
dapper.DbConnectionProvider = sp =>
{
if(useSqlServer)
return new SqlServerDbConnectionProvider(sqlServerConnectionString!);
else
return new SqliteDbConnectionProvider(sqliteConnectionString);
};
});
if (useHangfire)
elsa.UseHangfire();
@ -49,6 +63,8 @@ services
{
if(useMongoDb)
identity.UseMongoDb();
else if (useDapper)
identity.UseDapper();
else
identity.UseEntityFrameworkCore(ef =>
{
@ -69,6 +85,8 @@ services
{
if(useMongoDb)
management.UseMongoDb();
else if (useDapper)
management.UseDapper();
else
management.UseEntityFrameworkCore(ef =>
{
@ -86,6 +104,8 @@ services
{
if(useMongoDb)
runtime.UseMongoDb();
else if (useDapper)
runtime.UseDapper();
else
runtime.UseEntityFrameworkCore(ef =>
{
@ -118,7 +138,7 @@ services
.UseWorkflowsApi(api => api.AddFastEndpointsAssembly<Program>())
.UseRealTimeWorkflows()
.UseJavaScript(js => js.JintOptions = options => options.AllowClrAccess = true)
.UseLiquid(liquid => liquid.FluidOptions = options => options.Encoder = NullEncoder.Default)
.UseLiquid(liquid => liquid.FluidOptions = options => options.Encoder = HtmlEncoder.Default)
.UseHttp(http => http.HttpEndpointAuthorizationHandler = sp => sp.GetRequiredService<AllowAnonymousHttpEndpointAuthorizationHandler>())
.UseEmail(email => email.ConfigureOptions = options => configuration.GetSection("Smtp").Bind(options));
});

View file

@ -26,7 +26,7 @@
"Tokens": {
"SigningKey": "secret-signing-key",
"AccessTokenLifetime": "1:00:00:00",
"RefreshTokenLifetime": "1:00:10:00"
"RefreshTokenLifetime": "7:00:00:00"
},
"Roles": [
{

View file

@ -39,6 +39,7 @@ public class Initial : Migration
.WithColumn("Id").AsString().PrimaryKey()
.WithColumn("DefinitionId").AsString().NotNullable()
.WithColumn("Name").AsString().Nullable()
.WithColumn("ToolVersion").AsString().Nullable()
.WithColumn("Description").AsString().Nullable()
.WithColumn("ProviderName").AsString().Nullable()
.WithColumn("MaterializerName").AsString().NotNullable()

View file

@ -68,7 +68,10 @@ public class Initial : Migration
.WithColumn("Source").AsString().Nullable()
.WithColumn("SerializedActivityState").AsString().Nullable()
.WithColumn("SerializedPayload").AsString().Nullable()
.WithColumn("Timestamp").AsDateTimeOffset().NotNullable();
.WithColumn("SerializedException").AsString().Nullable()
.WithColumn("Timestamp").AsDateTimeOffset().NotNullable()
.WithColumn("Sequence").AsInt64().NotNullable()
;
IfDatabase("Sqlite")
.Create
@ -90,8 +93,11 @@ public class Initial : Migration
.WithColumn("Source").AsString().Nullable()
.WithColumn("SerializedActivityState").AsString().Nullable()
.WithColumn("SerializedPayload").AsString().Nullable()
.WithColumn("SerializedOutputs").AsString().Nullable()
.WithColumn("SerializedException").AsString().Nullable()
.WithColumn("Timestamp").AsDateTime2().NotNullable()
.WithColumn("Sequence").AsInt64().NotNullable();
.WithColumn("Sequence").AsInt64().NotNullable()
;
IfDatabase("SqlServer", "Oracle", "MySql", "Postgres")
.Create
@ -104,7 +110,7 @@ public class Initial : Migration
.WithColumn("ActivityName").AsString().Nullable()
.WithColumn("SerializedActivityState").AsString().Nullable()
.WithColumn("SerializedPayload").AsString().Nullable()
.WithColumn("SerializedOutput").AsString().Nullable()
.WithColumn("SerializedOutputs").AsString().Nullable()
.WithColumn("SerializedException").AsString().Nullable()
.WithColumn("StartedAt").AsDateTimeOffset().NotNullable()
.WithColumn("CompletedAt").AsDateTimeOffset().Nullable()
@ -121,7 +127,10 @@ public class Initial : Migration
.WithColumn("ActivityType").AsString().NotNullable()
.WithColumn("ActivityTypeVersion").AsInt32().NotNullable()
.WithColumn("ActivityName").AsString().Nullable()
.WithColumn("ActivityState").AsString().Nullable()
.WithColumn("SerializedActivityState").AsString().Nullable()
.WithColumn("SerializedPayload").AsString().Nullable()
.WithColumn("SerializedOutputs").AsString().Nullable()
.WithColumn("SerializedException").AsString().Nullable()
.WithColumn("StartedAt").AsDateTime2().NotNullable()
.WithColumn("CompletedAt").AsDateTime2().Nullable()
.WithColumn("HasBookmarks").AsBoolean().NotNullable()
@ -133,11 +142,12 @@ public class Initial : Migration
.Table("WorkflowInboxMessages")
.WithColumn("Id").AsString().PrimaryKey()
.WithColumn("ActivityTypeName").AsString().NotNullable()
.WithColumn("WorkflowInstanceId").AsString().NotNullable()
.WithColumn("WorkflowInstanceId").AsString().Nullable()
.WithColumn("ActivityInstanceId").AsString().Nullable()
.WithColumn("CorrelationId").AsString().Nullable()
.WithColumn("Hash").AsString().Nullable()
.WithColumn("BookmarkPayload").AsString()
.WithColumn("Input").AsString().Nullable()
.WithColumn("Hash").AsString().NotNullable()
.WithColumn("SerializedBookmarkPayload").AsString()
.WithColumn("SerializedInput").AsString().Nullable()
.WithColumn("CreatedAt").AsDateTimeOffset()
.WithColumn("ExpiresAt").AsDateTimeOffset()
;
@ -147,11 +157,12 @@ public class Initial : Migration
.Table("WorkflowInboxMessages")
.WithColumn("Id").AsString().PrimaryKey()
.WithColumn("ActivityTypeName").AsString().NotNullable()
.WithColumn("WorkflowInstanceId").AsString().NotNullable()
.WithColumn("WorkflowInstanceId").AsString().Nullable()
.WithColumn("ActivityInstanceId").AsString().Nullable()
.WithColumn("CorrelationId").AsString().Nullable()
.WithColumn("Hash").AsString().Nullable()
.WithColumn("BookmarkPayload").AsString()
.WithColumn("Input").AsString().Nullable()
.WithColumn("Hash").AsString().NotNullable()
.WithColumn("SerializedBookmarkPayload").AsString()
.WithColumn("SerializedInput").AsString().Nullable()
.WithColumn("CreatedAt").AsDateTime2()
.WithColumn("ExpiresAt").AsDateTime2()
;

View file

@ -53,28 +53,28 @@ public abstract class SqlDialectBase : ISqlDialect
}
/// <inheritdoc />
public virtual string Skip(int count) => $"OFFSET {count}";
public virtual string Skip(int count) => $"offset {count}";
/// <inheritdoc />
public virtual string Take(int count) => $"LIMIT {count}";
public virtual string Take(int count) => $"limit {count}";
/// <inheritdoc />
public string Insert(string table, string[] fields)
public string Insert(string table, string[] fields, Func<string, string>? getParamName = default)
{
getParamName ??= x => x;
var fieldList = string.Join(", ", fields);
var fieldParamNames = fields.Select(x => $"@{x}");
var fieldParamNames = fields.Select(x => $"@{getParamName(x)}");
var fieldParamList = string.Join(", ", fieldParamNames);
return $"INSERT INTO {table} ({fieldList}) VALUES ({fieldParamList});";
}
/// <inheritdoc />
public virtual string Upsert(string table, string primaryKeyField, string[] fields)
public virtual 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 => $"@{x}");
var fieldParamNames = fields.Select(x => $"@{getParamName(x)}");
var fieldParamList = string.Join(", ", fieldParamNames);
//var updateList = string.Join(", ", fields.Select(x => $"{x} = @{x}"));
//return $"insert into {table} ({fieldList}) values ({fieldParamList}) on conflict(id) do update set {updateList}";
return $"INSERT OR REPLACE INTO {table} ({primaryKeyField}, {fieldList}) VALUES (@{primaryKeyField}, {fieldParamList});";
return $"INSERT OR REPLACE INTO {table} ({primaryKeyField}, {fieldList}) VALUES (@{getParamName(primaryKeyField)}, {fieldParamList});";
}
}

View file

@ -101,15 +101,17 @@ public interface ISqlDialect
/// </summary>
/// <param name="table">The table.</param>
/// <param name="fields">The fields.</param>
/// <param name="getParamName">An optional function to get the parameter name.</param>
/// <returns>The query.</returns>
string Insert(string table, string[] fields);
string Insert(string table, string[] fields, Func<string, string>? getParamName = default);
/// <summary>
/// Builds an UPSERT query.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="primaryKeyField">The primary key field.</param>
/// <param name="fields">The fields.</param>
/// <param name="getParamName">An optional function to get the parameter name.</param>
/// <returns>The query.</returns>
string Upsert(string table, string primaryKeyField, string[] fields);
string Upsert(string table, string primaryKeyField, string[] fields, Func<string, string>? getParamName = default);
}

View file

@ -70,6 +70,21 @@ public static class ParameterizedQueryBuilderExtensions
return query;
}
/// <summary>
/// Begins a DELETE FROM query.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="table">The table.</param>
/// <param name="innerQuery">The inner query.</param>
public static ParameterizedQuery Delete(this ParameterizedQuery query, string table, ParameterizedQuery innerQuery)
{
query.Sql.AppendLine(query.Dialect.Delete(table));
query.Sql.AppendLine("and rowid in (");
query.Sql.AppendLine(innerQuery.Sql.ToString());
query.Sql.AppendLine(")");
return query;
}
/// <summary>
/// Begins a SELECT COUNT(*) FROM query.
/// </summary>
@ -122,7 +137,7 @@ public static class ParameterizedQueryBuilderExtensions
return query;
}
/// <summary>
/// Appends an IS NULL clause to the query.
/// </summary>
@ -133,7 +148,7 @@ public static class ParameterizedQueryBuilderExtensions
query.Sql.AppendLine(query.Dialect.IsNull(field));
return query;
}
/// <summary>
/// Appends an IS NOT NULL clause to the query.
/// </summary>
@ -262,7 +277,7 @@ public static class ParameterizedQueryBuilderExtensions
{
if (!orderFields.Any())
return query;
var clauses = string.Join(",", orderFields.Select(x => $"{x.Field} {(x.Direction == OrderDirection.Ascending ? "asc" : "desc")}"));
query.Sql.AppendLine($"order by {clauses}");
return query;
@ -314,22 +329,25 @@ public static class ParameterizedQueryBuilderExtensions
/// <param name="table">The table.</param>
/// <param name="primaryKeyField">The primary key field.</param>
/// <param name="record">The record.</param>
public static ParameterizedQuery Upsert(this ParameterizedQuery query, string table, string primaryKeyField, object record)
/// <param name="getParameterName">An optional function to get the parameter name.</param>
public static ParameterizedQuery Upsert(this ParameterizedQuery query, string table, string primaryKeyField, object record, Func<string, string>? getParameterName = default)
{
var fields = record.GetType().GetProperties()
.Where(x => x.CanRead && x.Name != primaryKeyField)
.Select(x => x.Name)
.ToArray();
query.Sql.AppendLine(query.Dialect.Upsert(table, primaryKeyField, fields));
getParameterName ??= x => x;
query.Sql.AppendLine(query.Dialect.Upsert(table, primaryKeyField, fields, getParameterName));
var primaryKeyValue = record.GetType().GetProperty(primaryKeyField)?.GetValue(record);
query.Parameters.Add($"@{primaryKeyField}", primaryKeyValue);
query.Parameters.Add($"@{getParameterName(primaryKeyField)}", primaryKeyValue);
foreach (var field in fields)
{
var value = record.GetType().GetProperty(field)?.GetValue(record);
query.Parameters.Add($"@{field}", value);
query.Parameters.Add($"@{getParameterName(field)}", value);
}
return query;
@ -341,18 +359,20 @@ public static class ParameterizedQueryBuilderExtensions
/// <param name="query">The query.</param>
/// <param name="table">The table.</param>
/// <param name="record">The record.</param>
public static ParameterizedQuery Insert(this ParameterizedQuery query, string table, object record)
/// <param name="getParameterName">An optional function to get the parameter name.</param>
public static ParameterizedQuery Insert(this ParameterizedQuery query, string table, object record, Func<string, string>? getParameterName = default)
{
var fields = record.GetType().GetProperties()
.Select(x => x.Name)
.ToArray();
query.Sql.AppendLine(query.Dialect.Insert(table, fields));
getParameterName ??= x => x;
query.Sql.AppendLine(query.Dialect.Insert(table, fields, getParameterName));
foreach (var field in fields)
{
var value = record.GetType().GetProperty(field)?.GetValue(record);
query.Parameters.Add($"@{field}", value);
query.Parameters.Add($"@{getParameterName(field)}", value);
}
return query;

View file

@ -27,6 +27,7 @@ public class DapperFeature : FeatureBase
/// </summary>
public Func<IServiceProvider, IDbConnectionProvider> DbConnectionProvider { get; set; } = _ => new SqliteDbConnectionProvider();
/// <inheritdoc />
public override void Apply()
{
Services.AddSingleton(DbConnectionProvider);

View file

@ -18,4 +18,5 @@ internal class WorkflowDefinitionRecord
public int Version { get; set; } = 1;
public bool IsLatest { get; set; }
public bool IsPublished { get; set; }
public bool IsReadonly { get; set; } = false;
}

View file

@ -195,6 +195,7 @@ public class DapperWorkflowDefinitionStore : IWorkflowDefinitionStore
Description = source.Description,
IsPublished = source.IsPublished,
IsLatest = source.IsLatest,
IsReadonly = source.IsReadonly,
CreatedAt = source.CreatedAt,
StringData = source.StringData,
Options = props.Options,
@ -232,6 +233,7 @@ public class DapperWorkflowDefinitionStore : IWorkflowDefinitionStore
IsPublished = source.IsPublished,
IsLatest = source.IsLatest,
CreatedAt = source.CreatedAt,
IsReadonly = source.IsReadonly,
StringData = source.StringData,
Props = _payloadSerializer.Serialize(props),
MaterializerName = source.MaterializerName,

View file

@ -1,8 +1,10 @@
using Elsa.Dapper.Features;
using Elsa.Dapper.Modules.Runtime.Stores;
using Elsa.Dapper.Services;
using Elsa.Features.Abstractions;
using Elsa.Features.Attributes;
using Elsa.Features.Services;
using Elsa.Workflows.Runtime.Entities;
using Elsa.Workflows.Runtime.Features;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;

View file

@ -15,6 +15,7 @@ internal class WorkflowExecutionLogRecordRecord
public string? ActivityName { get; set; } = default!;
public string NodeId { get; set; } = default!;
public DateTimeOffset Timestamp { get; set; }
public long Sequence { get; set; }
public string? EventName { get; set; }
public string? Message { get; set; }
public string? Source { get; set; }

View file

@ -18,7 +18,7 @@ public class WorkflowInboxMessageRecord
/// <summary>
/// An optional bookmark payload that can be used to filter the workflow instances to deliver the message to.
/// </summary>
public string BookmarkPayload { get; set; } = default!;
public string SerializedBookmarkPayload { get; set; } = default!;
/// <summary>
/// The hash of the bookmark.
@ -43,7 +43,7 @@ public class WorkflowInboxMessageRecord
/// <summary>
/// An optional set of inputs to deliver to the workflow instance.
/// </summary>
public string? Input { get; set; }
public string? SerializedInput { get; set; }
/// <summary>
/// The date and time the message was created.

View file

@ -1,3 +1,4 @@
using Elsa.Dapper.Contracts;
using Elsa.Dapper.Extensions;
using Elsa.Dapper.Models;
using Elsa.Dapper.Modules.Runtime.Records;
@ -18,7 +19,7 @@ namespace Elsa.Dapper.Modules.Runtime.Stores;
/// </summary>
public class DapperActivityExecutionRecordStore : IActivityExecutionStore
{
private const string TableName = "WorkflowExecutionLogRecords";
private const string TableName = "ActivityExecutionRecords";
private const string PrimaryKeyName = "Id";
private readonly IPayloadSerializer _payloadSerializer;
private readonly ISafeSerializer _safeSerializer;
@ -27,11 +28,11 @@ public class DapperActivityExecutionRecordStore : IActivityExecutionStore
/// <summary>
/// Initializes a new instance of the <see cref="DapperActivityExecutionRecordStore"/> class.
/// </summary>
public DapperActivityExecutionRecordStore(IPayloadSerializer payloadSerializer, ISafeSerializer safeSerializer, Store<ActivityExecutionRecordRecord> store)
public DapperActivityExecutionRecordStore(IDbConnectionProvider dbConnectionProvider, IPayloadSerializer payloadSerializer, ISafeSerializer safeSerializer)
{
_payloadSerializer = payloadSerializer;
_safeSerializer = safeSerializer;
_store = store;
_store = new Store<ActivityExecutionRecordRecord>(dbConnectionProvider, TableName, PrimaryKeyName);
}
/// <inheritdoc />
@ -83,7 +84,7 @@ public class DapperActivityExecutionRecordStore : IActivityExecutionStore
if (filter.Completed != null)
{
if(filter.Completed == true)
if (filter.Completed == true)
query.IsNotNull(nameof(ActivityExecutionRecordRecord.CompletedAt));
else
query.IsNull(nameof(ActivityExecutionRecordRecord.CompletedAt));

View file

@ -121,6 +121,7 @@ public class DapperWorkflowExecutionLogStore : IWorkflowExecutionLogStore
ActivityName = source.ActivityName,
NodeId = source.NodeId,
Timestamp = source.Timestamp,
Sequence = source.Sequence,
EventName = source.EventName,
Message = source.Message,
Source = source.Source,
@ -146,6 +147,7 @@ public class DapperWorkflowExecutionLogStore : IWorkflowExecutionLogStore
ActivityName = source.ActivityName,
NodeId = source.NodeId,
Timestamp = source.Timestamp,
Sequence = source.Sequence,
EventName = source.EventName,
Message = source.Message,
Source = source.Source,

View file

@ -83,7 +83,7 @@ public class DapperWorkflowInboxMessageStore : IWorkflowInboxMessageStore
clauses.Add(clause);
}
var clausesSql = string.Join(" OR ", $"({clauses.Select(x => x.Sql)})");
var clausesSql = string.Join(" OR ", clauses.Select(x => x.Sql.ToString()));
query.Sql.AppendLine(clausesSql);
}
@ -99,8 +99,8 @@ public class DapperWorkflowInboxMessageStore : IWorkflowInboxMessageStore
CorrelationId = source.CorrelationId,
ActivityInstanceId = source.ActivityInstanceId,
Hash = source.Hash,
BookmarkPayload = _payloadSerializer.Serialize(source.BookmarkPayload),
Input = source.Input != null ? _payloadSerializer.Serialize(source.Input) : default,
SerializedBookmarkPayload = _payloadSerializer.Serialize(source.BookmarkPayload),
SerializedInput = source.Input != null ? _payloadSerializer.Serialize(source.Input) : default,
CreatedAt = source.CreatedAt,
ExpiresAt = source.ExpiresAt,
};
@ -116,8 +116,8 @@ public class DapperWorkflowInboxMessageStore : IWorkflowInboxMessageStore
CorrelationId = source.CorrelationId,
ActivityInstanceId = source.ActivityInstanceId,
Hash = source.Hash,
BookmarkPayload = _payloadSerializer.Deserialize(source.BookmarkPayload),
Input = source.Input != null ? _payloadSerializer.Deserialize<Dictionary<string, object>>(source.Input) : default,
BookmarkPayload = _payloadSerializer.Deserialize(source.SerializedBookmarkPayload),
Input = source.SerializedInput != null ? _payloadSerializer.Deserialize<Dictionary<string, object>>(source.SerializedInput) : default,
CreatedAt = source.CreatedAt,
ExpiresAt = source.ExpiresAt,
};

View file

@ -0,0 +1,47 @@
using System.Data;
using Elsa.Dapper.Contracts;
using Elsa.Dapper.Dialects;
using JetBrains.Annotations;
using Microsoft.Data.SqlClient;
namespace Elsa.Dapper.Services;
/// <summary>
/// Provides a SQLite connection to the database.
/// </summary>
[PublicAPI]
public class SqlServerDbConnectionProvider : IDbConnectionProvider
{
private readonly string _connectionString = "Server=localhost;Database=Elsa;Trusted_Connection=True;";
/// <summary>
/// Initializes a new instance of the <see cref="SqlServerDbConnectionProvider"/> class.
/// </summary>
public SqlServerDbConnectionProvider()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SqlServerDbConnectionProvider"/> class.
/// </summary>
/// <param name="connectionString">The connection string to use.</param>
public SqlServerDbConnectionProvider(string connectionString)
{
_connectionString = connectionString;
}
/// <inheritdoc />
public string GetConnectionString() =>_connectionString;
/// <inheritdoc />
public IDbConnection GetConnection()
{
return new SqlConnection
{
ConnectionString = GetConnectionString()
};
}
/// <inheritdoc />
public ISqlDialect Dialect => new SqliteDialect();
}

View file

@ -12,8 +12,26 @@ namespace Elsa.Dapper.Services;
[PublicAPI]
public class SqliteDbConnectionProvider : IDbConnectionProvider
{
private readonly string _connectionString = "Data Source=elsa.dapper.db";
/// <summary>
/// Initializes a new instance of the <see cref="SqliteDbConnectionProvider"/> class.
/// </summary>
public SqliteDbConnectionProvider()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SqliteDbConnectionProvider"/> class.
/// </summary>
/// <param name="connectionString">The connection string to use.</param>
public SqliteDbConnectionProvider(string connectionString)
{
_connectionString = connectionString;
}
/// <inheritdoc />
public string GetConnectionString() => "Data Source=elsa.dapper.db";
public string GetConnectionString() =>_connectionString;
/// <inheritdoc />
public IDbConnection GetConnection()

View file

@ -205,9 +205,14 @@ public class Store<T> where T : notnull
public async Task SaveManyAsync(IEnumerable<T> records, string primaryKey = "Id", CancellationToken cancellationToken = default)
{
var query = new ParameterizedQuery(_dbConnectionProvider.Dialect);
var currentIndex = 0;
foreach (var record in records)
query.Upsert(TableName, primaryKey, record);
{
var index = currentIndex;
query.Upsert(TableName, primaryKey, record, field => $"{field}_{index}");
currentIndex++;
}
using var connection = _dbConnectionProvider.GetConnection();
await query.ExecuteAsync(connection);
@ -249,11 +254,13 @@ public class Store<T> where T : notnull
/// <returns>The number of records deleted.</returns>
public async Task<long> DeleteAsync(Action<ParameterizedQuery> filter, PageArgs pageArgs, IEnumerable<OrderField> orderFields, CancellationToken cancellationToken = default)
{
var selectQuery = _dbConnectionProvider.CreateQuery().From(TableName, "rowid");
filter(selectQuery);
selectQuery = selectQuery.OrderBy(orderFields.ToArray()).Page(pageArgs);
var deleteQuery = _dbConnectionProvider.CreateQuery().Delete(TableName, selectQuery);
using var connection = _dbConnectionProvider.GetConnection();
var query = _dbConnectionProvider.CreateQuery().Delete(TableName);
filter(query);
query = query.OrderBy(orderFields.ToArray()).Page(pageArgs);
return await query.ExecuteAsync(connection);
return await deleteQuery.ExecuteAsync(connection);
}
/// <summary>

View file

@ -70,7 +70,7 @@ internal class List : ElsaEndpoint<Request, PagedListResponse<WorkflowDefinition
{
var order = new WorkflowDefinitionOrder<string>
{
KeySelector = p => p.Name ?? string.Empty,
KeySelector = p => p.Name!,
Direction = direction
};