diff --git a/README.md b/README.md index 5d40e52..f9aebb5 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,17 @@ This package is a simple, easy and fluent way to execute SQL on database connect You can install the package using one of the options bellow: - Package Manager ``` -PM> NuGet\Install-Package Dapper.FluentExecution -Version 0.1.1 +PM> NuGet\Install-Package Dapper.FluentExecution -Version 0.2.0 ``` - .NET CLI ``` -dotnet add package Dapper.FluentExecution --version 0.1.1 +dotnet add package Dapper.FluentExecution --version 0.2.0 ``` - PackageReference ``` - + ``` ## Usage @@ -49,14 +49,13 @@ public async Task GetByIdAsync(int id, int? specificAddressId, Cancella .WithParameter("@Id", id) .QuerySingleOrDefaultAsync(cancellation); ``` -## TODO - v0.2.0 -- [ ] Add unit tests for `IExecutionBuilder` methods -- [ ] Add summary doc on all .cs files -- [ ] Add suport to `Execution` methods on `IExecutionBuilder`. E.g.: `Execute, ExecuteAsync, ExecuteScalar, ExecuteScalarAsync...`. -- [ ] Add support to dynamic parameter by dynamic object. E.g.: `new { Param1 = "1", Param2 = 2 }` -- [ ] Add support to set command timeout -- [ ] Add support to set query as an execution of stored procedure -- [ ] Add `QueryAsync, Query,...` an `IEnumerable` result +## Release - v0.2.0 +- Add summary doc on all .cs files +- Add suport to `Execution` methods on `IExecutionBuilder`. E.g.: `Execute, ExecuteAsync, ExecuteScalar, ExecuteScalarAsync...`. +- Add support to dynamic parameter by dynamic object. E.g.: `new { Param1 = "1", Param2 = 2 }` +- Add support to set command timeout +- Add support to set query as an execution of stored procedure +- Add `QueryAsync, Query,...` an `IEnumerable` result ## Release - v0.1.1 - Fixing `csproj` documentation to Nuget.org diff --git a/src/fluent-execution/AsyncResult.cs b/src/fluent-execution/AsyncResult.cs index 7995684..bc766d5 100644 --- a/src/fluent-execution/AsyncResult.cs +++ b/src/fluent-execution/AsyncResult.cs @@ -1,5 +1,8 @@ namespace Dapper.FluentExecution; +/// +///It's helper struct to allows returns a async result at fluent way +/// public struct AsyncResult { private readonly Task> taskResult; @@ -9,6 +12,16 @@ internal AsyncResult(Task> result) this.taskResult = result; } + /// + ///Performs an awaitable result as of + /// + ///An IEnumerable of + public async Task> GetResultAsync() => await taskResult; + + /// + ///Performs awaitable result as a of + /// + ///A list of public async Task> ToListAsync() { var newResult = await taskResult; @@ -16,6 +29,10 @@ public async Task> ToListAsync() return newResult.ToList(); } + /// + ///Performs an awaitable result as an of + /// + ///An array of public async Task ToArrayAsync() { var newResult = await taskResult; diff --git a/src/fluent-execution/Dapper.FluentExecution.csproj b/src/fluent-execution/Dapper.FluentExecution.csproj index f6b0285..71f050b 100644 --- a/src/fluent-execution/Dapper.FluentExecution.csproj +++ b/src/fluent-execution/Dapper.FluentExecution.csproj @@ -14,7 +14,7 @@ Augusto Mesquita Aucamana augustodeveloper - 0.1.1 + 0.2.0 Dapper.FluentExecution Dapper;Dapper.Fluent;Dapper.Query;Execution;Dapper.Execution MIT diff --git a/src/fluent-execution/ExecutionSqlBuilder.cs b/src/fluent-execution/ExecutionSqlBuilder.cs index 447dccb..6641df0 100644 --- a/src/fluent-execution/ExecutionSqlBuilder.cs +++ b/src/fluent-execution/ExecutionSqlBuilder.cs @@ -10,6 +10,8 @@ internal class ExecutionSqlBuilder : IExecutionBuilder private readonly StringBuilder sqlBuilder; private DynamicParameters? parameters; private IDbTransaction? transaction; + private CommandType commandType; + private TimeSpan? commandTimeout; private DynamicParameters Parameters => GetParameters(); @@ -17,6 +19,7 @@ protected ExecutionSqlBuilder(string rootSql, IDbConnection connection) { this.connection = connection; this.sqlBuilder = new(rootSql); + this.commandType = CommandType.Text; } internal static IExecutionBuilder New(string? sql, IDbConnection? connection) @@ -33,7 +36,6 @@ internal static IExecutionBuilder New(string? sql, IDbConnection? connection) #else ArgumentNullException.ThrowIfNull(connection); #endif - return new ExecutionSqlBuilder(sql!, connection!); } @@ -47,8 +49,23 @@ private DynamicParameters GetParameters() return parameters; } + private int? GetCommandTimeout() + { + if (!commandTimeout.HasValue) + { + return null; + } + + if (commandTimeout.Value.Seconds < 1) + { + return null; + } + + return commandTimeout.Value.Seconds; + } + private CommandDefinition BuildCommandDefinition(CancellationToken cancellation = default) - => new CommandDefinition(sqlBuilder.ToString(), parameters, transaction: transaction, cancellationToken: cancellation); + => new CommandDefinition(sqlBuilder.ToString(), parameters, transaction, GetCommandTimeout(), commandType, cancellationToken: cancellation); IExecutionBuilder IExecutionBuilder.WithTransaction(IDbTransaction transaction) { @@ -57,6 +74,16 @@ IExecutionBuilder IExecutionBuilder.WithTransaction(IDbTransaction transaction) return this; } + IExecutionBuilder IExecutionBuilder.AsStoredProcedure() + { + if (commandType != CommandType.StoredProcedure) + { + commandType = CommandType.StoredProcedure; + } + + return this; + } + IExecutionBuilder IExecutionBuilder.AppendSql(bool condition, string sql) { if (condition) @@ -118,6 +145,29 @@ IExecutionBuilder IExecutionBuilder.WithParameter(string parameterName, object v return this; } + IExecutionBuilder IExecutionBuilder.WithParameter(bool condition, object values) + { + if (condition) + { + Parameters.AddDynamicParams(values); + } + + return this; + } + + IExecutionBuilder IExecutionBuilder.WithParameter(object values) + { + Parameters.AddDynamicParams(values); + + return this; + } + + IExecutionBuilder IExecutionBuilder.WithCommandTimeout(TimeSpan timeout) + { + this.commandTimeout = timeout; + return this; + } + IEnumerable IExecutionBuilder.Query() => this.connection.Query(BuildCommandDefinition()); IEnumerable IExecutionBuilder.Query() => this.connection.Query(BuildCommandDefinition()); @@ -158,15 +208,9 @@ AsyncResult IExecutionBuilder.QueryAsync(CancellationToken cancellation return new(result); } - async Task IExecutionBuilder.QuerySingleAsync(CancellationToken cancellation) - { - return await this.connection.QuerySingleAsync(BuildCommandDefinition(cancellation)); - } + async Task IExecutionBuilder.QuerySingleAsync(CancellationToken cancellation) => await this.connection.QuerySingleAsync(BuildCommandDefinition(cancellation)); - async Task IExecutionBuilder.QuerySingleAsync(CancellationToken cancellation) - { - return await this.connection.QuerySingleAsync(BuildCommandDefinition(cancellation)); - } + async Task IExecutionBuilder.QuerySingleAsync(CancellationToken cancellation) => await this.connection.QuerySingleAsync(BuildCommandDefinition(cancellation)); async Task IExecutionBuilder.QuerySingleOrDefaultAsync(CancellationToken cancellation) { @@ -193,15 +237,9 @@ async Task IExecutionBuilder.QuerySingleAsync(CancellationToken cancellati } - async Task IExecutionBuilder.QueryFirstAsync(CancellationToken cancellation) - { - return await this.connection.QueryFirstAsync(BuildCommandDefinition(cancellation)); - } + async Task IExecutionBuilder.QueryFirstAsync(CancellationToken cancellation) => await this.connection.QueryFirstAsync(BuildCommandDefinition(cancellation)); - async Task IExecutionBuilder.QueryFirstAsync(CancellationToken cancellation) - { - return await this.connection.QueryFirstAsync(BuildCommandDefinition(cancellation)); - } + async Task IExecutionBuilder.QueryFirstAsync(CancellationToken cancellation) => await this.connection.QueryFirstAsync(BuildCommandDefinition(cancellation)); async Task IExecutionBuilder.QueryFirstOrDefaultAsync(CancellationToken cancellation) { @@ -232,4 +270,20 @@ async Task IExecutionBuilder.QueryMultipleAsync(Func this.connection.Execute(BuildCommandDefinition()); + + async Task IExecutionBuilder.ExecuteAsync(CancellationToken cancellation) => await this.connection.ExecuteAsync(BuildCommandDefinition(cancellation)); + + object IExecutionBuilder.ExecuteScalar() => this.connection.ExecuteScalar(BuildCommandDefinition()); + + T IExecutionBuilder.ExecuteScalar() => this.connection.ExecuteScalar(BuildCommandDefinition()); + + async Task IExecutionBuilder.ExecuteScalarAsync(CancellationToken cancellation) => await this.connection.ExecuteScalarAsync(BuildCommandDefinition(cancellation)); + + async Task IExecutionBuilder.ExecuteScalarAsync(CancellationToken cancellation) => await this.connection.ExecuteScalarAsync(BuildCommandDefinition(cancellation)); + + IDataReader IExecutionBuilder.ExecuteDataReader() => this.connection.ExecuteReader(BuildCommandDefinition()); + + async Task IExecutionBuilder.ExecuteDataReaderAsync(CancellationToken cancellation) => await this.connection.ExecuteReaderAsync(BuildCommandDefinition(cancellation)); } diff --git a/src/fluent-execution/StringExtensions.cs b/src/fluent-execution/StringExtensions.cs index 5cdce64..5972bfb 100644 --- a/src/fluent-execution/StringExtensions.cs +++ b/src/fluent-execution/StringExtensions.cs @@ -3,8 +3,21 @@ namespace Dapper.FluentExecution; +/// +/// Extension class to able starts a fluent query execution +/// on connection +/// public static class StringExtensions { + /// + /// Starts a fluent query execution from sql string + /// by that instantiate a + /// + ///SQL text to perform a query on connection + ///Database connection that allows to perform query + ///An instance + ///It could be throws if is invalid as null, empty or whitespace value + ///It could be throws if is invalid as null value public static IExecutionBuilder On(this string? sql, IDbConnection? connection) { return ExecutionSqlBuilder.New(sql, connection); diff --git a/src/fluent-execution/abstractions/IExecutionBuilder.cs b/src/fluent-execution/abstractions/IExecutionBuilder.cs index d69f5ba..d5d0b87 100644 --- a/src/fluent-execution/abstractions/IExecutionBuilder.cs +++ b/src/fluent-execution/abstractions/IExecutionBuilder.cs @@ -3,49 +3,341 @@ namespace Dapper.FluentExecution.Abstractions; /// +///Defines a protocol to makes the execution on +///database connection as fluent with Dapper extensions /// public interface IExecutionBuilder { + /// + ///Appends a SQL script string based on condition argument. + /// + ///SQL script text + ///Indicates wether the SQL script should be appended to execution + ///An instance of builder IExecutionBuilder AppendSql(bool condition, string sql); + + /// + ///Adds a transaction allows the execution can be commited or not + /// + ///Transaction instance to controls the execution on database + ///An instance of builder IExecutionBuilder WithTransaction(IDbTransaction transaction); + /// + ///Adds to the fluent execution a command timeout, the value will be converted to + ///seconds + /// + ///Indicates the timeout as + ///An instance of builder + IExecutionBuilder WithCommandTimeout(TimeSpan timeout); + + /// + ///Indicates the SQL script text is a Stored Procedure execution + /// + ///An instance of builder + IExecutionBuilder AsStoredProcedure(); + + /// + ///Adds a parameter of specific type and size indicating + /// + ///Identificator of parameter on execution + ///Database type of parameter + ///Object value to transform each property in parameters + ///Size of value on database + ///An instance of builder IExecutionBuilder WithParameter(string parameterName, DbType dbType, object value, int size); + + /// + ///Adds a parameter of specific type and size indicating, in cases the condition is true. + /// + ///Indicates wether parameter should be added to execution + ///Identificator of parameter on execution + ///Database type of parameter + ///Object value to transform each property in parameters + ///Size of value on database + ///An instance of builder IExecutionBuilder WithParameter(bool condition, string parameterName, DbType dbType, object value, int size); + + /// + ///Adds a parameter of specific type + /// + ///Identificator of parameter on execution + ///Database type of parameter + ///Object value to transform each property in parameters + ///An instance of builder IExecutionBuilder WithParameter(string parameterName, DbType dbType, object value); + + /// + ///Adds a parameter of specific type in cases the condition is true. + /// + ///Indicates wether parameter should be added to execution + ///Identificator of parameter on execution + ///Database type of parameter + ///Object value to transform each property in parameters + ///An instance of builder IExecutionBuilder WithParameter(bool condition, string parameterName, DbType dbType, object value); + + /// + ///Adds a parameter with identificator and + ///with stored value on . + /// + ///Identificator of parameter on execution + ///Object value to transform each property in parameters + ///An instance of builder IExecutionBuilder WithParameter(string parameterName, object value); + + /// + ///Adds a parameter with identificator and + ///with stored value on , in cases the + ///is true. + /// + ///Indicates wether parameter should be added to execution + ///param name="parameterName">Identificator of parameter on execution + ///Object value to transform each property in parameters + ///An instance of builder IExecutionBuilder WithParameter(bool condition, string parameterName, object value); + /// + /// Adds a dynamic object as paramters values by each property will + /// transform a parameter to execution, in cases the is true. + /// For Example: + /// + /// var dynParam = new { Param1 = "1", Param2 = 2 }; + /// builder.WithParameter(true, dynParam); + /// + ///Indicates wether parameters should be added to execution + ///Object value to transform each property in parameters + ///An instance of builder + IExecutionBuilder WithParameter(bool condition, object values); + + /// + /// Adds a dynamic object as paramters values by each property will + /// transform a parameter to execution + /// For Example: + /// + /// var dynParam = new { Param1 = "1", Param2 = 2 }; + /// builder.WithParameter(dynParam); + /// + ///Object value to transform each property in parameters + ///An instance of builder + IExecutionBuilder WithParameter(object values); + + /// + ///Executes a query by all SQL script text and all previous configurations or parameters added. + /// + ///An enumerable value from query executed IEnumerable Query(); + + /// + ///Executes a query by all SQL script text and all previous configurations or parameters added. + /// + ///An enumerable of value from query executed IEnumerable Query(); + /// + ///Executes a query by all SQL script text and all previous configurations or parameters added. + /// + ///Allows to cancel all operation by the cancellatio token + ///An instance to result enumerable values AsyncResult QueryAsync(CancellationToken cancellation = default); + + /// + ///Executes a query by all SQL script text and all previous configurations or parameters added. + /// + ///Allows to cancel all operation by the cancellatio token + ///An instance to result enumerable values AsyncResult QueryAsync(CancellationToken cancellation = default); + /// + ///Executes a query by all SQL script text and all previous configurations or parameters added. + ///The query should result a single result. + /// + ///A value as single result from query dynamic QuerySingle(); + + /// + ///Executes a query by all SQL script text and all previous configurations or parameters added. + ///The query should result a single result. + /// + ///A value as single result from query T QuerySingle(); + /// + ///Executes an awaitable query by all SQL script text and all previous configurations or parameters added. + ///The query should result a single result. + /// + ///Allows to cancel all operation by the cancellatio token + ///A value as single result from query Task QuerySingleAsync(CancellationToken cancellation = default); + + /// + ///Executes an awaitable query by all SQL script text and all previous configurations or parameters added. + ///The query should result a single result. + /// + ///Allows to cancel all operation by the cancellatio token + ///A value as single result from query Task QuerySingleAsync(CancellationToken cancellation = default); + /// + ///Executes a query by all SQL script text and all previous configurations or parameters added. + ///The query should result a single result, otherwise, should returns a default value. + /// + ///A nullable value as single result from query dynamic? QuerySingleOrDefault(); + + /// + ///Executes a query by all SQL script text and all previous configurations or parameters added. + ///The query should result a single result, otherwise, should returns a default value. + /// + ///A nullable value as single result from query T? QuerySingleOrDefault(); + /// + ///Executes an awaitable query by all SQL script text and all previous configurations or parameters added. + ///The query should result a single result, otherwise, should returns a default value. + /// + ///Allows to cancel all operation by the cancellatio token + ///A nullable value as single result from query Task QuerySingleOrDefaultAsync(CancellationToken cancellation = default); + + /// + ///Executes an awaitable query by all SQL script text and all previous configurations or parameters added. + ///The query should result a single result, otherwise, should returns a default value. + /// + ///Allows to cancel all operation by the cancellatio token + ///A nullable value as single result from query Task QuerySingleOrDefaultAsync(CancellationToken cancellation = default); + /// + ///Executes a query by all SQL script text and all previous configurations or parameters added. + ///The query can result multiple rows or at least one row, but this operation will result first row. + /// + ///A value as first result from query dynamic QueryFirst(); + + /// + ///Executes a query by all SQL script text and all previous configurations or parameters added. + ///The query can result multiple rows or at least one row, but this operation will result first row. + /// + ///A value as first result from query T QueryFirst(); + + /// + ///Executes an awaitable query by all SQL script text and all previous configurations or parameters added. + ///The query can result multiple rows or at least one row, but this operation will result first row. + /// + ///Allows to cancel all operation by the cancellatio token + ///A value as first result from query Task QueryFirstAsync(CancellationToken cancellation = default); + + /// + ///Executes an awaitable query by all SQL script text and all previous configurations or parameters added. + ///The query can result multiple rows or at least one row, but this operation will result first row. + /// + ///Allows to cancel all operation by the cancellatio token + ///A value as first result from query Task QueryFirstAsync(CancellationToken cancellation = default); + /// + ///Executes a query by all SQL script text and all previous configurations or parameters added. + ///The query can result multiple rows, but this operation will result first row. In cases the + ///query returns an empty result, it will returns a default value + /// + ///A value as first result from query dynamic? QueryFirstOrDefault(); + + /// + ///Executes a query by all SQL script text and all previous configurations or parameters added. + ///The query can result multiple rows, but this operation will result first row. In cases the + ///query returns an empty result, it will returns a default value + /// + ///A value as first result from query T? QueryFirstOrDefault(); + /// + ///Executes an awaitable query by all SQL script text and all previous configurations or parameters added. + ///The query can result multiple rows, but this operation will result first row. In cases the + ///query returns an empty result, it will returns a default value + /// + ///Allows to cancel all operation by the cancellatio token + ///A value as first result from query Task QueryFirstOrDefaultAsync(CancellationToken cancellation = default); + + /// + ///Executes an awaitable query by all SQL script text and all previous configurations or parameters added. + ///The query can result multiple rows, but this operation will result first row. In cases the + ///query returns an empty result, it will returns a default value + /// + ///Allows to cancel all operation by the cancellatio token + ///A value as first result from query Task QueryFirstOrDefaultAsync(CancellationToken cancellation = default); + /// + ///Executes a multiple queries by SQL scripts and all previous configurations or parameters added. + ///The queries can be result multiples rows and all results will merged by a function. + /// + ///An function to merged multiple results from query + ///A merged value T QueryMultiple(Func func); + + /// + ///Executes an awaitable multiple queries by SQL scripts and all previous configurations or parameters added. + ///The queries can be result multiples rows and all results will merged by a function. + /// + ///An function to merged multiple results from query + ///Allows to cancel all operation by the cancellatio token + ///A merged value Task QueryMultipleAsync(Func> funcTask, CancellationToken cancellation = default); + + /// + ///Executes a SQL scripts that can result a number modifications on execution. + /// + ///A number of modifications occurred on execution + int Execute(); + + /// + ///Executes an awaitable SQL scripts that can result a number modifications on execution. + /// + ///Allows to cancel all operation by the cancellatio token + ///A number of modifications occurred on execution + Task ExecuteAsync(CancellationToken cancellation = default); + + /// + ///Executes a SQL scripts that will result a object value + /// + ///An object value from script execution + object ExecuteScalar(); + + /// + ///Executes a SQL scripts that will result a object value + /// + ///A value from script execution + T ExecuteScalar(); + + /// + ///Executes an awaitable SQL scripts that will result a object value + /// + ///An object value from script execution + Task ExecuteScalarAsync(CancellationToken cancellation = default); + + /// + ///Executes an awaitable SQL scripts that will result a object value + /// + ///Allows to cancel all operation by the cancellatio token + ///A value from script execution + Task ExecuteScalarAsync(CancellationToken cancellation = default); + + /// + ///Executes a SQL script and results a way to iterates all rows from script + /// + ///A value from script execution + IDataReader ExecuteDataReader(); + + /// + ///Executes an awaitable SQL script and results a way to iterates all rows from script + /// + ///Allows to cancel all operation by the cancellatio token + ///A value from script execution + Task ExecuteDataReaderAsync(CancellationToken cancellation = default); } diff --git a/src/fluent-execution/sqlserver/SqlServerExecutionSqlBuilder.cs b/src/fluent-execution/sqlserver/SqlServerExecutionSqlBuilder.cs index 417b025..7621385 100644 --- a/src/fluent-execution/sqlserver/SqlServerExecutionSqlBuilder.cs +++ b/src/fluent-execution/sqlserver/SqlServerExecutionSqlBuilder.cs @@ -1,14 +1,12 @@ using System.Data; using Dapper.FluentExecution.Abstractions; -using Microsoft.Data.SqlClient; -using Microsoft.Data.SqlClient.Server; namespace Dapper.FluentExecution.SqlServer; public static class SqlServerExecutionSqlBuilderExtension { /// https://github.com/dotnet/SqlClient/blob/0156df230c4426be535ad6af32ac7d16188377d1/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs#L239 - public static DbType ToDbType(this SqlDbType dbType) + internal static DbType ToDbType(this SqlDbType dbType) => dbType switch { SqlDbType.VarChar => DbType.AnsiString, @@ -37,15 +35,53 @@ public static DbType ToDbType(this SqlDbType dbType) _ => throw new NotSupportedException($"{dbType} is not supported") }; + /// + ///Add a parameter on with specific data types of SQL Server client, + ///only if is , otherwise, nothing added. + /// + ///Builder instance to adds new parameter + ///Coditional parameter to add parameter in cases the result is + ///Parameter name without '@'. This method already add prefix `@` + ///Type identifier on SQL Server client + ///Parameter value + ///An instance public static IExecutionBuilder WithSqlParameter(this IExecutionBuilder builder, bool condition, string parameterName, SqlDbType dbType, object value) => builder.WithParameter(condition, $"@{parameterName}", dbType.ToDbType(), value); - public static IExecutionBuilder WithSqlParameter(this IExecutionBuilder builder, bool condition, string parameterName, SqlDbType dbType, object value, int size) + /// + ///Add a parameter on with specific data types of SQL Server client, + ///only if is , otherwise, nothing added. + /// + ///Builder instance to adds new parameter + ///Coditional parameter to add parameter in cases the result is + ///Parameter name without '@'. This method already add prefix `@` + ///Type identifier on SQL Server client + ///Size of + ///Parameter value + ///An instance + public static IExecutionBuilder WithSqlParameter(this IExecutionBuilder builder, bool condition, string parameterName, SqlDbType dbType, int size, object value) => builder.WithParameter(condition, $"@{parameterName}", dbType.ToDbType(), value, size); + /// + ///Add a parameter on with specific data types of SQL Server client + /// + ///Builder instance to adds new parameter + ///Parameter name without '@'. This method already add prefix `@` + ///Type identifier on SQL Server client + ///Parameter value + ///An instance public static IExecutionBuilder WithSqlParameter(this IExecutionBuilder builder, string parameterName, SqlDbType dbType, object value) => builder.WithParameter($"@{parameterName}", dbType.ToDbType(), value); + /// + ///Add a parameter on with specific data types of SQL Server client + /// + ///Builder instance to adds new parameter + ///Parameter name without '@'. This method already add prefix `@` + ///Type identifier on SQL Server client + ///Size of + ///Parameter value + ///An instance public static IExecutionBuilder WithSqlParameter(this IExecutionBuilder builder, string parameterName, SqlDbType dbType, object value, int size) => builder.WithParameter($"@{parameterName}", dbType.ToDbType(), value, size); }