Skip to content
Open

Hi #10

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
610dad9
v0.9.2
JocaPC Mar 23, 2018
563e56f
Error reported in callback
JocaPC Mar 24, 2018
2ea3758
Two Stream utility extensions
JocaPC Mar 24, 2018
e80b0c0
Nuget v0.9.3
JocaPC Mar 24, 2018
f828349
v.9.4
JocaPC Mar 24, 2018
5eb7ea6
v.0.9.5 - refactoring
JocaPC Mar 25, 2018
bbadfdb
Added Proc() helper
JocaPC Mar 29, 2018
32f4154
Renamed AddWithValue() to Param()
JocaPC Mar 29, 2018
a0a4a06
v0.9.6 - Added logging in .net core 2.0
JocaPC Mar 30, 2018
6ede5eb
Added Common logging
JocaPC Mar 30, 2018
50168d0
Added Common.Logging
JocaPC Mar 30, 2018
f92cfd2
v.0.9.7
JocaPC Mar 31, 2018
432c170
Updated documentation
JocaPC Mar 31, 2018
7bb7032
Final release - v1.0
JocaPC Apr 1, 2018
3110f38
Set theme jekyll-theme-slate
JocaPC Apr 1, 2018
f73bb22
Updated documentation
JocaPC Apr 1, 2018
d888c80
Merge branch 'master' of https://github.com/JocaPC/CLR-Belgrade-SqlCl…
JocaPC Apr 1, 2018
05b401b
More updates in documentation
JocaPC Apr 1, 2018
e3664d6
Minor changes in doc
JocaPC Apr 1, 2018
051510d
Updated intro page
JocaPC Apr 1, 2018
abae8a5
Updated intro
JocaPC Apr 1, 2018
1a4efaa
Updating intro
JocaPC Apr 1, 2018
7441877
Updated intro
JocaPC Apr 1, 2018
2798e10
Update intro + license
JocaPC Apr 1, 2018
02d6886
Corrected tag in intro
JocaPC Apr 1, 2018
88fbf1a
Updated path to license
JocaPC Apr 1, 2018
b0b5e46
Updated intro
JocaPC Apr 1, 2018
ac6291b
Updated readme
JocaPC Apr 1, 2018
7965605
Update index.md
JocaPC Apr 1, 2018
aabf2f5
Added retry logic (v1.1)
JocaPC Apr 2, 2018
dc42f42
Added some missing comments
JocaPC Apr 2, 2018
b9daacf
Added notifications with tests
JocaPC Apr 6, 2018
090758d
v.1.1.2 Logger passed from command to pipe/mapper
JocaPC Apr 14, 2018
99827d7
v 1.1.3. Experimental FirstOfDefault helper
JocaPC Jul 8, 2018
d7d98d3
Custom output encoding
JocaPC Aug 5, 2018
6f4eb6d
Update Test/Master/BasicQueryMapperTest.cs
JocaPC Oct 8, 2018
a2865d8
Build for .netcoreapp2.1 and .standard2.0
JocaPC Oct 13, 2018
1fd6419
Added new error test case
JocaPC Oct 13, 2018
8fe3411
Refactored tests
JocaPC Oct 13, 2018
53b21ba
Cleaning-up errors test
JocaPC Oct 13, 2018
be1deec
Removing net46 from SqlDependency test
JocaPC Oct 13, 2018
3febee9
Pipe inherits Mapper
JocaPC Jan 5, 2019
d7b9c71
Added query extensions
JocaPC Jan 5, 2019
186698c
Refactored tests to use IQuery instead of IQueryMapper
JocaPC Jan 5, 2019
2efc436
Renamed main interface files
JocaPC Jan 6, 2019
e779082
Moved extension file
JocaPC Jan 6, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Code/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ obj/*
bin/*
nuget/*
.vs/*
.vscode/*
project.lock.json
Binary file removed Code/BelgradeSql.pfx
Binary file not shown.
Binary file added Code/BelgradeSqlClient.pfx
Binary file not shown.
26 changes: 11 additions & 15 deletions Code/Code.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard1.6;netcoreapp1.0;netcoreapp1.1;netcoreapp2.0;net46</TargetFrameworks>
<TargetFrameworks>netstandard1.6;netstandard2.0;netcoreapp1.0;netcoreapp1.1;netcoreapp2.0;netcoreapp2.1;net46</TargetFrameworks>
<AssemblyName>Belgrade.SqlClient</AssemblyName>
<PackageId>Belgrade.Sql.Client</PackageId>
<PackageTargetFallback Condition=" '$(TargetFramework)' == 'netstandard1.5' ">$(PackageTargetFallback);dnxcore50</PackageTargetFallback>
Expand All @@ -18,13 +18,15 @@
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>Jovan Popovic</Authors>
<Version>0.9.1</Version>
<PackageProjectUrl>https://github.com/JocaPC/CLR-Belgrade-SqlClient</PackageProjectUrl>
<PackageTags>C#, .NET, SqlServer</PackageTags>
<RepositoryUrl>https://github.com/JocaPC/CLR-Belgrade-SqlClient</RepositoryUrl>
<Version>1.1.4</Version>
<PackageProjectUrl>http://jocapc.github.io/Belgrade-SqlClient/</PackageProjectUrl>
<PackageTags>C#, .NET, SqlServer, AdoNet</PackageTags>
<RepositoryUrl>https://github.com/JocaPC/Belgrade-SqlClient</RepositoryUrl>
<PackageLicenseUrl>https://opensource.org/licenses/MIT</PackageLicenseUrl>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>BelgradeSql.pfx</AssemblyOriginatorKeyFile>
<AssemblyOriginatorKeyFile>BelgradeSqlClient.pfx</AssemblyOriginatorKeyFile>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageReleaseNotes>Added query notifications in mapper.</PackageReleaseNotes>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard1.5|AnyCPU'">
Expand Down Expand Up @@ -52,14 +54,8 @@
</ItemGroup>

<ItemGroup>
<Compile Remove="IQuery.cs" />
<Compile Remove="SqlDb\Extensions\CommandExtensions.cs" />
<Compile Remove="SqlDb\Extensions\IQueryExtensions.cs" />
<Compile Remove="SqlDb\Extensions\QueryMapperExtensions.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Data.SqlClient" Version="4.4.2" />
<PackageReference Include="Common.Logging" Version="3.4.1" />
<PackageReference Include="System.Data.SqlClient" Version="4.4.3" />
</ItemGroup>

</Project>
80 changes: 80 additions & 0 deletions Code/Common/BaseStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
// This source file is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.See the license files for details.
using Common.Logging;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Threading.Tasks;

namespace Belgrade.SqlClient.Common
{
Expand Down Expand Up @@ -94,5 +96,83 @@ internal BaseStatement AddParameter(string name, DbType type, object value, int
this.Command.Parameters.Add(p);
return this;
}

protected ILog _logger = null;

/// <summary>
/// Adds a logger that will be used by SQL client.
/// </summary>
/// <param name="logger">Common.Logging.ILog where log records will be written.</param>
/// <returns>This statement.</returns>
public virtual BaseStatement AddLogger(ILog logger)
{
this._logger = logger;
return this;
}

protected async Task ExecuteWithRetry(DbCommand command, object callback)
{
bool isResultSentToCallback = false;
int retryIteration = 0;
bool shouldRetry = false;
Exception rootException = null;
do
{
shouldRetry = false; // Let's assume that we should not retry execution in this iteration.
rootException = null;
try
{
isResultSentToCallback = await ExecuteCommand(command, callback).ConfigureAwait(false);
}
catch (Exception ex)
{
rootException = ex;
// If this is transient error AND results are not already sent to the client:
// Retry the action.
if (SqlDb.RetryErrorHandler.ShouldRetry(ex) && !isResultSentToCallback)
{
shouldRetry = true;
retryIteration++;
if (_logger != null)
_logger.InfoFormat("Database call returned the transient error {message}. Retrying..", ex.Message);
}
else
{
if(_logger!=null)
_logger.WarnFormat("Database call returned the error {message}. Trying to handle exception...", ex.Message);
await ExecuteCallbackWithException(callback, ex).ConfigureAwait(false);
}
}
finally
{
command.Connection.Close();
if (shouldRetry && !isResultSentToCallback)
{
if (SqlDb.RetryErrorHandler.ShouldWaitToRetry(rootException))
{
if (_logger != null)
_logger.InfoFormat("Delayed retry {iteration} due to the transient error.", retryIteration);
await Task.Delay(5000 + 10000 * (retryIteration - 1)).ConfigureAwait(false); // wait 5, 15, and 25 sec
}
else
{
if (_logger != null)
_logger.InfoFormat("Retrying immediatelly {iteration} attempt", retryIteration);
}
}
}
} while (shouldRetry && retryIteration < SqlDb.RetryErrorHandler.RETRY_COUNT && !isResultSentToCallback);

if (shouldRetry && retryIteration == SqlDb.RetryErrorHandler.RETRY_COUNT)
{
if (this._logger != null)
this._logger.ErrorFormat("Query failed after {iteration} retries.\n{excetpion}", SqlDb.RetryErrorHandler.RETRY_COUNT, rootException);

await ExecuteCallbackWithException(callback, rootException);
}
}

protected virtual async Task ExecuteCallbackWithException(object callback, Exception ex) { }
protected virtual async Task<bool> ExecuteCommand(DbCommand command, object callback) => false;
}
}
95 changes: 95 additions & 0 deletions Code/Common/Extensions/ICommandExtensions.Obsolete.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System;
using System.Data.Common;
using System.IO;
using System.Threading.Tasks;

namespace Belgrade.SqlClient
{
public static partial class ICommandExtensions
{
/// <summary>
/// Executes SQL command text.
/// </summary>
/// <returns>Generic task.</returns>
[Obsolete("Use command.Sql(...).Exec() instead.")]
public static Task Exec(this ICommand command, DbCommand cmd)
{
return command.Sql(cmd).Exec();
}

/// <summary>
/// Maps results of SQL command to callback.
/// </summary>
/// <returns>Generic task.</returns>
[Obsolete("Use command.Sql(...).Map(callback) instead.")]
public static Task Map(this ICommand command, DbCommand cmd, Action<DbDataReader> callback)
{
return command.Sql(cmd).Map(callback);
}

/// <summary>
/// Maps results of SQL command to callback.
/// </summary>
/// <returns>Generic task.</returns>
[Obsolete("Use command.Sql(...).Map(callback) instead.")]
public static Task Map(this ICommand command, DbCommand cmd, Func<DbDataReader, Task> callback)
{
return command.Sql(cmd).Map(callback);
}

/// <summary>
/// Maps results of SQL command to callback.
/// </summary>
/// <returns>Generic task.</returns>
[Obsolete("Use command.Sql(...).Stream(output, options) instead.")]
public static Task Stream(this ICommand command, DbCommand cmd, Stream output, Options options = null)
{
return command.Sql(cmd).Stream(output, options);
}

#region "Utilities for default output"

#endregion

#region "Backward compatiliblity methods"

[Obsolete("Use Exec() method instead of this one.")]
/// <summary>
/// Executes sql statement and provides each row to the callback function.
/// </summary>
/// <param name="sql">SQL query that will be executed.</param>
/// <param name="callback">Callback function that will be called for each row.</param>
/// <returns>Task</returns>
public static Task ExecuteNonQuery(this ICommand command, string sql)
{
return command.Exec(sql);
}

[Obsolete("Use Map() method instead of this one.")]
/// <summary>
/// Executes sql statement and provides each row to the callback function.
/// </summary>
/// <param name="sql">SQL query that will be executed.</param>
/// <param name="callback">Callback function that will be called for each row.</param>
/// <returns>Task</returns>
public static Task ExecuteReader(this ICommand command, string sql, Action<DbDataReader> callback)
{
return command.Map(sql, callback);
}


[Obsolete("Use Map() method instead of this one.")]
/// <summary>
/// Executes sql statement and provides each row to the callback function.
/// </summary>
/// <param name="sql">SQL query that will be executed.</param>
/// <param name="callback">Callback function that will be called for each row.</param>
/// <returns>Task</returns>
public static Task ExecuteReader(this ICommand command, string sql, Func<DbDataReader, Task> callback)
{
return command.Map(sql, callback);
}
#endregion

}
}
105 changes: 105 additions & 0 deletions Code/Common/Extensions/ICommandExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using Belgrade.SqlClient.Common;
using Common.Logging;
using System;
using System.Data.Common;
using System.IO;
using System.Threading.Tasks;

namespace Belgrade.SqlClient
{
public static partial class ICommandExtensions
{
/// <summary>
/// Set the query text on the command.
/// </summary>
/// <returns>Query Pipe.</returns>
public static ICommand Sql(this ICommand command, DbCommand cmd)
{
if (command is BaseStatement)
(command as BaseStatement).SetCommand(cmd);
return command;
}

/// <summary>
/// Initializes a stored procedure (no need for explicit EXEC in command text.)
/// </summary>
/// <param name="command">Sql command initialized as stored procedure.</param>
/// <param name="cmd">DbCommand containing the name of stored procedure.</param>
/// <returns>Command with initialized stored procedure.</returns>
public static ICommand Proc(this ICommand command, DbCommand cmd)
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
return command.Sql(cmd);
}

/// <summary>
/// Add the parameter to the command.
/// </summary>
/// <param name="command">the command where the parameter will be added.</param>
/// <param name="name">The name of parameter.</param>
/// <param name="type">The type of parameter.</param>
/// <param name="value">The value of the parameter.</param>
/// <param name="size">The size of the parameter.</param>
/// <returns></returns>
public static ICommand Param(this ICommand command, string name, System.Data.DbType type, object value, int size = 0)
{
if (command is BaseStatement)
{
if(value != null)
(command as BaseStatement).AddParameter(name, type, value, size);
else
(command as BaseStatement).AddParameter(name, type, DBNull.Value, size);
}
return command;
}

#region "Utilities for default output"

/// <summary>
/// Executes SQL query and put results into stream.
/// </summary>
/// <param name="output">Output stream where results will be written.</param>
/// <param name="defaultOutput">Output text that will be placed into stream if there are no results from database.</param>
/// <returns>Task</returns>
public static Task Stream(this ICommand command, Stream output, string defaultOutput = "[]")
{
return command.Stream(output, new Options() { DefaultOutput = defaultOutput });
}

/// <summary>
/// Executes SQL query and put results into stream.
/// </summary>
/// <param name="output">Output stream where results will be written.</param>
/// <param name="defaultOutput">Output content that will be placed into stream if there are no results from database.</param>
/// <returns>Task</returns>
public static Task Stream(this ICommand command, Stream output, byte[] defaultOutput)
{
return command.Stream(output, new Options() { DefaultOutput = defaultOutput });
}

/// <summary>
/// Executes SQL query and put results into stream.
/// </summary>
/// <param name="writer">Text writer where results will be written.</param>
/// <param name="defaultOutput">Output text that will be placed into stream if there are no results from database.</param>
/// <returns>Task</returns>
public static Task Stream(this ICommand command, TextWriter writer, string defaultOutput = "[]")
{
return command.Stream(writer, new Options() { DefaultOutput = defaultOutput });
}

#endregion

/// <summary>
/// Adds a logger that will be used by SQL Command.
/// </summary>
/// <param name="logger">Common.Logging.ILog where log records will be written.</param>
/// <returns>This statement.</returns>
public static ICommand AddLogger(this ICommand command, ILog logger)
{
(command as BaseStatement).AddLogger(logger);
return command;
}

}
}
Loading