laatzen/LaaProductionWeb/LaaProduction.SQL/SQLConnection.cs
2024-03-06 12:48:08 +01:00

188 lines
9.1 KiB
C#

namespace LaaProduction.SQL
{
using LaaProduction.SQL.Interfaces;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
/// <summary>
/// A wrapper class around the <see cref="System.Data.SqlClient"/>.
/// Exposes <see cref="System.Data.SqlClient.SqlConnection"/>, <see cref="System.Data.SqlClient.SqlCommand"/> and <see cref="System.Data.SqlClient.SqlDataReader"/> classes
/// in a fluent manner, all exposed methods can be chained in a single C# query.
/// </summary>
public class SQLConnection : ISQLConnection
{
private readonly string connectionString;
private readonly Action<string> errorCallback;
/// <summary>
/// When <paramref name="connectionString"/> is null, white space or is not a valid connection string exception is thrown.
/// </summary>
/// <param name="connectionString"><see cref="System.String"/> - SQL connection string.</param>
public SQLConnection(string connectionString)
{
connectionString.ThrowIfNullOrWhiteSpace(nameof(connectionString));
var connectionStringBuilder = new SqlConnectionStringBuilder(connectionString);
this.connectionString = connectionStringBuilder.ToString();
}
/// <summary>
/// When <paramref name="connectionString"/> is null, white space or is not a valid connection string exception is thrown.
/// Additionaly accepts <see cref="System.Action{System.String}"/> for error handling.
/// Whenever the sql query throws error on execution, this will be passesd within the callback.
/// </summary>
/// <param name="connectionString"><see cref="System.String"/> - SQL connection string.</param>
/// <param name="errorCallback"><see cref="System.Action{System.String}"/> - error handler for SQL execution errors.</param>
public SQLConnection(string connectionString, Action<string> errorCallback = null)
: this(connectionString)
=> this.errorCallback = errorCallback;
/// <summary>
/// Creates a <see cref="LaaProduction.SQL.Interfaces.ISQLCommand"/> wrapper that accepts a SQL query string.
/// </summary>
/// <param name="commandText"><see cref="System.String"/> - SQL query string</param>
/// <returns><see cref="LaaProduction.SQL.Interfaces.ISQLCommand"/> - that exposes basic functionalities from the <see cref="System.Data.SqlClient.SqlCommand" />.</returns>
public ISQLCommand CreateCommand(string commandText)
=> new SQLCommand(this, commandText);
/// <summary>
/// Static method that creates a <see cref="LaaProduction.SQL.Interfaces.ISQLConnection"/> client with given connection string.
/// </summary>
/// <param name="connectionString"><see cref="System.String"/> - SQL connection string.</param>
/// <returns><see cref="LaaProduction.SQL.Interfaces.ISQLConnection"/></returns>
public static ISQLConnection CreateSQLConnection(string connectionString)
=> new SQLConnection(connectionString);
/// <summary>
/// Static method that creates a <see cref="LaaProduction.SQL.Interfaces.ISQLConnection"/> client with given connection string and error callback that receives all error messages.
/// </summary>
/// <param name="connectionString"><see cref="System.String"/> - SQL connection string.</param>
/// <param name="errorCallback"><see cref="System.Action{System.String}"/> - error handler for SQL execution errors.</param>
/// <returns><see cref="LaaProduction.SQL.Interfaces.ISQLConnection"/></returns>
public static ISQLConnection CreateSQLConnection(string connectionString, Action<string> errorCallback = null)
=> new SQLConnection(connectionString, errorCallback);
/// <summary>
/// Internal method called from <see cref="LaaProduction.SQL.Interfaces.ISQLCommand"/>.
/// Like in a <see cref="System.Data.SqlClient.SqlCommand"/> performs INSERT, UPDATE, DELETE queries and returns the number of rows affected.
/// </summary>
/// <param name="command"><see cref="System.String"/> - SQL query string</param>
/// <returns><see cref="System.Int32"/> - the number of rows affected.</returns>
internal int ExecuteNonQuery(SQLCommand command)
{
command.ThrowIfNull(nameof(command));
var rowsAffected = 0;
using (var sqlConnection = new SqlConnection(this.connectionString))
{
sqlConnection.OpenWithErrorHandling(this.errorCallback);
using (var sqlCommand = sqlConnection.CreateCommand())
{
sqlCommand.CommandText = command.CommandText;
sqlCommand.Parameters.AddRange(command.Parameters);
rowsAffected = sqlCommand.ExecuteNonQuery();
sqlCommand.Parameters.Clear();
}
}
return rowsAffected;
}
/// <summary>
/// Internal method called from <see cref="LaaProduction.SQL.Interfaces.ISQLCommand"/>.
/// Like in a <see cref="System.Data.SqlClient.SqlCommand"/> performs SELECT queries and returns a enumerable of the results readed.
/// </summary>
/// <typeparam name="T">Is the type that returns the <see cref="System.Func{IFluentSQLReader, T} expression"/></typeparam>
/// <param name="command"><see cref="System.String"/> - SQL query string</param>
/// <param name="expression">Expression that reads specified data from the <see cref="LaaProduction.SQL.Interfaces.ISQLReader"/>.</param>
/// <returns><see cref="System.Collections.Generic.IEnumerable{T}"/></returns>
internal IEnumerable<T> ExecuteReader<T>(SQLCommand command, Func<ISQLReader, T> expression)
{
command.ThrowIfNull(nameof(command));
expression.ThrowIfNull(nameof(expression));
using (var sqlConnection = new SqlConnection(this.connectionString))
{
sqlConnection.OpenWithErrorHandling(this.errorCallback);
using (var sqlCommand = sqlConnection.CreateCommand())
{
sqlCommand.CommandText = command.CommandText;
sqlCommand.Parameters.AddRange(command.Parameters);
using (SQLReader sqlReader = sqlCommand.ExecuteReader())
{
sqlCommand.Parameters.Clear();
while (sqlReader.Read())
{
yield return expression(sqlReader);
}
}
}
}
}
/// <summary>
/// Internal method called from <see cref="LaaProduction.SQL.Interfaces.ISQLCommand"/>.
/// Like in a <see cref="System.Data.SqlClient.SqlCommand"/> performs SELECT, READ, UPDATE, DELETE queries and returns first column from the first row from the result.
/// </summary>
/// <typeparam name="T">From user specified.</typeparam>
/// <param name="command"><see cref="System.String"/> - SQL query string</param>
/// <param name="expression">Expression that reads specified data from the <see cref="LaaProduction.SQL.Interfaces.ISQLReader"/>.</param>
/// <returns><see cref="System.Collections.Generic.IEnumerable{T}"/></returns>
internal T ExecuteScalar<T>(SQLCommand command, Func<ISQLReader, T> expression)
{
command.ThrowIfNull(nameof(command));
expression.ThrowIfNull(nameof(expression));
var scalarResult = default(T);
using (var sqlConnection = new SqlConnection(this.connectionString))
{
sqlConnection.OpenWithErrorHandling(this.errorCallback);
using (var sqlCommand = sqlConnection.CreateCommand())
{
sqlCommand.CommandText = command.CommandText;
sqlCommand.Parameters.AddRange(command.Parameters);
var scalarObject = sqlCommand.ExecuteScalar();
sqlCommand.Parameters.Clear();
if (scalarObject is T scalarValue)
{
scalarResult = scalarValue;
}
}
}
return scalarResult;
}
/// <summary>
/// Is same like <see cref="LaaProduction.SQL.SQLConnection.ExecuteReader{T}(SQLCommand, Func{ISQLReader, T})"/> but returns only the first data row.
/// </summary>
/// <typeparam name="T">From user specified.</typeparam>
/// <param name="command"><see cref="System.String"/> - SQL query string</param>
/// <param name="expression">Expression that reads specified data from the <see cref="LaaProduction.SQL.Interfaces.ISQLReader"/>.</param>
/// <returns><see cref="{T}"/></returns>
internal T FirstOrDefault<T>(SQLCommand command, Func<ISQLReader, T> expression)
=> this
.ExecuteReader(command, expression)
.FirstOrDefault();
}
}