From 3730045d0d2253d2df02f71f696e919c5d176283 Mon Sep 17 00:00:00 2001 From: Pim Brouwers Date: Wed, 19 Jun 2024 12:44:01 -0400 Subject: [PATCH] upgrade to net8.0 --- .editorconfig | 161 ++++++ Spiffy.sln | 36 ++ src/Spiffy/Batch/DbBatch.cs | 159 +++--- src/Spiffy/Batch/DbFixture.cs | 78 ++- src/Spiffy/Batch/IDbBatch.cs | 30 +- src/Spiffy/Batch/IDbBatchFactory.cs | 15 +- src/Spiffy/Batch/IDbConnectionFactory.cs | 13 +- src/Spiffy/Batch/IDbHandler.cs | 22 +- src/Spiffy/Batch/IDbHandlerAsync.cs | 22 +- src/Spiffy/DbCommandBuilder.cs | 231 ++++---- src/Spiffy/DbException.cs | 247 ++++---- src/Spiffy/DbParams.cs | 518 +++++++++-------- src/Spiffy/Extensions/DbCommandExtensions.cs | 231 ++++---- .../Extensions/DbDataReaderExtensions.cs | 112 ++-- .../Extensions/IDataReaderExtensions.cs | 527 +++++++++--------- src/Spiffy/Extensions/IDbCommandExtensions.cs | 419 +++++++------- .../Extensions/IDbConnectionExtensions.cs | 65 ++- .../Extensions/IDbTransactionExtensions.cs | 35 +- src/Spiffy/Spiffy.csproj | 6 +- test/Spiffy.Tests/AsyncTests.cs | 155 +++--- test/Spiffy.Tests/DbParamTests.cs | 86 +-- test/Spiffy.Tests/Program.cs | 6 - test/Spiffy.Tests/Spiffy.Tests.csproj | 13 +- test/Spiffy.Tests/TestDb.cs | 30 +- test/Spiffy.Tests/Tests.cs | 11 +- 25 files changed, 1673 insertions(+), 1555 deletions(-) create mode 100644 .editorconfig create mode 100644 Spiffy.sln delete mode 100644 test/Spiffy.Tests/Program.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6341be3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,161 @@ +root=true + +[*] +charset=utf-8 +end_of_line=lf +trim_trailing_whitespace=true +insert_final_newline=true +indent_style=space +max_line_length=120 + +[*.{csproj, fsproj, sln}] +indent_size=2 + +[*.{cs, csx, fs, fsx, cshtml}] +indent_size=4 + +[*.ps1] +indent_size = 2 + +[*.json] +indent_size = 2 + +[*.sql] +indent_size = 2 + +[*.html] +indent_size = 2 + +[*.css] +indent_size = 2 + +[*.js] +indent_size = 2 + +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = true +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent + +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +dotnet_naming_rule.private_members_with_underscore.symbols = private_fields +dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore +dotnet_naming_rule.private_members_with_underscore.severity = suggestion +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private +dotnet_naming_style.prefix_underscore.capitalization = camel_case +dotnet_naming_style.prefix_underscore.required_prefix = _ +dotnet_style_operator_placement_when_wrapping = beginning_of_line +end_of_line = crlf + +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +# Namespaces +csharp_style_namespace_declarations = file_scoped:warning + +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_using_directive = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +csharp_using_directive_placement = inside_namespace:warning +csharp_prefer_simple_using_statement = true:warning +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:warning +csharp_style_expression_bodied_lambdas = true:warning +csharp_style_expression_bodied_local_functions = false:silent diff --git a/Spiffy.sln b/Spiffy.sln new file mode 100644 index 0000000..defd775 --- /dev/null +++ b/Spiffy.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E98E7450-AA92-4741-A563-54937E412331}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spiffy", "src\Spiffy\Spiffy.csproj", "{F9048160-807F-4126-8015-D845534514AC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{4539BA53-CE14-44DD-82E6-0B32B5022A0D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spiffy.Tests", "test\Spiffy.Tests\Spiffy.Tests.csproj", "{A97C1767-8253-4679-9DB8-37837A1A5325}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F9048160-807F-4126-8015-D845534514AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F9048160-807F-4126-8015-D845534514AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F9048160-807F-4126-8015-D845534514AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F9048160-807F-4126-8015-D845534514AC}.Release|Any CPU.Build.0 = Release|Any CPU + {A97C1767-8253-4679-9DB8-37837A1A5325}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A97C1767-8253-4679-9DB8-37837A1A5325}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A97C1767-8253-4679-9DB8-37837A1A5325}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A97C1767-8253-4679-9DB8-37837A1A5325}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {F9048160-807F-4126-8015-D845534514AC} = {E98E7450-AA92-4741-A563-54937E412331} + {A97C1767-8253-4679-9DB8-37837A1A5325} = {4539BA53-CE14-44DD-82E6-0B32B5022A0D} + EndGlobalSection +EndGlobal diff --git a/src/Spiffy/Batch/DbBatch.cs b/src/Spiffy/Batch/DbBatch.cs index e017900..7c38070 100644 --- a/src/Spiffy/Batch/DbBatch.cs +++ b/src/Spiffy/Batch/DbBatch.cs @@ -1,30 +1,23 @@ -using Spiffy; +namespace Spiffy; + using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Threading.Tasks; -namespace Spiffy +/// +/// Database unit of work. +/// +/// +/// Constitute a new DbBatch from IDbConnection and IDbTransaction +/// +/// +/// +public class DbBatch(IDbConnection connection, IDbTransaction transaction) : IDbBatch { - /// - /// Database unit of work. - /// - public class DbBatch : IDbBatch - { - private readonly IDbConnection _connection; - private readonly IDbTransaction _transaction; - - /// - /// Constitute a new DbBatch from IDbConnection and IDbTransaction - /// - /// - /// - public DbBatch(IDbConnection connection, IDbTransaction transaction) - { - _transaction = transaction ?? throw new ArgumentNullException(nameof(transaction)); - _connection = connection ?? throw new ArgumentNullException(nameof(connection)); - } + private readonly IDbConnection _connection = connection ?? throw new ArgumentNullException(nameof(connection)); + private readonly IDbTransaction _transaction = transaction ?? throw new ArgumentNullException(nameof(transaction)); /// /// Commit unit of work and cleanup. @@ -32,19 +25,19 @@ public DbBatch(IDbConnection connection, IDbTransaction transaction) /// public void Commit() { - try - { - _transaction.Commit(); - } - catch (Exception ex) - { - _transaction.Rollback(); - throw new FailedCommitBatchException(ex); - } - finally - { - Dispose(); - } + try + { + _transaction.Commit(); + } + catch (Exception ex) + { + _transaction.Rollback(); + throw new FailedCommitBatchException(ex); + } + finally + { + Dispose(); + } } /// @@ -52,18 +45,18 @@ public void Commit() /// public void Rollback() { - try - { - _transaction.Rollback(); - } - catch (Exception) - { - throw; - } - finally - { - Dispose(); - } + try + { + _transaction.Rollback(); + } + catch (Exception) + { + throw; + } + finally + { + Dispose(); + } } /// @@ -72,7 +65,7 @@ public void Rollback() /// /// /// - public void Exec(string sql, DbParams param = null) => + public void Exec(string sql, DbParams? param = null) => Do(sql, param, cmd => cmd.Exec()); /// @@ -90,7 +83,7 @@ public void ExecMany(string sql, IEnumerable paramList) => /// /// /// - public object Scalar(string sql, DbParams param = null) => + public object? Scalar(string sql, DbParams? param = null) => Do(sql, param, cmd => cmd.Scalar()); /// @@ -122,7 +115,7 @@ public IEnumerable Query(string sql, Func map) => /// /// /// - public T QuerySingle(string sql, DbParams param, Func map) => + public T? QuerySingle(string sql, DbParams param, Func map) => Do(sql, param, cmd => cmd.QuerySingle(map)); /// @@ -132,7 +125,7 @@ public T QuerySingle(string sql, DbParams param, Func map) => /// /// /// - public T QuerySingle(string sql, Func map) => + public T? QuerySingle(string sql, Func map) => QuerySingle(sql, new DbParams(), map); /// @@ -160,7 +153,7 @@ public T Read(string sql, Func read) => /// /// /// - public Task ExecAsync(string sql, DbParams param = null) => + public Task ExecAsync(string sql, DbParams? param = null) => DoAsync(sql, param, cmd => cmd.ExecAsync()); /// @@ -179,7 +172,7 @@ public Task ExecManyAsync(string sql, IEnumerable paramList) => /// /// /// - public Task ScalarAsync(string sql, DbParams param = null) => + public Task ScalarAsync(string sql, DbParams? param = null) => DoAsync(sql, param, cmd => cmd.ScalarAsync()); /// @@ -201,7 +194,7 @@ public Task> QueryAsync(string sql, DbParams param, Func /// public Task> QueryAsync(string sql, Func map) => - DoAsync(sql, new DbParams(), cmd => cmd.QueryAsync(map)); + DoAsync(sql, [], cmd => cmd.QueryAsync(map)); /// /// Asynchronously execute paramterized query, read only first record and apply mapping. @@ -211,7 +204,7 @@ public Task> QueryAsync(string sql, Func map) /// /// /// - public Task QuerySingleAsync(string sql, DbParams param, Func map) => + public Task QuerySingleAsync(string sql, DbParams param, Func map) => DoAsync(sql, param, cmd => cmd.QuerySingleAsync(map)); /// @@ -221,8 +214,8 @@ public Task QuerySingleAsync(string sql, DbParams param, Func /// /// - public Task QuerySingleAsync(string sql, Func map) => - DoAsync(sql, new DbParams(), cmd => cmd.QuerySingleAsync(map)); + public Task QuerySingleAsync(string sql, Func map) => + DoAsync(sql, [], cmd => cmd.QuerySingleAsync(map)); /// /// Execute paramterized query and manually cursor IDataReader. @@ -248,45 +241,43 @@ public Task ReadAsync(string sql, Func read) => /// public void Dispose() { - _connection.Close(); - _transaction.Dispose(); + _connection.Close(); + _transaction.Dispose(); } - private void Do(string sql, DbParams param, Action action) => - action(new DbCommandBuilder(_transaction, sql, param ?? new DbParams()).Build()); + private void Do(string sql, DbParams? param, Action action) => + action(new DbCommandBuilder(_transaction, sql, param ?? new DbParams()).Build()); - private T Do(string sql, DbParams param, Func func) => - func(new DbCommandBuilder(_transaction, sql, param ?? new DbParams()).Build()); + private T Do(string sql, DbParams? param, Func func) => + func(new DbCommandBuilder(_transaction, sql, param ?? new DbParams()).Build()); - private async Task DoAsync(string sql, DbParams param, Func func) => - await func(new DbCommandBuilder(_transaction).CommandText(sql).DbParams(param).Build() as DbCommand); + private Task DoAsync(string sql, DbParams? param, Func func) => + func(new DbCommandBuilder(_transaction).CommandText(sql).DbParams(param ?? []).Build().ToDbCommand()); - private async Task DoAsync(string sql, DbParams param, Func> func) => - await func(new DbCommandBuilder(_transaction).CommandText(sql).DbParams(param).Build() as DbCommand); + private Task DoAsync(string sql, DbParams? param, Func> func) => + func(new DbCommandBuilder(_transaction).CommandText(sql).DbParams(param ?? []).Build().ToDbCommand()); private void DoMany(string sql, IEnumerable paramList, Action func) { - var cmd = new DbCommandBuilder(_transaction, sql).Build(); - - foreach (var param in paramList) - { - cmd.Parameters.Clear(); - cmd.SetDbParams(param); - func(cmd); - } - + var cmd = new DbCommandBuilder(_transaction, sql).Build(); + + foreach (var param in paramList) + { + cmd.Parameters.Clear(); + cmd.SetDbParams(param); + func(cmd); + } } - private async Task DoManyAsync(string sql, IEnumerable paramList, Func func) + private async Task DoManyAsync(string sql, IEnumerable paramList, Func func) { - var cmd = new DbCommandBuilder(_transaction, sql).Build() as System.Data.Common.DbCommand; - - foreach (var param in paramList) - { - cmd.Parameters.Clear(); - cmd.SetDbParams(param); - await func(cmd); - } + var cmd = new DbCommandBuilder(_transaction, sql).Build().ToDbCommand(); + + foreach (var param in paramList) + { + cmd.Parameters.Clear(); + cmd.SetDbParams(param); + await func(cmd); + } } - } } diff --git a/src/Spiffy/Batch/DbFixture.cs b/src/Spiffy/Batch/DbFixture.cs index 405a93d..f5b9aaa 100644 --- a/src/Spiffy/Batch/DbFixture.cs +++ b/src/Spiffy/Batch/DbFixture.cs @@ -1,19 +1,18 @@ -using Spiffy; +namespace Spiffy; + using System; using System.Collections.Generic; using System.Data; using System.Threading.Tasks; -namespace Spiffy +/// +/// Represents the ability to obtain new unit instances to perform +/// database-bound tasks transactionally, as well as query +/// the database directly. +/// +/// The IDbConnectionFactory to use for connection creation. +public class DbFixture : IDbBatchFactory, IDbConnectionFactory, IDbHandler, IDbHandlerAsync where TConn : IDbConnectionFactory { - /// - /// Represents the ability to obtain new unit instances to perform - /// database-bound tasks transactionally, as well as query - /// the database directly. - /// - /// The IDbConnectionFactory to use for connection creation. - public class DbFixture : IDbBatchFactory, IDbConnectionFactory, IDbHandler, IDbHandlerAsync where TConn : IDbConnectionFactory - { private readonly TConn _connectionFactory; /// @@ -22,10 +21,10 @@ public class DbFixture : IDbBatchFactory, IDbConnectionFactory, IDbHandle /// public DbFixture(TConn connectionFactory) { - if (connectionFactory == null) - throw new ArgumentNullException(nameof(connectionFactory)); + if (connectionFactory == null) + throw new ArgumentNullException(nameof(connectionFactory)); - _connectionFactory = connectionFactory; + _connectionFactory = connectionFactory; } /// @@ -41,10 +40,10 @@ public IDbConnection NewConnection() => /// public IDbBatch NewBatch() { - var conn = _connectionFactory.NewConnection(); - conn.TryOpenConnection(); - var tran = conn.TryBeginTransaction(); - return new DbBatch(conn, tran); + var conn = _connectionFactory.NewConnection(); + conn.TryOpenConnection(); + var tran = conn.TryBeginTransaction(); + return new DbBatch(conn, tran); } /// @@ -55,10 +54,10 @@ public IDbBatch NewBatch() /// public T Batch(Func fn) { - var batch = NewBatch(); - var result = fn(batch); - batch.Commit(); - return result; + var batch = NewBatch(); + var result = fn(batch); + batch.Commit(); + return result; } /// @@ -68,9 +67,9 @@ public T Batch(Func fn) /// public void Batch(Action fn) { - var batch = NewBatch(); - fn(batch); - batch.Commit(); + var batch = NewBatch(); + fn(batch); + batch.Commit(); } /// @@ -81,10 +80,10 @@ public void Batch(Action fn) /// public async Task BatchAsync(Func> fn) { - var batch = NewBatch(); - var result = await fn(batch); - batch.Commit(); - return result; + var batch = NewBatch(); + var result = await fn(batch); + batch.Commit(); + return result; } /// @@ -94,9 +93,9 @@ public async Task BatchAsync(Func> fn) /// public async Task BatchAsync(Func fn) { - var batch = NewBatch(); - await fn(batch); - batch.Commit(); + var batch = NewBatch(); + await fn(batch); + batch.Commit(); } /// @@ -105,7 +104,7 @@ public async Task BatchAsync(Func fn) /// /// /// - public void Exec(string sql, DbParams param = null) => + public void Exec(string sql, DbParams? param = null) => Batch(b => b.Exec(sql, param)); /// @@ -123,7 +122,7 @@ public void ExecMany(string sql, IEnumerable param) => /// /// /// - public object Scalar(string sql, DbParams param = null) => + public object? Scalar(string sql, DbParams? param = null) => Batch(b => b.Scalar(sql, param)); /// @@ -155,7 +154,7 @@ public IEnumerable Query(string sql, Func map) => /// /// /// - public T QuerySingle(string sql, DbParams param, Func map) => + public T? QuerySingle(string sql, DbParams param, Func map) => Batch(b => b.QuerySingle(sql, param, map)); /// @@ -165,7 +164,7 @@ public T QuerySingle(string sql, DbParams param, Func map) => /// /// /// - public T QuerySingle(string sql, Func map) => + public T? QuerySingle(string sql, Func map) => Batch(b => b.QuerySingle(sql, map)); /// @@ -193,7 +192,7 @@ public T Read(string sql, Func read) => /// /// /// - public Task ExecAsync(string sql, DbParams param = null) => + public Task ExecAsync(string sql, DbParams? param = null) => BatchAsync(b => b.ExecAsync(sql, param)); /// @@ -211,7 +210,7 @@ public Task ExecManyAsync(string sql, IEnumerable paramList) => /// /// /// - public Task ScalarAsync(string sql, DbParams param = null) => + public Task ScalarAsync(string sql, DbParams? param = null) => BatchAsync(b => b.ScalarAsync(sql, param)); /// @@ -243,7 +242,7 @@ public Task> QueryAsync(string sql, Func map) /// /// /// - public Task QuerySingleAsync(string sql, DbParams param, Func map) => + public Task QuerySingleAsync(string sql, DbParams param, Func map) => BatchAsync(b => b.QuerySingleAsync(sql, param, map)); /// @@ -253,7 +252,7 @@ public Task QuerySingleAsync(string sql, DbParams param, Func /// /// - public Task QuerySingleAsync(string sql, Func map) => + public Task QuerySingleAsync(string sql, Func map) => BatchAsync(b => b.QuerySingleAsync(sql, map)); /// @@ -274,5 +273,4 @@ public Task ReadAsync(string sql, DbParams param, Func rea /// public Task ReadAsync(string sql, Func read) => BatchAsync(b => b.ReadAsync(sql, read)); - } } diff --git a/src/Spiffy/Batch/IDbBatch.cs b/src/Spiffy/Batch/IDbBatch.cs index 4a4d722..6e1b95d 100644 --- a/src/Spiffy/Batch/IDbBatch.cs +++ b/src/Spiffy/Batch/IDbBatch.cs @@ -1,15 +1,14 @@ -using Spiffy; +namespace Spiffy; + using System; using System.Data; using System.Threading.Tasks; -namespace Spiffy +/// +/// Database unit of work. +/// +public interface IDbBatch : IDbHandler, IDbHandlerAsync, IDisposable { - /// - /// Database unit of work. - /// - public interface IDbBatch : IDbHandler, IDbHandlerAsync, IDisposable - { /// /// Commit unit of work. /// @@ -19,21 +18,4 @@ public interface IDbBatch : IDbHandler, IDbHandlerAsync, IDisposable /// Rollback unit of work. /// void Rollback(); - - // /// - // /// Execute paramterized query and manually cursor IDataReader. - // /// - // /// - // /// - // /// - // IDataReader Read(string sql, DbParams param = null); - - // /// - // /// Asynchronously execute paramterized query and manually cursor IDataReader. - // /// - // /// - // /// - // /// - // Task ReadAsync(string sql, DbParams param = null); - } } diff --git a/src/Spiffy/Batch/IDbBatchFactory.cs b/src/Spiffy/Batch/IDbBatchFactory.cs index a7f64f4..4eb49c2 100644 --- a/src/Spiffy/Batch/IDbBatchFactory.cs +++ b/src/Spiffy/Batch/IDbBatchFactory.cs @@ -1,13 +1,13 @@ +namespace Spiffy; + using System; using System.Threading.Tasks; -namespace Spiffy +/// +/// Represents the ability to create IDbBatch. +/// +public interface IDbBatchFactory { - /// - /// Represents the ability to create IDbBatch. - /// - public interface IDbBatchFactory - { /// /// Create a new IDbBatch, which represents a database unit of work. /// @@ -44,5 +44,4 @@ public interface IDbBatchFactory /// Task BatchAsync(Func fn); - } -} \ No newline at end of file +} diff --git a/src/Spiffy/Batch/IDbConnectionFactory.cs b/src/Spiffy/Batch/IDbConnectionFactory.cs index d2f0751..f4c3104 100644 --- a/src/Spiffy/Batch/IDbConnectionFactory.cs +++ b/src/Spiffy/Batch/IDbConnectionFactory.cs @@ -1,16 +1,15 @@ +namespace Spiffy; + using System.Data; -namespace Spiffy +/// +/// Represents the ability to create new IDbConnection instances. +/// +public interface IDbConnectionFactory { - /// - /// Represents the ability to create new IDbConnection instances. - /// - public interface IDbConnectionFactory - { /// /// Create a new instance of an IDbConnection implementation. /// /// IDbConnection NewConnection(); - } } diff --git a/src/Spiffy/Batch/IDbHandler.cs b/src/Spiffy/Batch/IDbHandler.cs index 94032d1..6c5e35e 100644 --- a/src/Spiffy/Batch/IDbHandler.cs +++ b/src/Spiffy/Batch/IDbHandler.cs @@ -1,22 +1,21 @@ -using Spiffy; +namespace Spiffy; + using System; using System.Collections.Generic; using System.Data; -namespace Spiffy +/// +/// Represents the ability to do work against a data source. +/// +public interface IDbHandler { - /// - /// Represents the ability to do work against a data source. - /// - public interface IDbHandler - { /// /// Execute parameterized query. /// /// /// /// - void Exec(string sql, DbParams param = null); + void Exec(string sql, DbParams? param = null); /// /// Execute parameterized query multiple times @@ -32,7 +31,7 @@ public interface IDbHandler /// /// /// - object Scalar(string sql, DbParams param = null); + object? Scalar(string sql, DbParams? param = null); /// /// Execute parameterized query, enumerate all records and apply mapping. @@ -61,7 +60,7 @@ public interface IDbHandler /// /// /// - T QuerySingle(string sql, DbParams param, Func map); + T? QuerySingle(string sql, DbParams param, Func map); /// /// Execute query, read only first record and apply mapping. @@ -70,7 +69,7 @@ public interface IDbHandler /// /// /// - T QuerySingle(string sql, Func map); + T? QuerySingle(string sql, Func map); /// /// Execute paramterized query and manually cursor IDataReader. @@ -88,5 +87,4 @@ public interface IDbHandler /// /// T Read(string sql, Func read); - } } diff --git a/src/Spiffy/Batch/IDbHandlerAsync.cs b/src/Spiffy/Batch/IDbHandlerAsync.cs index 3b3efb7..5be4a3e 100644 --- a/src/Spiffy/Batch/IDbHandlerAsync.cs +++ b/src/Spiffy/Batch/IDbHandlerAsync.cs @@ -1,23 +1,22 @@ -using Spiffy; +namespace Spiffy; + using System; using System.Collections.Generic; using System.Data; using System.Threading.Tasks; -namespace Spiffy +/// +/// Represents the ability to do work asynchronously against a data source. +/// +public interface IDbHandlerAsync { - /// - /// Represents the ability to do work asynchronously against a data source. - /// - public interface IDbHandlerAsync - { /// /// Asynchronously execute parameterized query. /// /// /// /// - Task ExecAsync(string sql, DbParams param = null); + Task ExecAsync(string sql, DbParams? param = null); /// /// Asynchronously execute parameterized query multiple times @@ -33,7 +32,7 @@ public interface IDbHandlerAsync /// /// /// - Task ScalarAsync(string sql, DbParams param = null); + Task ScalarAsync(string sql, DbParams? param = null); /// /// Asynchronously execute parameterized query, enumerate all records and apply mapping. @@ -62,7 +61,7 @@ public interface IDbHandlerAsync /// /// /// - Task QuerySingleAsync(string sql, DbParams param, Func map); + Task QuerySingleAsync(string sql, DbParams param, Func map); /// /// Asynchronously execute query, read only first record and apply mapping. @@ -71,7 +70,7 @@ public interface IDbHandlerAsync /// /// /// - Task QuerySingleAsync(string sql, Func map); + Task QuerySingleAsync(string sql, Func map); /// /// Execute paramterized query and manually cursor IDataReader. @@ -89,5 +88,4 @@ public interface IDbHandlerAsync /// /// Task ReadAsync(string sql, Func read); - } } diff --git a/src/Spiffy/DbCommandBuilder.cs b/src/Spiffy/DbCommandBuilder.cs index b9e6168..787899c 100644 --- a/src/Spiffy/DbCommandBuilder.cs +++ b/src/Spiffy/DbCommandBuilder.cs @@ -1,138 +1,131 @@ -using System; +namespace Spiffy; + +using System; using System.Data; -namespace Spiffy +/// +/// A fluent API for generating IDbCommand instances +/// +/// +/// Create an empty DbCommandBuilder +/// +/// +public sealed class DbCommandBuilder(IDbConnection conn) { + private readonly IDbConnection _conn = conn ?? throw new ArgumentNullException(nameof(conn)); + private string? _commandText; + private int? _commandTimeout; + private CommandType _commandType = System.Data.CommandType.Text; + private DbParams _dbParams = []; + private IDbTransaction? _transaction; + /// - /// A fluent API for generating IDbCommand instances + /// Create an initialized DbCommandBuilder /// - public class DbCommandBuilder + /// + /// + /// + public DbCommandBuilder(IDbConnection conn, string commandText, DbParams? param = null) : this(conn) { - private readonly IDbConnection _conn; - private string _commandText; - private int? _commandTimeout; - private CommandType _commandType; - private DbParams _dbParams; - private IDbTransaction _transaction; - - /// - /// Create an empty DbCommandBuilder - /// - /// - public DbCommandBuilder(IDbConnection conn) - { - _conn = conn ?? throw new ArgumentNullException(nameof(conn)); - _commandType = System.Data.CommandType.Text; - } + CommandText(commandText); + DbParams(param); + } - /// - /// Create an initialized DbCommandBuilder - /// - /// - /// - /// - public DbCommandBuilder(IDbConnection conn, string commandText, DbParams param = null) : this(conn) - { - CommandText(commandText); - DbParams(param); - } + /// + /// Create an empty DbCommandBuilder from IDbTransaction + /// + /// + public DbCommandBuilder(IDbTransaction tran) : this(tran.Connection!) + { + Transaction(tran); + } - /// - /// Create an empty DbCommandBuilder from IDbTransaction - /// - /// - public DbCommandBuilder(IDbTransaction tran) : this(tran.Connection) - { - Transaction(tran); - } + /// + /// Create an initialized DbCommandBuilder + /// + /// + /// + /// + public DbCommandBuilder(IDbTransaction tran, string commandText, DbParams? param = null) : this(tran) + { + CommandText(commandText); + DbParams(param); + } - /// - /// Create an initialized DbCommandBuilder - /// - /// - /// - /// - public DbCommandBuilder(IDbTransaction tran, string commandText, DbParams param = null) : this(tran) - { - CommandText(commandText); - DbParams(param); - } + /// + /// Generate IDbCommand + /// + /// + public IDbCommand Build() + { + var command = _conn.CreateCommand(); + command.CommandType = _commandType; + command.CommandText = _commandText; - /// - /// Generate IDbCommand - /// - /// - public IDbCommand Build() - { - var command = _conn.CreateCommand(); - command.CommandType = _commandType; - command.CommandText = _commandText; - - if (_commandTimeout.HasValue) - command.CommandTimeout = _commandTimeout.Value; + if (_commandTimeout.HasValue) + command.CommandTimeout = _commandTimeout.Value; - if (_dbParams != null) - command.SetDbParams(_dbParams); + if (_dbParams != null) + command.SetDbParams(_dbParams); - if (_transaction != null) - command.Transaction = _transaction; + if (_transaction != null) + command.Transaction = _transaction; - return command; - } + return command; + } - /// - /// Set statement text - /// - /// - /// - public DbCommandBuilder CommandText(string query) - { - _commandText = query; - return this; - } + /// + /// Set statement text + /// + /// + /// + public DbCommandBuilder CommandText(string query) + { + _commandText = query; + return this; + } - /// - /// Set command type (default: CommandType.Text) - /// - /// - /// - public DbCommandBuilder CommandType(CommandType commandType) - { - _commandType = commandType; - return this; - } + /// + /// Set command type (default: CommandType.Text) + /// + /// + /// + public DbCommandBuilder CommandType(CommandType commandType) + { + _commandType = commandType; + return this; + } - /// - /// Set command timeout - /// - /// - /// - public DbCommandBuilder Timeout(int commandTimeout) - { - _commandTimeout = commandTimeout; - return this; - } + /// + /// Set command timeout + /// + /// + /// + public DbCommandBuilder Timeout(int commandTimeout) + { + _commandTimeout = commandTimeout; + return this; + } - /// - /// Add DbParams - /// - /// - /// - public DbCommandBuilder DbParams(DbParams dbParams) - { - _dbParams = dbParams ?? new DbParams(); - return this; - } + /// + /// Add DbParams + /// + /// + /// + public DbCommandBuilder DbParams(DbParams? dbParams) + { + _dbParams = dbParams ?? []; + return this; + } - /// - /// Set transaction - /// - /// - /// - public DbCommandBuilder Transaction(IDbTransaction transaction) - { - _transaction = transaction; - return this; - } + /// + /// Set transaction + /// + /// + /// + public DbCommandBuilder Transaction(IDbTransaction transaction) + { + _transaction = transaction; + return this; } -} \ No newline at end of file +} diff --git a/src/Spiffy/DbException.cs b/src/Spiffy/DbException.cs index c3a89b8..4b18072 100644 --- a/src/Spiffy/DbException.cs +++ b/src/Spiffy/DbException.cs @@ -1,159 +1,142 @@ -using System; +namespace Spiffy; -namespace Spiffy +using System; + +/// +/// Errors thrown during database interaction. +/// +public enum DbErrorCode { /// - /// Errors thrown during database interaction. + /// Could Not Open Connection /// - public enum DbErrorCode - { - /// - /// Could Not Open Connection - /// - CouldNotOpenConnection = 1000, - /// - /// Could Not Close Connection - /// - CouldNotCloseConnection = 1001, - /// - /// Connection Busy - /// - ConnectionBusy = 1002, - - /// - /// Could Not Begin Transaction - /// - CouldNotBeginTransaction = 2000, - - /// - /// Could Not Begin Batch - /// - CouldNotBeginBatch = 3000, - /// - /// Could Not Commit Batch - /// - CouldNotCommitBatch = 3001, - - /// - /// Could Not Execute Non Query - /// - CouldNotExecuteNonQuery = 4000, - /// - /// Could Not Execute Scalar - /// - CouldNotExecuteScalar = 4001, - /// - /// Could Not Execute Reader - /// - CouldNotExecuteReader = 4002, - - /// - /// Could Not Cast Value - /// - CouldNotCastValue = 5000, - } - + CouldNotOpenConnection = 1000, /// - /// Represents errors that occur during database interactions. + /// Could Not Close Connection /// - public class DbException : Exception - { - /// - /// The error DbErrorCode - /// - public readonly DbErrorCode ErrorCode; - - /// - /// Initializes a new instance of the DbException class - /// - /// - /// - /// - public DbException(DbErrorCode errorCode, string message, Exception innerEx = null) - : base(message, innerEx) - { - ErrorCode = errorCode; - } - } + CouldNotCloseConnection = 1001, + /// + /// Connection Busy + /// + ConnectionBusy = 1002, /// - /// Represents a failure to open a database connection + /// Could Not Begin Transaction /// - public class CouldNotOpenConnectionException : DbException - { - /// - /// Initializes a new instance of the CouldNotOpenConnectionException class - /// - /// - public CouldNotOpenConnectionException(Exception ex) - : base(DbErrorCode.CouldNotOpenConnection, $"Could not establish database connection.", ex) { } - } + CouldNotBeginTransaction = 2000, /// - /// Represents a failure due to a busy database connection + /// Could Not Begin Batch /// - public class ConnectionBusyException : DbException - { - /// - /// Initializes a new instance of the ConnectionBusyException class - /// - public ConnectionBusyException() - : base(DbErrorCode.ConnectionBusy, $"The connection is not currently available to open.") - { - } - } + CouldNotBeginBatch = 3000, + /// + /// Could Not Commit Batch + /// + CouldNotCommitBatch = 3001, /// - /// Represents a failure to begin a new database transaction + /// Could Not Execute Non Query /// - public class FailedTransacitonException : DbException - { - /// - /// Initializes a new instance of the FailedTransacitonException class - /// - /// - public FailedTransacitonException(Exception ex) - : base(DbErrorCode.CouldNotBeginTransaction, $"Could not begin transaction.", ex) { } - } + CouldNotExecuteNonQuery = 4000, + /// + /// Could Not Execute Scalar + /// + CouldNotExecuteScalar = 4001, + /// + /// Could Not Execute Reader + /// + CouldNotExecuteReader = 4002, /// - /// Represents a failure to begin a database batch + /// Could Not Cast Value /// - public class FailedBeginBatchException : DbException - { - /// - /// Initializes a new instance of the FailedBeginBatchException class - /// - /// - public FailedBeginBatchException(Exception ex) - : base(DbErrorCode.CouldNotBeginBatch, $"Could not begin batch.", ex) { } - } + CouldNotCastValue = 5000, +} +/// +/// Represents errors that occur during database interactions. +/// +/// +/// Initializes a new instance of the DbException class +/// +/// +/// +/// +public class DbException(DbErrorCode errorCode, string message, Exception? innerEx = null) : Exception(message, innerEx) +{ /// - /// Represents a failure to commit a database transaction + /// The error DbErrorCode /// - public class FailedCommitBatchException : DbException - { - /// - /// Initializes a new instance of the FailedCommitBatchException class - /// - /// - public FailedCommitBatchException(Exception ex) - : base(DbErrorCode.CouldNotCommitBatch, $"Could not commit batch.", ex) { } - } + public readonly DbErrorCode ErrorCode = errorCode; +} + +/// +/// Represents a failure to open a database connection +/// +/// +/// Initializes a new instance of the CouldNotOpenConnectionException class +/// +/// +public class CouldNotOpenConnectionException(Exception ex) : DbException(DbErrorCode.CouldNotOpenConnection, $"Could not establish database connection.", ex) +{ +} +/// +/// Represents a failure due to a busy database connection +/// +public class ConnectionBusyException : DbException +{ /// - /// Represents a failure to execute a database command. + /// Initializes a new instance of the ConnectionBusyException class /// - public class FailedExecutionException : DbException + public ConnectionBusyException() + : base(DbErrorCode.ConnectionBusy, $"The connection is not currently available to open.") { - /// - /// Initializes a new instance of the FailedExecutionException class. - /// - /// - /// - /// /// - public FailedExecutionException(DbErrorCode errorCode, string sql, Exception ex) - : base(errorCode, sql, ex) { } } } + +/// +/// Represents a failure to begin a new database transaction +/// +/// +/// Initializes a new instance of the FailedTransacitonException class +/// +/// +public class FailedTransacitonException(Exception ex) : DbException(DbErrorCode.CouldNotBeginTransaction, $"Could not begin transaction.", ex) +{ +} + +/// +/// Represents a failure to begin a database batch +/// +/// +/// Initializes a new instance of the FailedBeginBatchException class +/// +/// +public class FailedBeginBatchException(Exception ex) : DbException(DbErrorCode.CouldNotBeginBatch, $"Could not begin batch.", ex) +{ +} + +/// +/// Represents a failure to commit a database transaction +/// +/// +/// Initializes a new instance of the FailedCommitBatchException class +/// +/// +public class FailedCommitBatchException(Exception ex) : DbException(DbErrorCode.CouldNotCommitBatch, $"Could not commit batch.", ex) +{ +} + +/// +/// Represents a failure to execute a database command. +/// +/// +/// Initializes a new instance of the FailedExecutionException class. +/// +/// +/// +/// /// +public class FailedExecutionException(DbErrorCode errorCode, string sql, Exception ex) : DbException(errorCode, sql, ex) +{ +} diff --git a/src/Spiffy/DbParams.cs b/src/Spiffy/DbParams.cs index b01e230..e3990b2 100644 --- a/src/Spiffy/DbParams.cs +++ b/src/Spiffy/DbParams.cs @@ -1,288 +1,280 @@ -using System; +namespace Spiffy; + +using System; using System.Collections.Generic; using System.Data; using System.Linq; -namespace Spiffy +/// +/// A container for database bound parameters. +/// +public sealed class DbParams : Dictionary { /// - /// A container for database bound parameters. + /// Initialize a new instance of the DbParams class /// - public sealed class DbParams : Dictionary + public DbParams() { - /// - /// Initialize a new instance of the DbParams class - /// - public DbParams() - { - } + } - /// - /// Initialize a new instance of the DbParams class from a key and value - /// - public DbParams(string key, object value) + /// + /// Initialize a new instance of the DbParams class from a key and value + /// + public DbParams(string key, object value) + { + if (!this.ContainsKey(key)) { - if (!this.ContainsKey(key)) - { - this[key] = value; - } + this[key] = value; } } +} + +/// +/// Extensions for DbParams +/// +public static class DbParamsExtensions +{ /// - /// Extensions for DbParams + /// Returns a new dictionary of this, others merged leftward. /// - public static class DbParamsExtensions + public static DbParams Add(this DbParams p1, DbParams p2) { - - /// - /// Returns a new dictionary of this, others merged leftward. - /// - public static DbParams Add(this DbParams p1, DbParams p2) - { - p2.ToList() - .ForEach(x => + p2.ToList() + .ForEach(x => + { + if (!p1.ContainsKey(x.Key)) { - if (!p1.ContainsKey(x.Key)) - { - p1.Add(x.Key, x.Value); - } - }); + p1.Add(x.Key, x.Value); + } + }); - return p1; - } + return p1; } +} +/// +/// A container for database bound parameters with DbType +/// +/// +/// Initialize a new instance of the DbTypeParam class +/// +/// +/// +public sealed class DbTypeParam(DbType dbType, object value) +{ /// - /// A container for database bound parameters with DbType + /// The DbType of the parameter /// - public sealed class DbTypeParam - { - /// - /// The DbType of the parameter - /// - public DbType DbType { get; } - - /// - /// The value of the parameter - /// - public object Value { get; } - - /// - /// Initialize a new instance of the DbTypeParam class - /// - /// - /// - public DbTypeParam(DbType dbType, object value) - { - DbType = dbType; - Value = value; - } + public DbType DbType { get; } = dbType; - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.AnsiString - /// - public static DbTypeParam AnsiString(string v) => new DbTypeParam(DbType.AnsiString, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Binary - /// - public static DbTypeParam Binary(byte[] v) => new DbTypeParam(DbType.Binary, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Binary - /// - public static DbTypeParam Bytes(byte[] v) => Binary(v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Byte - /// - public static DbTypeParam Byte(byte v) => new DbTypeParam(DbType.Byte, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Boolean - /// - public static DbTypeParam Boolean(bool v) => new DbTypeParam(DbType.Boolean, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Currency - /// - public static DbTypeParam Currency(decimal v) => new DbTypeParam(DbType.Currency, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Date - /// - public static DbTypeParam Date(DateTime v) => new DbTypeParam(DbType.Date, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.DateTime - /// - public static DbTypeParam DateTime(DateTime v) => new DbTypeParam(DbType.DateTime, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Decimal - /// - public static DbTypeParam Decimal(decimal v) => new DbTypeParam(DbType.Decimal, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Double - /// - public static DbTypeParam Double(double v) => new DbTypeParam(DbType.Double, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Guid - /// - public static DbTypeParam Guid(Guid v) => new DbTypeParam(DbType.Guid, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Int16 - /// - public static DbTypeParam Int16(short v) => new DbTypeParam(DbType.Int16, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Int32 - /// - public static DbTypeParam Int32(int v) => new DbTypeParam(DbType.Int32, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Int64 - /// - public static DbTypeParam Int64(long v) => new DbTypeParam(DbType.Int64, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Object - /// - public static DbTypeParam Object(object v) => new DbTypeParam(DbType.Object, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Single - /// - public static DbTypeParam Float(float v) => new DbTypeParam(DbType.Single, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.String - /// - public static DbTypeParam String(string v) => new DbTypeParam(DbType.String, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.UInt16 - /// - public static DbTypeParam UInt16(ushort v) => new DbTypeParam(DbType.UInt16, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.UInt32 - /// - public static DbTypeParam UInt32(uint v) => new DbTypeParam(DbType.UInt32, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.UInt64 - /// - public static DbTypeParam UInt64(ulong v) => new DbTypeParam(DbType.UInt64, v); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.AnsiString - /// - public static DbTypeParam NullAnsiString => new DbTypeParam(DbType.AnsiString, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Binary - /// - public static DbTypeParam NullBinary => new DbTypeParam(DbType.Binary, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Binary - /// - public static DbTypeParam NullBytes => NullBinary; - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Byte - /// - public static DbTypeParam NullByte => new DbTypeParam(DbType.Byte, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Boolean - /// - public static DbTypeParam NullBoolean => new DbTypeParam(DbType.Boolean, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Currency - /// - public static DbTypeParam NullCurrency => new DbTypeParam(DbType.Currency, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Date - /// - public static DbTypeParam NullDate => new DbTypeParam(DbType.Date, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.DateTime - /// - public static DbTypeParam NullDateTime => new DbTypeParam(DbType.DateTime, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Decimal - /// - public static DbTypeParam NullDecimal => new DbTypeParam(DbType.Decimal, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Double - /// - public static DbTypeParam NullDouble => new DbTypeParam(DbType.Double, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Guid - /// - public static DbTypeParam NullGuid => new DbTypeParam(DbType.Guid, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Int16 - /// - public static DbTypeParam NullInt16 => new DbTypeParam(DbType.Int16, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Int32 - /// - public static DbTypeParam NullInt32 => new DbTypeParam(DbType.Int32, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Int64 - /// - public static DbTypeParam NullInt64 => new DbTypeParam(DbType.Int64, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Object - /// - public static DbTypeParam NullObject => new DbTypeParam(DbType.Object, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Single - /// - public static DbTypeParam NullFloat => new DbTypeParam(DbType.Single, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.String - /// - public static DbTypeParam NullString => new DbTypeParam(DbType.String, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.Time - /// - public static DbTypeParam NullTime => new DbTypeParam(DbType.Time, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.UInt16 - /// - public static DbTypeParam NullUInt16 => new DbTypeParam(DbType.UInt16, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.UInt32 - /// - public static DbTypeParam NullUInt32 => new DbTypeParam(DbType.UInt32, DBNull.Value); - - /// - /// Initialize a new instance of the DbTypeParam with a null value DbType.UInt64 - /// - public static DbTypeParam NullUInt64 => new DbTypeParam(DbType.UInt64, DBNull.Value); - } + /// + /// The value of the parameter + /// + public object Value { get; } = value; + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.AnsiString + /// + public static DbTypeParam AnsiString(string v) => new DbTypeParam(DbType.AnsiString, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Binary + /// + public static DbTypeParam Binary(byte[] v) => new DbTypeParam(DbType.Binary, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Binary + /// + public static DbTypeParam Bytes(byte[] v) => Binary(v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Byte + /// + public static DbTypeParam Byte(byte v) => new DbTypeParam(DbType.Byte, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Boolean + /// + public static DbTypeParam Boolean(bool v) => new DbTypeParam(DbType.Boolean, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Currency + /// + public static DbTypeParam Currency(decimal v) => new DbTypeParam(DbType.Currency, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Date + /// + public static DbTypeParam Date(DateTime v) => new DbTypeParam(DbType.Date, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.DateTime + /// + public static DbTypeParam DateTime(DateTime v) => new DbTypeParam(DbType.DateTime, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Decimal + /// + public static DbTypeParam Decimal(decimal v) => new DbTypeParam(DbType.Decimal, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Double + /// + public static DbTypeParam Double(double v) => new DbTypeParam(DbType.Double, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Guid + /// + public static DbTypeParam Guid(Guid v) => new DbTypeParam(DbType.Guid, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Int16 + /// + public static DbTypeParam Int16(short v) => new DbTypeParam(DbType.Int16, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Int32 + /// + public static DbTypeParam Int32(int v) => new DbTypeParam(DbType.Int32, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Int64 + /// + public static DbTypeParam Int64(long v) => new DbTypeParam(DbType.Int64, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Object + /// + public static DbTypeParam Object(object v) => new DbTypeParam(DbType.Object, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Single + /// + public static DbTypeParam Float(float v) => new DbTypeParam(DbType.Single, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.String + /// + public static DbTypeParam String(string v) => new DbTypeParam(DbType.String, v); + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.UInt16 + /// + public static DbTypeParam UInt16(ushort v) => new DbTypeParam(DbType.UInt16, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.UInt32 + /// + public static DbTypeParam UInt32(uint v) => new DbTypeParam(DbType.UInt32, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.UInt64 + /// + public static DbTypeParam UInt64(ulong v) => new DbTypeParam(DbType.UInt64, v); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.AnsiString + /// + public static DbTypeParam NullAnsiString => new DbTypeParam(DbType.AnsiString, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Binary + /// + public static DbTypeParam NullBinary => new DbTypeParam(DbType.Binary, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Binary + /// + public static DbTypeParam NullBytes => NullBinary; + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Byte + /// + public static DbTypeParam NullByte => new DbTypeParam(DbType.Byte, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Boolean + /// + public static DbTypeParam NullBoolean => new DbTypeParam(DbType.Boolean, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Currency + /// + public static DbTypeParam NullCurrency => new DbTypeParam(DbType.Currency, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Date + /// + public static DbTypeParam NullDate => new DbTypeParam(DbType.Date, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.DateTime + /// + public static DbTypeParam NullDateTime => new DbTypeParam(DbType.DateTime, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Decimal + /// + public static DbTypeParam NullDecimal => new DbTypeParam(DbType.Decimal, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Double + /// + public static DbTypeParam NullDouble => new DbTypeParam(DbType.Double, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Guid + /// + public static DbTypeParam NullGuid => new DbTypeParam(DbType.Guid, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Int16 + /// + public static DbTypeParam NullInt16 => new DbTypeParam(DbType.Int16, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Int32 + /// + public static DbTypeParam NullInt32 => new DbTypeParam(DbType.Int32, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Int64 + /// + public static DbTypeParam NullInt64 => new DbTypeParam(DbType.Int64, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Object + /// + public static DbTypeParam NullObject => new DbTypeParam(DbType.Object, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Single + /// + public static DbTypeParam NullFloat => new DbTypeParam(DbType.Single, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.String + /// + public static DbTypeParam NullString => new DbTypeParam(DbType.String, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.Time + /// + public static DbTypeParam NullTime => new DbTypeParam(DbType.Time, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.UInt16 + /// + public static DbTypeParam NullUInt16 => new DbTypeParam(DbType.UInt16, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.UInt32 + /// + public static DbTypeParam NullUInt32 => new DbTypeParam(DbType.UInt32, DBNull.Value); + + /// + /// Initialize a new instance of the DbTypeParam with a null value DbType.UInt64 + /// + public static DbTypeParam NullUInt64 => new DbTypeParam(DbType.UInt64, DBNull.Value); } diff --git a/src/Spiffy/Extensions/DbCommandExtensions.cs b/src/Spiffy/Extensions/DbCommandExtensions.cs index b554dcd..55f195f 100644 --- a/src/Spiffy/Extensions/DbCommandExtensions.cs +++ b/src/Spiffy/Extensions/DbCommandExtensions.cs @@ -1,149 +1,142 @@ -using System; +namespace Spiffy; + +using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Threading; using System.Threading.Tasks; -namespace Spiffy +/// +/// DbCommand extension methods for async workloads +/// +public static class DbCommandExtensions { /// - /// DbCommand extension methods for async workloads + /// Asynchronously execute parameterized query with no results /// - public static class DbCommandExtensions - { - /// - /// Asynchronously execute parameterized query with no results - /// - /// - /// - public async static Task ExecAsync(this DbCommand dbCommand) => - await dbCommand.DoVoidAsync(async cmd => await cmd.ExecuteNonQueryAsync()); + /// + /// + public async static Task ExecAsync(this DbCommand dbCommand) => + await dbCommand.DoVoidAsync(async cmd => await cmd.ExecuteNonQueryAsync()); - /// - /// Asynchronously execute parameterized query many times with no results - /// - /// - /// - /// - public async static Task ExecManyAsync(this DbCommand dbCommand, IEnumerable paramList) => - await dbCommand.DoManyAsync(paramList, async cmd => await cmd.ExecuteNonQueryAsync()); + /// + /// Asynchronously execute parameterized query many times with no results + /// + /// + /// + /// + public async static Task ExecManyAsync(this DbCommand dbCommand, IEnumerable paramList) => + await dbCommand.DoManyAsync(paramList, async cmd => await cmd.ExecuteNonQueryAsync()); - /// - /// Asynchronously execute parameterized query and return single object value. - /// - /// - /// - public static async Task ScalarAsync(this DbCommand dbCommand) => - await dbCommand.DoAsync(async cmd => await cmd.ExecuteScalarAsync()); + /// + /// Asynchronously execute parameterized query and return single object value. + /// + /// + /// + public static async Task ScalarAsync(this DbCommand dbCommand) => + await dbCommand.DoAsync(async cmd => await cmd.ExecuteScalarAsync()); - /// - /// Asynchronously execute parameterized query, enumerate all records and apply mapping. - /// - /// - /// - /// - /// - /// - public async static Task> QueryAsync(this DbCommand dbCommand, Func map, CommandBehavior commandBehavior = CommandBehavior.SequentialAccess) => - await dbCommand.DoAsync(async cmd => - { - using (var rd = await dbCommand.ExecuteReaderAsync(commandBehavior)) - { - return await rd.MapAsync(map); - } - }); + /// + /// Asynchronously execute parameterized query, enumerate all records and apply mapping. + /// + /// + /// + /// + /// + /// + public async static Task> QueryAsync(this DbCommand dbCommand, Func map, CommandBehavior commandBehavior = CommandBehavior.SequentialAccess) => + await dbCommand.DoAsync(async cmd => + { + using var rd = await dbCommand.ExecuteReaderAsync(commandBehavior); + return await rd.MapAsync(map); + }); - /// - /// Asynchronously execute paramterized query, read only first record and apply mapping. - /// - /// - /// - /// - /// - /// - public async static Task QuerySingleAsync(this DbCommand dbCommand, Func map, CommandBehavior commandBehavior = CommandBehavior.SequentialAccess) => - await dbCommand.DoAsync(async cmd => - { - using (var rd = await dbCommand.TryExecuteReaderAsync(commandBehavior)) - { - return await rd.MapFirstAsync(map); - } - }); + /// + /// Asynchronously execute paramterized query, read only first record and apply mapping. + /// + /// + /// + /// + /// + /// + public async static Task QuerySingleAsync(this DbCommand dbCommand, Func map, CommandBehavior commandBehavior = CommandBehavior.SequentialAccess) => + await dbCommand.DoAsync(async cmd => + { + using var rd = await dbCommand.TryExecuteReaderAsync(commandBehavior); + return await rd.MapFirstAsync(map); + }); - /// - /// Asynchronously execute paramterized query and manually cursor IDataReader. - /// - /// - /// - /// - public async static Task ReadAsync(this DbCommand dbCommand, Func read, CommandBehavior commandBehavior = CommandBehavior.SequentialAccess) => - await dbCommand.DoAsync(async cmd => - { - using (var rd = await dbCommand.TryExecuteReaderAsync(commandBehavior)) - { - return read(rd); - } - }); + /// + /// Asynchronously execute paramterized query and manually cursor IDataReader. + /// + /// + /// + /// + public async static Task ReadAsync(this DbCommand dbCommand, Func read, CommandBehavior commandBehavior = CommandBehavior.SequentialAccess) => + await dbCommand.DoAsync(async cmd => + { + using var rd = await dbCommand.TryExecuteReaderAsync(commandBehavior); + return read(rd); + }); - private static async Task DoAsync(this DbCommand cmd, Func> func) + private static async Task DoAsync(this DbCommand cmd, Func> func) + { + try { - try - { - cmd.Connection.TryOpenConnection(); - return await func(cmd); - } - catch (Exception ex) - { - cmd.TryRollback(); - throw new FailedExecutionException(DbErrorCode.CouldNotExecuteNonQuery, cmd.CommandText, ex); - } + cmd.Connection?.TryOpenConnection(); + return await func(cmd); + } + catch (Exception ex) + { + cmd.TryRollback(); + throw new FailedExecutionException(DbErrorCode.CouldNotExecuteNonQuery, cmd.CommandText, ex); } + } - private static async Task DoVoidAsync(this DbCommand cmd, Func func) + private static async Task DoVoidAsync(this DbCommand cmd, Func func) + { + try { - try - { - cmd.Connection.TryOpenConnection(); - await func(cmd); - } - catch (DbException ex) - { - cmd.TryRollback(); - throw new FailedExecutionException(DbErrorCode.CouldNotExecuteNonQuery, cmd.CommandText, ex); - } + cmd.Connection?.TryOpenConnection(); + await func(cmd); + } + catch (DbException ex) + { + cmd.TryRollback(); + throw new FailedExecutionException(DbErrorCode.CouldNotExecuteNonQuery, cmd.CommandText, ex); } + } - private static async Task DoManyAsync(this DbCommand cmd, IEnumerable paramList, Func func) + private static async Task DoManyAsync(this DbCommand cmd, IEnumerable paramList, Func func) + { + try { - try - { - cmd.Connection.TryOpenConnection(); - foreach (var param in paramList) - { - cmd.Parameters.Clear(); - cmd.SetDbParams(param); - await cmd.DoVoidAsync(func); - } - } - catch (FailedExecutionException) + cmd.Connection?.TryOpenConnection(); + foreach (var param in paramList) { - cmd.TryRollback(); - throw; + cmd.Parameters.Clear(); + cmd.SetDbParams(param); + await cmd.DoVoidAsync(func); } } + catch (FailedExecutionException) + { + cmd.TryRollback(); + throw; + } + } - private async static Task TryExecuteReaderAsync(this DbCommand cmd, CommandBehavior commandBehavior) + private async static Task TryExecuteReaderAsync(this DbCommand cmd, CommandBehavior commandBehavior) + { + try { - try - { - cmd.Connection.TryOpenConnection(); - return await cmd.ExecuteReaderAsync(commandBehavior); - } - catch (Exception ex) - { - throw new FailedExecutionException(DbErrorCode.CouldNotExecuteReader, cmd.CommandText, ex); - } + cmd.Connection?.TryOpenConnection(); + return await cmd.ExecuteReaderAsync(commandBehavior); + } + catch (Exception ex) + { + throw new FailedExecutionException(DbErrorCode.CouldNotExecuteReader, cmd.CommandText, ex); } } } diff --git a/src/Spiffy/Extensions/DbDataReaderExtensions.cs b/src/Spiffy/Extensions/DbDataReaderExtensions.cs index abccdaa..f10606c 100644 --- a/src/Spiffy/Extensions/DbDataReaderExtensions.cs +++ b/src/Spiffy/Extensions/DbDataReaderExtensions.cs @@ -1,72 +1,80 @@ +namespace Spiffy; + using System; using System.Collections.Generic; using System.Data.Common; using System.Linq; using System.Threading.Tasks; -namespace Spiffy +/// +/// DbDataReader extension methods +/// +public static class DbDataReaderExtensions { /// - /// DbDataReader extension methods + /// Asynchronously map DbDataReader to IEnumerable of T /// - public static class DbDataReaderExtensions + /// + /// + /// + /// + public static async Task> MapAsync(this DbDataReader rd, Func map) { - /// - /// Asynchronously map DbDataReader to IEnumerable of T - /// - /// - /// - /// - /// - public static async Task> MapAsync(this DbDataReader rd, Func map) + var records = new List(); + + while (await rd.ReadAsync()) { - var records = new List(); + records.Add(map(rd)); + } - while (await rd.ReadAsync()) - { - records.Add(map(rd)); - } + return records; + } - return records; + /// + /// Asynchronously map first iteration of DbDataReader to T + /// + /// + /// + /// + /// + public static async Task MapFirstAsync(this DbDataReader rd, Func map) + { + if (await rd.ReadAsync()) + { + return map(rd); } - - /// - /// Asynchronously map first iteration of DbDataReader to T - /// - /// - /// - /// - /// - public static async Task MapFirstAsync(this DbDataReader rd, Func map) + else { - if (await rd.ReadAsync()) - { - return map(rd); - } - else - { - return default; - } + return default; } + } - /// - /// Asynchronously map next iteration of DbDataReader to IEnumerable of T - /// - /// - /// - /// - /// - public static async Task> MapNextAsync(this DbDataReader rd, Func map) => - await (rd.NextResult() ? rd.MapAsync(map) : Task.FromResult(Enumerable.Empty())); + /// + /// Asynchronously map next iteration of DbDataReader to IEnumerable of T + /// + /// + /// + /// + /// + public static async Task> MapNextAsync(this DbDataReader rd, Func map) => + await (rd.NextResult() ? rd.MapAsync(map) : Task.FromResult(Enumerable.Empty())); - /// - /// Asynchronously map next iteration of DbDataReader to T - /// - /// - /// - /// - /// - public static async Task MapFirstNextAsync(this DbDataReader rd, Func map) => - await (rd.NextResult() ? rd.MapFirstAsync(map) : Task.FromResult(default)); + /// + /// Asynchronously map next iteration of DbDataReader to T + /// + /// + /// + /// + /// + public static async Task MapFirstNextAsync(this DbDataReader rd, Func map) + { + if (await rd.NextResultAsync()) + { + return await rd.MapFirstAsync(map); + } + else + { + return default; + } } } diff --git a/src/Spiffy/Extensions/IDataReaderExtensions.cs b/src/Spiffy/Extensions/IDataReaderExtensions.cs index 2a0e707..2048ad2 100644 --- a/src/Spiffy/Extensions/IDataReaderExtensions.cs +++ b/src/Spiffy/Extensions/IDataReaderExtensions.cs @@ -1,295 +1,288 @@ -using System; -using System.Collections.Generic; +namespace Spiffy; + using System.Data; -using System.IO; -using System.Linq; -namespace Spiffy +/// +/// IDataReader extension methods +/// +public static class IDataReaderExtensions { /// - /// IDataReader extension methods + /// Map IDataReader to IEnumerable of T /// - public static class IDataReaderExtensions + /// + /// + /// + /// + public static IEnumerable Map(this IDataReader rd, Func map) { - /// - /// Map IDataReader to IEnumerable of T - /// - /// - /// - /// - /// - public static IEnumerable Map(this IDataReader rd, Func map) + var records = new List(); + + while (rd.Read()) { - var records = new List(); + records.Add(map(rd)); + } - while (rd.Read()) - { - records.Add(map(rd)); - } + return records; + } - return records; + /// + /// Map first iteration of IDataReader to T + /// + /// + /// + /// + /// + public static T? MapFirst(this IDataReader rd, Func map) + { + if (rd.Read()) + { + return map(rd); } + else + { + return default; + } + } + + /// + /// Map next iteration of IDataReader to IEnumerable of T + /// + /// + /// + /// + /// + public static IEnumerable MapNext(this IDataReader rd, Func map) => + rd.NextResult() ? rd.Map(map) : []; + + /// + /// Map next iteration of IDataReader to T + /// + /// + /// + /// + /// + public static T? MapFirstNext(this IDataReader rd, Func map) => + rd.NextResult() ? rd.MapFirst(map) : default; + + /// + /// /// Read string from IDataReader + /// + public static string? ReadString(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetString); + + /// + /// Read char from IDataReader + /// + public static char ReadChar(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetChar); + + /// + /// Read bool from IDataReader + /// + public static bool ReadBoolean(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetBoolean); + + /// + /// Read bool from IDataReader + /// + public static bool ReadBool(this IDataReader rd, string field) => rd.ReadBoolean(field); + + /// + /// Read byte from IDataReader + /// + public static byte ReadByte(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetByte); - /// - /// Map first iteration of IDataReader to T - /// - /// - /// - /// - /// - public static T MapFirst(this IDataReader rd, Func map) + /// + /// Read short from IDataReader + /// + public static short ReadInt16(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetInt16); + + /// + /// Read short from IDataReader + /// + public static short ReadShort(this IDataReader rd, string field) => rd.ReadInt16(field); + + /// + /// Read int from IDataReader + /// + public static int ReadInt32(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetInt32); + + /// + /// Read int from IDataReader + /// + public static int ReadInt(this IDataReader rd, string field) => rd.ReadInt32(field); + + /// + /// Read long from IDataReader + /// + public static long ReadInt64(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetInt64); + + /// + /// Read long from IDataReader + /// + public static long ReadLong(this IDataReader rd, string field) => rd.ReadInt64(field); + + /// + /// Read decimal from IDataReader + /// + public static decimal ReadDecimal(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetDecimal); + + /// + /// Read double from IDataReader + /// + public static double ReadDouble(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetDouble); + + /// + /// Read float from IDataReader + /// + public static float ReadFloat(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetFloat); + + /// + /// Read Guid from IDataReader + /// + public static Guid ReadGuid(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetGuid); + + /// + /// Read DateTime from IDataReader + /// + public static DateTime ReadDateTime(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetDateTime); + + /// + /// Read byte[] from IDataReader + /// + /// + /// + /// + public static byte[] ReadBytes(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.StreamBytes) ?? []; + + /// + /// Read bool? from IDataReader + /// + public static bool? ReadNullableBoolean(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetBoolean); + + /// + /// Read bool? from IDataReader + /// + public static bool? ReadNullableBool(this IDataReader rd, string field) => rd.ReadNullableBoolean(field); + + /// + /// Read byte? from IDataReader + /// + public static byte? ReadNullableByte(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetByte); + + /// + /// Read short? from IDataReader + /// + public static short? ReadNullableInt16(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetInt16); + + /// + /// Read short? from IDataReader + /// + public static short? ReadNullableShort(this IDataReader rd, string field) => rd.ReadNullableInt16(field); + + /// + /// Read int? from IDataReader + /// + public static int? ReadNullableInt32(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetInt32); + + /// + /// Read int? from IDataReader + /// + public static int? ReadNullableInt(this IDataReader rd, string field) => rd.ReadNullableInt32(field); + + /// + /// Read long? from IDataReader + /// + public static long? ReadNullableInt64(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetInt64); + + /// + /// Read long? from IDataReader + /// + public static long? ReadNullableLong(this IDataReader rd, string field) => rd.ReadNullableInt64(field); + + /// + /// Read decimal? from IDataReader + /// + public static decimal? ReadNullableDecimal(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetDecimal); + + /// + /// Read double? from IDataReader + /// + public static double? ReadNullableDouble(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetDouble); + + /// + /// Read float? from IDataReader + /// + public static float? ReadNullableFloat(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetFloat); + + /// + /// Read Guid? from IDataReader + /// + public static Guid? ReadNullableGuid(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetGuid); + + /// + /// Read DateTime? from IDataReader + /// + public static DateTime? ReadNullableDateTime(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetDateTime); + + private static T? ReadValueByField(this IDataReader rd, string fieldName, Func map) + { + var i = rd.GetOrdinal(fieldName); + + if (rd.IsDBNull(i)) { - if (rd.Read()) - { - return map(rd); - } - else - { - return default; - } + return default; } - /// - /// Map next iteration of IDataReader to IEnumerable of T - /// - /// - /// - /// - /// - public static IEnumerable MapNext(this IDataReader rd, Func map) => - rd.NextResult() ? rd.Map(map) : Enumerable.Empty(); - - /// - /// Map next iteration of IDataReader to T - /// - /// - /// - /// - /// - public static T MapFirstNext(this IDataReader rd, Func map) => - rd.NextResult() ? rd.MapFirst(map) : default; - - /// - /// /// Read string from IDataReader - /// - public static string ReadString(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetString); - - /// - /// Read char from IDataReader - /// - public static char ReadChar(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetChar); - - /// - /// Read bool from IDataReader - /// - public static bool ReadBoolean(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetBoolean); - - /// - /// Read bool from IDataReader - /// - public static bool ReadBool(this IDataReader rd, string field) => rd.ReadBoolean(field); - - /// - /// Read byte from IDataReader - /// - public static byte ReadByte(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetByte); - - /// - /// Read short from IDataReader - /// - public static short ReadInt16(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetInt16); - - /// - /// Read short from IDataReader - /// - public static short ReadShort(this IDataReader rd, string field) => rd.ReadInt16(field); - - /// - /// Read int from IDataReader - /// - public static int ReadInt32(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetInt32); - - /// - /// Read int from IDataReader - /// - public static int ReadInt(this IDataReader rd, string field) => rd.ReadInt32(field); - - /// - /// Read long from IDataReader - /// - public static long ReadInt64(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetInt64); - - /// - /// Read long from IDataReader - /// - public static long ReadLong(this IDataReader rd, string field) => rd.ReadInt64(field); - - /// - /// Read decimal from IDataReader - /// - public static decimal ReadDecimal(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetDecimal); - - /// - /// Read double from IDataReader - /// - public static double ReadDouble(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetDouble); - - /// - /// Read float from IDataReader - /// - public static float ReadFloat(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetFloat); - - /// - /// Read Guid from IDataReader - /// - public static Guid ReadGuid(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetGuid); - - /// - /// Read DateTime from IDataReader - /// - public static DateTime ReadDateTime(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.GetDateTime); - - /// - /// Read byte[] from IDataReader - /// - /// - /// - /// - public static byte[] ReadBytes(this IDataReader rd, string field) => rd.ReadValueByField(field, rd.StreamBytes); - - /// - /// Read bool? from IDataReader - /// - public static bool? ReadNullableBoolean(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetBoolean); - - /// - /// Read bool? from IDataReader - /// - public static bool? ReadNullableBool(this IDataReader rd, string field) => rd.ReadNullableBoolean(field); - - /// - /// Read byte? from IDataReader - /// - public static byte? ReadNullableByte(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetByte); - - /// - /// Read short? from IDataReader - /// - public static short? ReadNullableInt16(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetInt16); - - /// - /// Read short? from IDataReader - /// - public static short? ReadNullableShort(this IDataReader rd, string field) => rd.ReadNullableInt16(field); - - /// - /// Read int? from IDataReader - /// - public static int? ReadNullableInt32(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetInt32); - - /// - /// Read int? from IDataReader - /// - public static int? ReadNullableInt(this IDataReader rd, string field) => rd.ReadNullableInt32(field); - - /// - /// Read long? from IDataReader - /// - public static long? ReadNullableInt64(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetInt64); - - /// - /// Read long? from IDataReader - /// - public static long? ReadNullableLong(this IDataReader rd, string field) => rd.ReadNullableInt64(field); - - /// - /// Read decimal? from IDataReader - /// - public static decimal? ReadNullableDecimal(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetDecimal); - - /// - /// Read double? from IDataReader - /// - public static double? ReadNullableDouble(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetDouble); - - /// - /// Read float? from IDataReader - /// - public static float? ReadNullableFloat(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetFloat); - - /// - /// Read Guid? from IDataReader - /// - public static Guid? ReadNullableGuid(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetGuid); - - /// - /// Read DateTime? from IDataReader - /// - public static DateTime? ReadNullableDateTime(this IDataReader rd, string field) => rd.ReadNullableValueByField(field, rd.GetDateTime); - - private static T ReadValueByField(this IDataReader rd, string fieldName, Func map) + try + { + return map(i); + } + catch (InvalidCastException ex) { - var i = rd.GetOrdinal(fieldName); - - if (rd.IsDBNull(i)) - { - return default; - } - - try - { - return map(i); - } - catch (InvalidCastException ex) - { - throw new FailedExecutionException(DbErrorCode.CouldNotCastValue, fieldName, ex); - } + throw new FailedExecutionException(DbErrorCode.CouldNotCastValue, fieldName, ex); } + } + + private static T? ReadNullableValueByField(this IDataReader rd, string fieldName, Func map) where T : struct + { + var i = rd.GetOrdinal(fieldName); - private static T? ReadNullableValueByField(this IDataReader rd, string fieldName, Func map) where T : struct + if (rd.IsDBNull(i)) { - var i = rd.GetOrdinal(fieldName); - - if (rd.IsDBNull(i)) - { - return null; - } - - try - { - return map(i); - } - catch (InvalidCastException ex) - { - throw new FailedExecutionException(DbErrorCode.CouldNotCastValue, fieldName, ex); - } + return null; } - private static byte[] StreamBytes(this IDataReader rd, int ordinal) + try { - var bufferSize = 1024; - var buffer = new byte[bufferSize]; - long bytesReturned; - var startIndex = 0; + return map(i); + } + catch (InvalidCastException ex) + { + throw new FailedExecutionException(DbErrorCode.CouldNotCastValue, fieldName, ex); + } + } - using (var ms = new MemoryStream()) - { - bytesReturned = rd.GetBytes(ordinal, startIndex, buffer, 0, bufferSize); - while (bytesReturned == bufferSize) - { - ms.Write(buffer, 0, (int)bytesReturned); - ms.Flush(); + private static byte[] StreamBytes(this IDataReader rd, int ordinal) + { + var bufferSize = 1024; + var buffer = new byte[bufferSize]; + long bytesReturned; + var startIndex = 0; + + using var ms = new MemoryStream(); + bytesReturned = rd.GetBytes(ordinal, startIndex, buffer, 0, bufferSize); + while (bytesReturned == bufferSize) + { + ms.Write(buffer, 0, (int)bytesReturned); + ms.Flush(); - startIndex += bufferSize; - bytesReturned = rd.GetBytes(ordinal, startIndex, buffer, 0, bufferSize); - } + startIndex += bufferSize; + bytesReturned = rd.GetBytes(ordinal, startIndex, buffer, 0, bufferSize); + } - ms.Write(buffer, 0, (int)bytesReturned); - ms.Flush(); + ms.Write(buffer, 0, (int)bytesReturned); + ms.Flush(); - return ms.ToArray(); - } + return ms.ToArray(); - } } } diff --git a/src/Spiffy/Extensions/IDbCommandExtensions.cs b/src/Spiffy/Extensions/IDbCommandExtensions.cs index 6cde4c6..36defed 100644 --- a/src/Spiffy/Extensions/IDbCommandExtensions.cs +++ b/src/Spiffy/Extensions/IDbCommandExtensions.cs @@ -1,242 +1,249 @@ -using System; +namespace Spiffy; + +using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Threading; using System.Threading.Tasks; -namespace Spiffy +/// +/// IDbCommand extension methods +/// +public static class IDbCommandExtensions { /// - /// IDbCommand extension methods + /// Convert IDbCommand to DbCommand /// - public static class IDbCommandExtensions + public static DbCommand ToDbCommand(this IDbCommand cmd) { - /// - /// Assing DbParams to IDbCommand - /// - /// - /// - public static void SetDbParams(this IDbCommand cmd, DbParams param) + if (cmd is DbCommand dbCmd) + { + return dbCmd; + } + else { - if (param != null) + throw new ArgumentException("IDbCommand must be a DbCommand", nameof(cmd)); + } + } + /// + /// Assing DbParams to IDbCommand + /// + /// + /// + public static void SetDbParams(this IDbCommand cmd, DbParams param) + { + if (param != null) + { + foreach (var p in param) { - foreach (var p in param) + var cmdParam = cmd.CreateParameter(); + cmdParam.ParameterName = p.Key; + + if (p.Value is DbTypeParam dbTypeParam) + { + cmdParam.DbType = dbTypeParam.DbType; + cmdParam.Value = dbTypeParam.Value ?? DBNull.Value; + } + else { - var cmdParam = cmd.CreateParameter(); - cmdParam.ParameterName = p.Key; - - if (p.Value is DbTypeParam dbTypeParam) - { - cmdParam.DbType = dbTypeParam.DbType; - cmdParam.Value = dbTypeParam.Value ?? DBNull.Value; - } - else - { - cmdParam.Value = p.Value ?? DBNull.Value; - } - - cmd.Parameters.Add(cmdParam); + cmdParam.Value = p.Value ?? DBNull.Value; } + + cmd.Parameters.Add(cmdParam); } } + } + + /// + /// Execute parameterized query with no results + /// + /// + /// + public static void Exec(this IDbCommand dbCommand) => + dbCommand.DoVoid(cmd => cmd.ExecuteNonQuery()); + + /// + /// Execute parameterized query many times with no results + /// + /// + /// + /// + public static void ExecMany(this IDbCommand dbCommand, IEnumerable paramList) => + dbCommand.DoMany(paramList, cmd => cmd.ExecuteNonQuery()); + + /// + /// Execute parameterized query and return single object value. + /// + /// + /// + public static object? Scalar(this IDbCommand dbCommand) => + dbCommand.Do(cmd => cmd.ExecuteScalar()); + + /// + /// Execute parameterized query, enumerate all records and apply mapping. + /// + /// + /// + /// + /// + /// + public static IEnumerable Query(this IDbCommand dbCommand, Func map, CommandBehavior commandBehavior = CommandBehavior.SequentialAccess) => + dbCommand.Do(cmd => + { + using var rd = cmd.TryExecuteReader(commandBehavior); + return rd.Map(map); + }); + + /// + /// Execute paramterized query, read only first record and apply mapping. + /// + /// + /// + /// + /// + /// + public static T? QuerySingle(this IDbCommand dbCommand, Func map, CommandBehavior commandBehavior = CommandBehavior.SequentialAccess) => + dbCommand.Do(cmd => + { + using var rd = cmd.TryExecuteReader(commandBehavior); + return rd.MapFirst(map); + }); + + /// + /// Execute paramterized query and manually cursor IDataReader. + /// + /// + /// + /// + public static T Read(this IDbCommand dbCommand, Func read, CommandBehavior commandBehavior = CommandBehavior.SequentialAccess) => + dbCommand.Do(cmd => + { + using var rd = cmd.TryExecuteReader(commandBehavior); + return read(rd); + }); + + // + // Async - /// - /// Execute parameterized query with no results - /// - /// - /// - public static void Exec(this IDbCommand dbCommand) => - dbCommand.DoVoid(cmd => cmd.ExecuteNonQuery()); - - /// - /// Execute parameterized query many times with no results - /// - /// - /// - /// - public static void ExecMany(this IDbCommand dbCommand, IEnumerable paramList) => - dbCommand.DoMany(paramList, cmd => cmd.ExecuteNonQuery()); - - /// - /// Execute parameterized query and return single object value. - /// - /// - /// - public static object Scalar(this IDbCommand dbCommand) => - dbCommand.Do(cmd => cmd.ExecuteScalar()); - - /// - /// Execute parameterized query, enumerate all records and apply mapping. - /// - /// - /// - /// - /// - /// - public static IEnumerable Query(this IDbCommand dbCommand, Func map, CommandBehavior commandBehavior = CommandBehavior.SequentialAccess) => - dbCommand.Do(cmd => - { - using (var rd = cmd.TryExecuteReader(commandBehavior)) - { - return rd.Map(map); - } - }); - - /// - /// Execute paramterized query, read only first record and apply mapping. - /// - /// - /// - /// - /// - /// - public static T QuerySingle(this IDbCommand dbCommand, Func map, CommandBehavior commandBehavior = CommandBehavior.SequentialAccess) => - dbCommand.Do(cmd => - { - using (var rd = cmd.TryExecuteReader(commandBehavior)) - { - return rd.MapFirst(map); - } - }); - - /// - /// Execute paramterized query and manually cursor IDataReader. - /// - /// - /// - /// - public static T Read(this IDbCommand dbCommand, Func read, CommandBehavior commandBehavior = CommandBehavior.SequentialAccess) => - dbCommand.Do(cmd => - { - using (var rd = cmd.TryExecuteReader(commandBehavior)) - { - return read(rd); - } - }); - - // - // Async - - /// - /// Asynchronously execute parameterized query with no results - /// - /// - /// - public static Task ExecAsync(this IDbCommand dbCommand) => - (dbCommand as DbCommand).ExecAsync(); - - /// - /// Asynchronously execute parameterized query many times with no results - /// - /// - /// - /// - public static Task ExecManyAsync(this IDbCommand dbCommand, IEnumerable paramList) => - (dbCommand as DbCommand).ExecManyAsync(paramList); - - /// - /// Asynchronously execute parameterized query, and return scalar object - /// - /// - /// - public static Task ScalarAsync(this IDbCommand dbCommand) => - (dbCommand as DbCommand).ScalarAsync(); - - /// - /// Asynchronously execute parameterized query, enumerate all records and apply mapping. - /// - /// - /// - /// - /// - public static Task> QueryAsync(this IDbCommand dbCommand, Func map) => - (dbCommand as DbCommand).QueryAsync(map); - - /// - /// Asynchronously execute paramterized query, read only first record and apply mapping. - /// - /// - /// - /// - /// - public static Task QuerySingleAsync(this IDbCommand dbCommand, Func map) => - (dbCommand as DbCommand).QuerySingleAsync(map); - - /// - /// Asynchronously execute paramterized query and manually cursor IDataReader. - /// - public static Task ReadAsync(this IDbCommand dbCommand) => - (dbCommand as DbCommand).ReadAsync(); - - - internal static void TryRollback(this IDbCommand cmd) + /// + /// Asynchronously execute parameterized query with no results + /// + /// + /// + public static Task ExecAsync(this IDbCommand dbCommand) => + dbCommand.ToDbCommand().ExecAsync(); + + /// + /// Asynchronously execute parameterized query many times with no results + /// + /// + /// + /// + public static Task ExecManyAsync(this IDbCommand dbCommand, IEnumerable paramList) => + dbCommand.ToDbCommand().ExecManyAsync(paramList); + + /// + /// Asynchronously execute parameterized query, and return scalar object + /// + /// + /// + public static Task ScalarAsync(this IDbCommand dbCommand) => + dbCommand.ToDbCommand().ScalarAsync(); + + /// + /// Asynchronously execute parameterized query, enumerate all records and apply mapping. + /// + /// + /// + /// + /// + public static Task> QueryAsync(this IDbCommand dbCommand, Func map) => + dbCommand.ToDbCommand().QueryAsync(map); + + /// + /// Asynchronously execute paramterized query, read only first record and apply mapping. + /// + /// + /// + /// + /// + public static Task QuerySingleAsync(this IDbCommand dbCommand, Func map) => + dbCommand.ToDbCommand().QuerySingleAsync(map); + + /// + /// Asynchronously execute paramterized query and manually cursor IDataReader. + /// + public static Task ReadAsync(this IDbCommand dbCommand) => + dbCommand.ToDbCommand().ReadAsync(); + + + internal static void TryRollback(this IDbCommand cmd) + { + if (cmd.Transaction != null) { - if (cmd.Transaction != null) - { - cmd.Transaction.Rollback(); - } + cmd.Transaction.Rollback(); } + } - private static T Do(this IDbCommand cmd, Func func) + private static T Do(this IDbCommand cmd, Func func) + { + try { - try - { - cmd.Connection.TryOpenConnection(); - return func(cmd); - } - catch (Exception ex) - { - cmd.TryRollback(); - throw new FailedExecutionException(DbErrorCode.CouldNotExecuteNonQuery, cmd.CommandText, ex); - } + cmd.Connection?.TryOpenConnection(); + return func(cmd); } + catch (Exception ex) + { + cmd.TryRollback(); + throw new FailedExecutionException(DbErrorCode.CouldNotExecuteNonQuery, cmd.CommandText, ex); + } + } - private static void DoVoid(this IDbCommand cmd, Action func) + private static void DoVoid(this IDbCommand cmd, Action func) + { + try { - try - { - cmd.Connection.TryOpenConnection(); - func(cmd); - } - catch (Exception ex) - { - cmd.TryRollback(); - throw new FailedExecutionException(DbErrorCode.CouldNotExecuteNonQuery, cmd.CommandText, ex); - } + cmd.Connection?.TryOpenConnection(); + func(cmd); } + catch (Exception ex) + { + cmd.TryRollback(); + throw new FailedExecutionException(DbErrorCode.CouldNotExecuteNonQuery, cmd.CommandText, ex); + } + } - private static void DoMany(this IDbCommand cmd, IEnumerable paramList, Action func) + private static void DoMany(this IDbCommand cmd, IEnumerable paramList, Action func) + { + try { - try - { - cmd.Connection.TryOpenConnection(); + cmd.Connection?.TryOpenConnection(); - foreach (var param in paramList) - { - cmd.Parameters.Clear(); - cmd.SetDbParams(param); - cmd.DoVoid(func); - } - } - catch (Exception ex) + foreach (var param in paramList) { - cmd.TryRollback(); - throw new FailedExecutionException(DbErrorCode.CouldNotExecuteNonQuery, cmd.CommandText, ex); + cmd.Parameters.Clear(); + cmd.SetDbParams(param); + cmd.DoVoid(func); } } + catch (Exception ex) + { + cmd.TryRollback(); + throw new FailedExecutionException(DbErrorCode.CouldNotExecuteNonQuery, cmd.CommandText, ex); + } + } - private static IDataReader TryExecuteReader(this IDbCommand cmd, CommandBehavior commandBehavior) + private static IDataReader TryExecuteReader(this IDbCommand cmd, CommandBehavior commandBehavior) + { + try { - try - { - cmd.Connection.TryOpenConnection(); - return cmd.ExecuteReader(commandBehavior); - } - catch (Exception ex) - { - throw new FailedExecutionException(DbErrorCode.CouldNotExecuteReader, cmd.CommandText, ex); - } + cmd.Connection?.TryOpenConnection(); + return cmd.ExecuteReader(commandBehavior); + } + catch (Exception ex) + { + throw new FailedExecutionException(DbErrorCode.CouldNotExecuteReader, cmd.CommandText, ex); } } } diff --git a/src/Spiffy/Extensions/IDbConnectionExtensions.cs b/src/Spiffy/Extensions/IDbConnectionExtensions.cs index 6d066eb..02969ba 100644 --- a/src/Spiffy/Extensions/IDbConnectionExtensions.cs +++ b/src/Spiffy/Extensions/IDbConnectionExtensions.cs @@ -1,48 +1,47 @@ -using System; +namespace Spiffy; + +using System; using System.Data; -namespace Spiffy +/// +/// IDbConnection extension methods +/// +public static class IDbConnectionExtensions { /// - /// IDbConnection extension methods + /// Attempt to open the IDbConnection if it is not already open. /// - public static class IDbConnectionExtensions + /// + public static void TryOpenConnection(this IDbConnection conn) { - /// - /// Attempt to open the IDbConnection if it is not already open. - /// - /// - public static void TryOpenConnection(this IDbConnection conn) + try { - try - { - if (conn.State == ConnectionState.Closed) - { - conn.Open(); - } - } - catch (Exception ex) + if (conn.State == ConnectionState.Closed) { - throw new CouldNotOpenConnectionException(ex); + conn.Open(); } } + catch (Exception ex) + { + throw new CouldNotOpenConnectionException(ex); + } + } - /// - /// Attempt to begin a IDbTransaction. - /// - /// - /// - public static IDbTransaction TryBeginTransaction(this IDbConnection connection) + /// + /// Attempt to begin a IDbTransaction. + /// + /// + /// + public static IDbTransaction TryBeginTransaction(this IDbConnection connection) + { + try { - try - { - connection.TryOpenConnection(); - return connection.BeginTransaction(); - } - catch (Exception ex) - { - throw new FailedTransacitonException(ex); - } + connection.TryOpenConnection(); + return connection.BeginTransaction(); + } + catch (Exception ex) + { + throw new FailedTransacitonException(ex); } } } diff --git a/src/Spiffy/Extensions/IDbTransactionExtensions.cs b/src/Spiffy/Extensions/IDbTransactionExtensions.cs index 383943f..4f8699b 100644 --- a/src/Spiffy/Extensions/IDbTransactionExtensions.cs +++ b/src/Spiffy/Extensions/IDbTransactionExtensions.cs @@ -1,28 +1,27 @@ -using System; +namespace Spiffy; + +using System; using System.Data; -namespace Spiffy +/// +/// IDbTransaction extension methods +/// +public static class IDbTransactionExtensions { /// - /// IDbTransaction extension methods + /// Commit unit of work and cleanup. + /// Rolls back transaction and throws FailedCommitBatchException on failure /// - public static class IDbTransactionExtensions + public static void TryCommit(this IDbTransaction tran) { - /// - /// Commit unit of work and cleanup. - /// Rolls back transaction and throws FailedCommitBatchException on failure - /// - public static void TryCommit(this IDbTransaction tran) + try + { + tran.Commit(); + } + catch (Exception ex) { - try - { - tran.Commit(); - } - catch (Exception ex) - { - tran.Rollback(); - throw new FailedCommitBatchException(ex); - } + tran.Rollback(); + throw new FailedCommitBatchException(ex); } } } diff --git a/src/Spiffy/Spiffy.csproj b/src/Spiffy/Spiffy.csproj index 84aec7f..dfdef71 100644 --- a/src/Spiffy/Spiffy.csproj +++ b/src/Spiffy/Spiffy.csproj @@ -2,7 +2,7 @@ Spiffy Spiffy - 4.0.0 + 5.0.0 $([System.DateTime]::Now.ToString(yyyy)) @@ -13,7 +13,9 @@ en-CA - netstandard2.0 + net8.0 + enable + enable portable Library true diff --git a/test/Spiffy.Tests/AsyncTests.cs b/test/Spiffy.Tests/AsyncTests.cs index 2875dd2..05168a4 100644 --- a/test/Spiffy.Tests/AsyncTests.cs +++ b/test/Spiffy.Tests/AsyncTests.cs @@ -5,119 +5,114 @@ namespace Spiffy.Tests; using Xunit; [Collection("TestDb")] -public class DbCommandExtensionsTests +public class DbCommandExtensionsTests(TestDb testDb) { - private readonly TestDb _testDb; + private readonly TestDb _testDb = testDb; - public DbCommandExtensionsTests(TestDb testDb) - { - _testDb = testDb; - } + [Fact] + public async Task CanExecAsync() + { + var descripton = _testDb.GenerateRandomString(); - [Fact] - public async Task CanExecAsync() - { - var descripton = _testDb.GenerateRandomString(); - - var sql = @" + var sql = @" INSERT INTO test_values (description) VALUES (@description); SELECT description FROM test_values WHERE description = @description;"; - var param = new DbParams("description", descripton); + var param = new DbParams("description", descripton); - using var conn = _testDb.NewConnection(); - using var cmd = new DbCommandBuilder(conn, sql, param).Build(); + using var conn = _testDb.NewConnection(); + using var cmd = new DbCommandBuilder(conn, sql, param).Build(); - var exists = await cmd.QuerySingleAsync(rd => rd.ReadString("description")); + var exists = await cmd.QuerySingleAsync(rd => rd.ReadString("description")); - Assert.NotNull(exists); - } + Assert.NotNull(exists); + } - [Fact] - public async Task CanExecTranAsync() - { - var descripton = _testDb.GenerateRandomString(); + [Fact] + public async Task CanExecTranAsync() + { + var descripton = _testDb.GenerateRandomString(); - var sql = @" + var sql = @" INSERT INTO test_values (description) VALUES (@description); SELECT description FROM test_values WHERE description = @description;"; - var param = new DbParams("description", descripton); + var param = new DbParams("description", descripton); - using var conn = _testDb.NewConnection(); - using var tran = conn.TryBeginTransaction(); - using var cmd = new DbCommandBuilder(tran, sql, param).Build(); + using var conn = _testDb.NewConnection(); + using var tran = conn.TryBeginTransaction(); + using var cmd = new DbCommandBuilder(tran, sql, param).Build(); - var exists = await cmd.QuerySingleAsync(rd => rd.ReadString("description")); + var exists = await cmd.QuerySingleAsync(rd => rd.ReadString("description")); - tran.TryCommit(); + tran.TryCommit(); - Assert.NotNull(exists); - } + Assert.NotNull(exists); + } - [Fact] - public async Task CanExecWithRollbackAsync() - { - var descripton = _testDb.GenerateRandomString(); + [Fact] + public async Task CanExecWithRollbackAsync() + { + var descripton = _testDb.GenerateRandomString(); - var sql = "INSERT INTO test_values (description) VALUES (@description);"; - var param = new DbParams("description", descripton); + var sql = "INSERT INTO test_values (description) VALUES (@description);"; + var param = new DbParams("description", descripton); - using var conn = _testDb.NewConnection(); - using var tran = conn.TryBeginTransaction(); - using var cmd = new DbCommandBuilder(tran, sql, param).Build(); - await cmd.ExecAsync(); - tran.Rollback(); + using var conn = _testDb.NewConnection(); + using var tran = conn.TryBeginTransaction(); + using var cmd = new DbCommandBuilder(tran, sql, param).Build(); + await cmd.ExecAsync(); + tran.Rollback(); - sql = "SELECT description FROM test_values WHERE description = @description;"; - using var existsCmd = new DbCommandBuilder(conn, sql, param).Build(); - var exists = await existsCmd.QuerySingleAsync(rd => rd.ReadString("description")); - Assert.Null(exists); - } + sql = "SELECT description FROM test_values WHERE description = @description;"; + using var existsCmd = new DbCommandBuilder(conn, sql, param).Build(); + var exists = await existsCmd.QuerySingleAsync(rd => rd.ReadString("description")); + Assert.Null(exists); + } - [Fact] - public async Task CanScalarAsync() - { - var expected = _testDb.GenerateRandomString(); + [Fact] + public async Task CanScalarAsync() + { + var expected = _testDb.GenerateRandomString(); - var sql = "SELECT @description AS description"; - var param = new DbParams("description", expected); + var sql = "SELECT @description AS description"; + var param = new DbParams("description", expected); - using var conn = _testDb.NewConnection(); - using var cmd = new DbCommandBuilder(conn, sql, param).Build(); + using var conn = _testDb.NewConnection(); + using var cmd = new DbCommandBuilder(conn, sql, param).Build(); - var result = (await cmd.ScalarAsync()) as string; + var result = (await cmd.ScalarAsync()) as string; - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); + } - [Fact] - public async Task CanQueryAsync() - { - var expected = _testDb.GenerateRandomString(); + [Fact] + public async Task CanQueryAsync() + { + var expected = _testDb.GenerateRandomString(); - var sql = "SELECT @description AS description"; - var param = new DbParams("description", expected); + var sql = "SELECT @description AS description"; + var param = new DbParams("description", expected); - using var conn = _testDb.NewConnection(); - using var cmd = new DbCommandBuilder(conn, sql, param).Build(); + using var conn = _testDb.NewConnection(); + using var cmd = new DbCommandBuilder(conn, sql, param).Build(); - var result = await cmd.QueryAsync(rd => rd.ReadString("description")); + var result = await cmd.QueryAsync(rd => rd.ReadString("description")); - Assert.Equal(expected, result.First()); - } + Assert.Equal(expected, result.First()); + } - [Fact] - public async Task CanQuerySingleAsync() - { - var expected = _testDb.GenerateRandomString(); - var sql = "SELECT @description AS description"; - var param = new DbParams("description", expected); + [Fact] + public async Task CanQuerySingleAsync() + { + var expected = _testDb.GenerateRandomString(); + var sql = "SELECT @description AS description"; + var param = new DbParams("description", expected); - using var conn = _testDb.NewConnection(); - using var cmd = new DbCommandBuilder(conn, sql, param).Build(); + using var conn = _testDb.NewConnection(); + using var cmd = new DbCommandBuilder(conn, sql, param).Build(); - var result = await cmd.QuerySingleAsync(rd => rd.ReadString("description")); + var result = await cmd.QuerySingleAsync(rd => rd.ReadString("description")); - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); + } } diff --git a/test/Spiffy.Tests/DbParamTests.cs b/test/Spiffy.Tests/DbParamTests.cs index 23a229e..37f76ce 100644 --- a/test/Spiffy.Tests/DbParamTests.cs +++ b/test/Spiffy.Tests/DbParamTests.cs @@ -4,45 +4,49 @@ namespace Spiffy.Tests; public class DbParamTests { - [Fact] - public void ShouldCreateEmptyParams() - { - var p = new DbParams(); - Assert.Empty(p.Keys); - } - - [Fact] - public void ShouldCreateParamsWithOneEntry() - { - var p = new DbParams("key", 1); - Assert.Equal(1, p["key"]); - } - - [Fact] - public void ShouldAddParams() - { - var p = new DbParams - { - { "key", 1 } - }; - p.Add("key2", 2); - Assert.Equal(1, p["key"]); - Assert.Equal(2, p["key2"]); - } - - [Fact] - public void ShouldCombineParams() - { - var p1 = new DbParams("key", 1); - p1.Add("key2", 2); - - var p2 = new DbParams("key1", 3); - p2.Add("key2", "WRONG"); - - p1.Add(p2); - - Assert.Equal(1, p1["key"]); - Assert.Equal(2, p1["key2"]); - Assert.Equal(3, p1["key1"]); - } + [Fact] + public void ShouldCreateEmptyParams() + { + var p = new DbParams(); + Assert.Empty(p.Keys); + } + + [Fact] + public void ShouldCreateParamsWithOneEntry() + { + var p = new DbParams("key", 1); + Assert.Equal(1, p["key"]); + } + + [Fact] + public void ShouldAddParams() + { + var p = new DbParams + { + { "key", 1 }, + { "key2", 2 } + }; + Assert.Equal(1, p["key"]); + Assert.Equal(2, p["key2"]); + } + + [Fact] + public void ShouldCombineParams() + { + var p1 = new DbParams("key", 1) + { + { "key2", 2 } + }; + + var p2 = new DbParams("key1", 3) + { + { "key2", "WRONG" } + }; + + p1.Add(p2); + + Assert.Equal(1, p1["key"]); + Assert.Equal(2, p1["key2"]); + Assert.Equal(3, p1["key1"]); + } } diff --git a/test/Spiffy.Tests/Program.cs b/test/Spiffy.Tests/Program.cs deleted file mode 100644 index e472954..0000000 --- a/test/Spiffy.Tests/Program.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Spiffy.Tests; - -public static class Program -{ - public static int Main() => 0; -} \ No newline at end of file diff --git a/test/Spiffy.Tests/Spiffy.Tests.csproj b/test/Spiffy.Tests/Spiffy.Tests.csproj index d175baf..bfcc913 100644 --- a/test/Spiffy.Tests/Spiffy.Tests.csproj +++ b/test/Spiffy.Tests/Spiffy.Tests.csproj @@ -1,21 +1,22 @@ + net8.0 + enable + enable false - false true - - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/Spiffy.Tests/TestDb.cs b/test/Spiffy.Tests/TestDb.cs index 4c95336..0a24b96 100644 --- a/test/Spiffy.Tests/TestDb.cs +++ b/test/Spiffy.Tests/TestDb.cs @@ -1,9 +1,7 @@ namespace Spiffy.Tests; -using System; using System.Data; using Microsoft.Data.Sqlite; -using System.IO; using Xunit; [CollectionDefinition("TestDb")] @@ -13,26 +11,26 @@ public class TestDbCollection : ICollectionFixture public sealed class TestDbConnectionFactory : IDbConnectionFactory { - private const string _dbName = "Spiffy.Tests.db"; - private const string _connectionString = $"Data Source={_dbName}"; - public IDbConnection NewConnection() => new SqliteConnection(_connectionString); + private const string _dbName = "Spiffy.Tests.db"; + private const string _connectionString = $"Data Source={_dbName}"; + public IDbConnection NewConnection() => new SqliteConnection(_connectionString); } public class TestDb : DbFixture { - public TestDb() : base(new TestDbConnectionFactory()) - { - using var conn = NewConnection(); + public TestDb() : base(new TestDbConnectionFactory()) + { + using var conn = NewConnection(); - var cmdBuilder = new DbCommandBuilder(conn); + var cmdBuilder = new DbCommandBuilder(conn); - using var fs = File.OpenRead("schema.sql"); - using var sr = new StreamReader(fs); - var sql = sr.ReadToEnd(); + using var fs = File.OpenRead("schema.sql"); + using var sr = new StreamReader(fs); + var sql = sr.ReadToEnd(); - using var cmd = cmdBuilder.CommandText(sql).Build(); - cmd.Exec(); - } + using var cmd = cmdBuilder.CommandText(sql).Build(); + cmd.Exec(); + } - public string GenerateRandomString() => Path.GetRandomFileName().Replace(".", ""); + public string GenerateRandomString() => Path.GetRandomFileName().Replace(".", ""); } diff --git a/test/Spiffy.Tests/Tests.cs b/test/Spiffy.Tests/Tests.cs index 66c915a..908bd29 100644 --- a/test/Spiffy.Tests/Tests.cs +++ b/test/Spiffy.Tests/Tests.cs @@ -5,14 +5,9 @@ namespace Spiffy.Tests; using Xunit; [Collection("TestDb")] -public class IDbCommandExtensionsTests +public class IDbCommandExtensionsTests(TestDb testDb) { - private readonly TestDb _testDb; - - public IDbCommandExtensionsTests(TestDb testDb) - { - _testDb = testDb; - } + private readonly TestDb _testDb = testDb; [Fact] public void CanExec() @@ -281,7 +276,7 @@ public void InsertAndSelectBinaryShouldWork() .Build(); var result = cmd.QuerySingle(rd => rd.ReadBytes("data")); - var resultStr = System.Text.Encoding.UTF8.GetString(result); + var resultStr = System.Text.Encoding.UTF8.GetString(result ?? []); Assert.Equal(resultStr, testString); } }