2025-01-27 19:39:03 +00:00
|
|
|
using System.Data;
|
2025-03-15 01:51:42 +00:00
|
|
|
using System.Data.Common;
|
|
|
|
|
using Elsa.Sql.Models;
|
2025-01-01 23:14:09 +00:00
|
|
|
|
|
|
|
|
namespace Elsa.Sql.Client;
|
|
|
|
|
|
2025-03-15 01:51:42 +00:00
|
|
|
public abstract class BaseSqlClient : ISqlClient
|
2025-01-01 23:14:09 +00:00
|
|
|
{
|
2025-03-15 01:51:42 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// The connection string used to connect with the database.
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected readonly string _connectionString;
|
|
|
|
|
|
2025-03-15 22:06:08 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// The marker used when injecting parameters into a query.
|
|
|
|
|
/// Default: "@"
|
|
|
|
|
/// </summary>
|
|
|
|
|
public virtual string ParameterMarker { get; set; } = "@";
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-03-16 00:44:35 +00:00
|
|
|
/// The text following the <c>ParameterMarker</c> when injecting parameters into a query.
|
|
|
|
|
/// Default: "param"
|
2025-03-15 22:06:08 +00:00
|
|
|
/// </summary>
|
2025-03-16 00:44:35 +00:00
|
|
|
public virtual string ParameterText { get; set; } = "p";
|
2025-03-15 22:06:08 +00:00
|
|
|
|
|
|
|
|
/// <summary>
|
2025-03-16 00:44:35 +00:00
|
|
|
/// Set to true to add a counter to the end of the parameter string.
|
|
|
|
|
/// Default: true
|
2025-03-15 22:06:08 +00:00
|
|
|
/// </summary>
|
|
|
|
|
public virtual bool IncrementParameter { get; set; } = true;
|
|
|
|
|
|
2025-03-16 00:59:53 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Default base implementation for an SQL client.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="connectionString"></param>
|
|
|
|
|
protected BaseSqlClient(string connectionString) => _connectionString = connectionString;
|
|
|
|
|
|
2025-03-15 01:51:42 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Create a connection using the client specific connection.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
protected abstract DbConnection CreateConnection();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Create a command using the client specific connection.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="query"></param>
|
|
|
|
|
/// <param name="connection"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
protected abstract DbCommand CreateCommand(string query, DbConnection connection);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public async Task<int?> ExecuteCommandAsync(EvaluatedQuery evaluatedQuery)
|
|
|
|
|
{
|
|
|
|
|
using var connection = CreateConnection();
|
|
|
|
|
connection.Open();
|
2025-03-16 00:44:35 +00:00
|
|
|
var command = CreateCommand(evaluatedQuery.Query, connection);
|
2025-03-15 22:06:08 +00:00
|
|
|
AddCommandParameters(command, evaluatedQuery.Parameters);
|
2025-03-15 01:51:42 +00:00
|
|
|
|
|
|
|
|
var result = await command.ExecuteNonQueryAsync();
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public async Task<object?> ExecuteScalarAsync(EvaluatedQuery evaluatedQuery)
|
|
|
|
|
{
|
|
|
|
|
using var connection = CreateConnection();
|
|
|
|
|
connection.Open();
|
2025-03-16 00:44:35 +00:00
|
|
|
var command = CreateCommand(evaluatedQuery.Query, connection);
|
2025-03-15 22:06:08 +00:00
|
|
|
AddCommandParameters(command, evaluatedQuery.Parameters);
|
2025-03-15 01:51:42 +00:00
|
|
|
|
|
|
|
|
var result = await command.ExecuteScalarAsync();
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public async Task<DataSet?> ExecuteQueryAsync(EvaluatedQuery evaluatedQuery)
|
|
|
|
|
{
|
|
|
|
|
using var connection = CreateConnection();
|
|
|
|
|
connection.Open();
|
2025-03-16 00:44:35 +00:00
|
|
|
var command = CreateCommand(evaluatedQuery.Query, connection);
|
2025-03-15 22:06:08 +00:00
|
|
|
AddCommandParameters(command, evaluatedQuery.Parameters);
|
2025-03-15 01:51:42 +00:00
|
|
|
|
|
|
|
|
using var reader = await command.ExecuteReaderAsync();
|
|
|
|
|
return await Task.FromResult(ReadAsDataSet(reader));
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-15 22:06:08 +00:00
|
|
|
/// <summary>
|
2025-03-16 00:44:35 +00:00
|
|
|
/// Add parameters into the query to prevent SQL injection.
|
2025-03-15 01:51:42 +00:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="command">Command to add the parameters to</param>
|
|
|
|
|
/// <param name="parameters">Parameters to add</param>
|
|
|
|
|
/// <returns></returns>
|
2025-03-15 22:06:08 +00:00
|
|
|
private DbCommand AddCommandParameters(DbCommand command, Dictionary<string, object?> parameters)
|
2025-03-15 01:51:42 +00:00
|
|
|
{
|
|
|
|
|
foreach (var param in parameters)
|
|
|
|
|
{
|
|
|
|
|
var dbParam = command.CreateParameter();
|
|
|
|
|
dbParam.ParameterName = param.Key;
|
|
|
|
|
dbParam.Value = param.Value ?? DBNull.Value;
|
|
|
|
|
command.Parameters.Add(dbParam);
|
|
|
|
|
}
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 23:14:09 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Returns <see cref="IDataReader"/> data as a <see cref="DataSet"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="reader">Reader to return data from.</param>
|
|
|
|
|
/// <returns><see cref="DataSet"/> of data.</returns>
|
2025-03-15 01:51:42 +00:00
|
|
|
private DataSet ReadAsDataSet(IDataReader reader)
|
2025-01-01 23:14:09 +00:00
|
|
|
{
|
|
|
|
|
var dataSet = new DataSet("dataset");
|
|
|
|
|
dataSet.Tables.Add(ReadAsDataTable(reader));
|
|
|
|
|
return dataSet;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns <see cref="IDataReader"/> data as a <see cref="DataTable"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="reader">Reader to return data from.</param>
|
|
|
|
|
/// <returns><see cref="DataTable"/> of data.</returns>
|
2025-03-15 01:51:42 +00:00
|
|
|
private DataTable ReadAsDataTable(IDataReader reader)
|
2025-01-01 23:14:09 +00:00
|
|
|
{
|
|
|
|
|
var data = new DataTable();
|
|
|
|
|
var schemaTable =reader.GetSchemaTable();
|
|
|
|
|
|
|
|
|
|
foreach (DataRow row in schemaTable.Rows)
|
|
|
|
|
{
|
|
|
|
|
string colName = row.Field<string>("ColumnName");
|
|
|
|
|
Type t = row.Field<Type>("DataType");
|
|
|
|
|
data.Columns.Add(colName, t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (reader.Read())
|
|
|
|
|
{
|
|
|
|
|
var newRow = data.Rows.Add();
|
|
|
|
|
foreach (DataColumn col in data.Columns)
|
|
|
|
|
{
|
|
|
|
|
newRow[col.ColumnName] = reader[col.ColumnName];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
}
|