namespace LaaProduction.Data.SQL { using LaaProduction.Data.SQL.Interfaces; using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using System.Text; /// /// A wrapper class around the . /// Exposes , and classes /// in a fluent manner, all exposed methods can be chained in a single C# query. /// public class SQLConnection : ISQLConnection { private readonly string connectionString; private readonly Action errorCallback; /// /// When is null, white space or is not a valid connection string exception is thrown. /// /// - SQL connection string. public SQLConnection(string connectionString) => this.connectionString = connectionString; /// /// When is null, white space or is not a valid connection string exception is thrown. /// Additionaly accepts for error handling. /// Whenever the sql query throws error on execution, this will be passesd within the callback. /// /// - SQL connection string. /// - error handler for SQL execution errors. public SQLConnection(string connectionString, Action errorCallback = null) : this(connectionString) => this.errorCallback = errorCallback; /// /// Creates a wrapper that accepts a SQL query string. /// /// - SQL query string /// - that exposes basic functionalities from the . public ISQLCommand CreateCommand(string commandText) => new SQLCommand(this, commandText); /// /// Internal method called from . /// Like in a performs INSERT, UPDATE, DELETE queries and returns the number of rows affected. /// /// - SQL query string /// - the number of rows affected. internal int ExecuteNonQuery(SQLCommand command) { var rowsAffected = 0; using (var sqlConnection = new SqlConnection(this.connectionString)) { this.OpenWithErrorHandling(sqlConnection); using (var sqlCommand = sqlConnection.CreateCommand()) { sqlCommand.CommandText = command.CommandText; sqlCommand.Parameters.AddRange(command.Parameters); rowsAffected = sqlCommand.ExecuteNonQuery(); sqlCommand.Parameters.Clear(); } } return rowsAffected; } /// /// Internal method called from . /// Like in a performs SELECT queries and returns a enumerable of the results readed. /// /// Is the type that returns the /// - SQL query string /// Expression that reads specified data from the . /// internal IEnumerable ExecuteReader(SQLCommand command, Func expression) { using (var sqlConnection = new SqlConnection(this.connectionString)) { this.OpenWithErrorHandling(sqlConnection); 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); } } } } } /// /// Internal method called from . /// Like in a performs SELECT, READ, UPDATE, DELETE queries and returns first column from the first row from the result. /// /// From user specified. /// - SQL query string /// Expression that reads specified data from the . /// internal T ExecuteScalar(SQLCommand command, Func expression) { var scalarResult = default(T); using (var sqlConnection = new SqlConnection(this.connectionString)) { this.OpenWithErrorHandling(sqlConnection); 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; } /// /// Is same like but returns only the first data row. /// /// From user specified. /// - SQL query string /// Expression that reads specified data from the . /// internal T FirstOrDefault(SQLCommand command, Func expression) => this .ExecuteReader(command, expression) .FirstOrDefault(); private void OpenWithErrorHandling(SqlConnection connection) { connection.FireInfoMessageEventOnUserErrors = true; connection.InfoMessage += (sender, args) => { var errors = new StringBuilder(); foreach (SqlError e in args.Errors) { errors.Append($"Server:{e.Server}|Source:{e.Source}|State:{e.State}|Class:{e.Class}|Number:{e.Number}|Line:{e.LineNumber}|Message:{e.Message}"); } this.errorCallback?.Invoke(errors.ToString()); }; connection.Open(); } } }