Skip to content

Commit

Permalink
minor cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
pwelter34 committed Sep 12, 2023
1 parent e2f5e67 commit b42701f
Show file tree
Hide file tree
Showing 15 changed files with 135 additions and 37 deletions.
4 changes: 2 additions & 2 deletions src/FluentCommand.SqlServer/Merge/DataMergeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ private static bool NeedQuote(Type type)
return true;
if (underType == typeof(Guid))
return true;
#if !NETSTANDARD2_0
#if NET6_0_OR_GREATER
if (underType == typeof(DateOnly))
return true;
if (underType == typeof(TimeOnly))
Expand All @@ -465,7 +465,7 @@ private static string GetValue(object value)
DateTimeOffset dateTimeOffset => dateTimeOffset.ToString("u"),
byte[] byteArray => ToHex(byteArray),
bool boolValue => boolValue ? "1" : "0",
#if !NETSTANDARD2_0
#if NET6_0_OR_GREATER
DateOnly dateValue => dateValue.ToString("yyyy-MM-dd"),
TimeOnly timeValue => timeValue.ToString("hh:mm:ss.ffffff"),
#endif
Expand Down
4 changes: 2 additions & 2 deletions src/FluentCommand/ConcurrencyToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public ConcurrencyToken(byte[] value)

public ConcurrencyToken(string value)
{
#if !NETSTANDARD2_0
#if NET5_0_OR_GREATER
Value = string.IsNullOrEmpty(value) ? Array.Empty<byte>() : Convert.FromHexString(value);
#else
Value = string.IsNullOrEmpty(value) ? Array.Empty<byte>() : FromHexString(value);
Expand All @@ -24,7 +24,7 @@ public ConcurrencyToken(string value)

public override string ToString()
{
#if !NETSTANDARD2_0
#if NET5_0_OR_GREATER
return Value != null ? Convert.ToHexString(Value) : null;
#else
return Value != null ? ToHexString(Value) : null;
Expand Down
38 changes: 21 additions & 17 deletions src/FluentCommand/DataCommand.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System.Data;
using System.Data.Common;
using System.Diagnostics;

using FluentCommand.Extensions;
using FluentCommand.Internal;

using HashCode = FluentCommand.Internal.HashCode;

Expand Down Expand Up @@ -162,7 +162,7 @@ public IDataCommand UseCache(DateTimeOffset absoluteExpiration)
/// A fluent <see langword="interface" /> to the data command.
/// </returns>
/// <remarks>
/// Cached keys are created using the current DataCommand state. When any Query opertion is
/// Cached keys are created using the current DataCommand state. When any Query operation is
/// executed with a cache policy, the results are cached. Use this method with the same parameters
/// to expire the cached item.
/// </remarks>
Expand Down Expand Up @@ -525,7 +525,7 @@ protected override void DisposeManagedResources()
Command?.Dispose();
}

#if !NETSTANDARD2_0
#if NETCOREAPP3_0_OR_GREATER
/// <summary>
/// Disposes the managed resources.
/// </summary>
Expand Down Expand Up @@ -556,12 +556,14 @@ private TResult QueryFactory<TResult>(Func<TResult> query, bool supportCache)

AssertDisposed();

var watch = Stopwatch.StartNew();
var watch = SharedStopwatch.StartNew();
var logged = false;

try
{
var cacheKey = CacheKey<TResult>(supportCache);

(bool cacheSuccess, TResult cacheValue) = GetCache<TResult>(cacheKey);
var (cacheSuccess, cacheValue) = GetCache<TResult>(cacheKey);
if (cacheSuccess)
return cacheValue;

Expand All @@ -577,19 +579,16 @@ private TResult QueryFactory<TResult>(Func<TResult> query, bool supportCache)
}
catch (Exception ex)
{
watch.Stop();
LogCommand(watch.Elapsed, ex);
logged = true;

throw;
}
finally
{
// if catch block didn't already log
if (watch.IsRunning)
{
watch.Stop();
if (!logged)
LogCommand(watch.Elapsed);
}

_dataSession.ReleaseConnection();
Dispose();
Expand All @@ -606,12 +605,14 @@ private TResult QueryFactory<TResult>(Func<TResult> query, bool supportCache)

AssertDisposed();

var watch = Stopwatch.StartNew();
var watch = SharedStopwatch.StartNew();
var logged = false;

try
{
var cacheKey = CacheKey<TResult>(supportCache);

(bool cacheSuccess, TResult cacheValue) = await GetCacheAsync<TResult>(cacheKey, cancellationToken).ConfigureAwait(false);
var (cacheSuccess, cacheValue) = await GetCacheAsync<TResult>(cacheKey, cancellationToken).ConfigureAwait(false);
if (cacheSuccess)
return cacheValue;

Expand All @@ -627,22 +628,25 @@ private TResult QueryFactory<TResult>(Func<TResult> query, bool supportCache)
}
catch (Exception ex)
{
watch.Stop();
LogCommand(watch.Elapsed, ex);
logged = true;

throw;
}
finally
{
// if catch block didn't already log
if (watch.IsRunning)
{
watch.Stop();
if (!logged)
LogCommand(watch.Elapsed);
}

#if NETCOREAPP3_0_OR_GREATER

await _dataSession.ReleaseConnectionAsync();
await DisposeAsync();
#else
_dataSession.ReleaseConnection();
Dispose();
#endif
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/FluentCommand/DataMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ static DataMapping()
{typeof(ushort), DbType.UInt16},
{typeof(uint), DbType.UInt32},
{typeof(ulong), DbType.UInt64},
#if !NETSTANDARD2_0
#if NET6_0_OR_GREATER
{typeof(DateOnly), DbType.Date},
{typeof(TimeOnly), DbType.Time},
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/FluentCommand/DataParameterHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ static DataParameterHandlers()
_dataTypeHandlers = new ConcurrentDictionary<Type, IDataParameterHandler>();
_dataTypeHandlers.TryAdd(typeof(ConcurrencyToken), new ConcurrencyTokenHandler());

#if !NETSTANDARD2_0
#if NET6_0_OR_GREATER
// once ADO supports DateOnly & TimeOnly, this can be removed
_dataTypeHandlers.TryAdd(typeof(DateOnly), new DateOnlyHandler());
_dataTypeHandlers.TryAdd(typeof(TimeOnly), new TimeOnlyHandler());
Expand Down
4 changes: 2 additions & 2 deletions src/FluentCommand/DataSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public DbTransaction BeginTransaction(IsolationLevel isolationLevel = IsolationL
return Transaction;
}

#if !NETSTANDARD2_0
#if NETCOREAPP3_0_OR_GREATER
/// <summary>
/// Starts a database transaction with the specified isolation level.
/// </summary>
Expand Down Expand Up @@ -237,7 +237,7 @@ public void ReleaseConnection()
_openedConnection = false;
}

#if !NETSTANDARD2_0
#if NETCOREAPP3_0_OR_GREATER
/// <summary>
/// Releases the connection.
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions src/FluentCommand/DisposableBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace FluentCommand;
/// </summary>
public abstract class DisposableBase
: IDisposable
#if !NETSTANDARD2_0
#if NETCOREAPP3_0_OR_GREATER
, IAsyncDisposable
#endif
{
Expand Down Expand Up @@ -65,7 +65,7 @@ protected virtual void DisposeManagedResources()
protected virtual void DisposeUnmanagedResources()
{ }

#if !NETSTANDARD2_0
#if NETCOREAPP3_0_OR_GREATER
/// <summary>
/// Disposes the asynchronous.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/FluentCommand/Handlers/DateOnlyHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#if !NETSTANDARD2_0
#if NET6_0_OR_GREATER
using System.Data;

using FluentCommand;
Expand Down
2 changes: 1 addition & 1 deletion src/FluentCommand/Handlers/TimeOnlyHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#if !NETSTANDARD2_0
#if NET6_0_OR_GREATER
using System.Data;

using FluentCommand;
Expand Down
2 changes: 1 addition & 1 deletion src/FluentCommand/IDataQueryAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace FluentCommand;
/// An <see langword="interface"/> defining a data query operations asynchronously.
/// </summary>
public interface IDataQueryAsync
#if !NETSTANDARD2_0
#if NETCOREAPP3_0_OR_GREATER
: IAsyncDisposable
#endif

Expand Down
6 changes: 3 additions & 3 deletions src/FluentCommand/IDataSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace FluentCommand;
/// </summary>
public interface IDataSession
: IDisposable
#if !NETSTANDARD2_0
#if NETCOREAPP3_0_OR_GREATER
, IAsyncDisposable
#endif
{
Expand Down Expand Up @@ -52,7 +52,7 @@ public interface IDataSession
/// <returns>A <see cref="DbTransaction"/> representing the new transaction.</returns>
DbTransaction BeginTransaction(IsolationLevel isolationLevel);

#if !NETSTANDARD2_0
#if NETCOREAPP3_0_OR_GREATER
/// <summary>
/// Starts a database transaction with the specified isolation level.
/// </summary>
Expand Down Expand Up @@ -99,7 +99,7 @@ public interface IDataSession
/// </summary>
void ReleaseConnection();

#if !NETSTANDARD2_0
#if NETCOREAPP3_0_OR_GREATER
/// <summary>
/// Releases the connection.
/// </summary>
Expand Down
35 changes: 35 additions & 0 deletions src/FluentCommand/Internal/SharedStopwatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Diagnostics;

namespace FluentCommand.Internal;

internal readonly struct SharedStopwatch
{
private static readonly Stopwatch _stopwatch = Stopwatch.StartNew();

private readonly TimeSpan _started;

private SharedStopwatch(TimeSpan started)
{
_started = started;
}

public TimeSpan Elapsed => _stopwatch.Elapsed - _started;

public static SharedStopwatch StartNew()
{
// This call to StartNewCore isn't required, but is included to avoid measurement errors
// which can occur during periods of high allocation activity. In some cases, calls to Stopwatch
// operations can block at their return point on the completion of a background GC operation. When
// this occurs, the GC wait time ends up included in the measured time span. In the event the first
// call to StartNewCore blocked on a GC operation, the second call will most likely occur when the
// GC is no longer active. In practice, a substantial improvement to the consistency of analyzer
// timing data was observed.
//
// Note that the call to SharedStopwatch.Elapsed is not affected, because the GC wait will occur
// after the timer has already recorded its stop time.
_ = StartNewCore();
return StartNewCore();
}

private static SharedStopwatch StartNewCore() => new(_stopwatch.Elapsed);
}
4 changes: 2 additions & 2 deletions test/FluentCommand.Entities/DataType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class DataType

public TimeSpan TimeSpan { get; set; }

#if !NETSTANDARD2_0
#if NET6_0_OR_GREATER
public DateOnly DateOnly { get; set; }

public TimeOnly TimeOnly { get; set; }
Expand All @@ -56,7 +56,7 @@ public class DataType

public TimeSpan? TimeSpanNull { get; set; }

#if !NETSTANDARD2_0
#if NET6_0_OR_GREATER
public DateOnly? DateOnlyNull { get; set; }

public TimeOnly? TimeOnlyNull { get; set; }
Expand Down
59 changes: 59 additions & 0 deletions test/FluentCommand.SqlServer.Tests/DataQueryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -388,4 +388,63 @@ public async System.Threading.Tasks.Task SqlQueryChangeTable()
await transaction.CommitAsync();
}


[Fact]
public async System.Threading.Tasks.Task SqlTransactionQuery()
{
await using var session = Services.GetRequiredService<IDataSession>();
session.Should().NotBeNull();

await using var transaction = await session.BeginTransactionAsync(IsolationLevel.ReadCommitted);

var id = Guid.NewGuid();
var user = new User
{
Id = id,
EmailAddress = $"{id}@email.com",
DisplayName = "Last, First",
FirstName = "First",
LastName = "Last",
Created = DateTimeOffset.Now,
Updated = DateTimeOffset.Now
};

var userId = await session
.Sql(builder => builder
.Insert<User>()
.Values(user)
.Output(p => p.Id)
.Tag()
)
.QueryValueAsync<Guid>();

userId.Should().Be(id);

var selected = await session
.Sql(builder => builder
.Select<User>()
.Where(p => p.Id, id)
.Tag()
)
.QuerySingleAsync<User>();

selected.Should().NotBeNull();
selected.Id.Should().Be(id);

var updateId = await session
.Sql(builder => builder
.Update<User>()
.Value(p => p.DisplayName, "Updated")
.Output(p => p.Id)
.Where(p => p.Id, id)
.Tag()
)
.QueryValueAsync<Guid>();

updateId.Should().Be(id);

await transaction.RollbackAsync();
}


}
4 changes: 2 additions & 2 deletions test/FluentCommand.SqlServer.Tests/DatabaseFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class DatabaseFixture : TestHostFixture
{
protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
var trackerConnnection = context.Configuration.GetConnectionString("Tracker");
var trackerConnection = context.Configuration.GetConnectionString("Tracker");
var cacheConnection = context.Configuration.GetConnectionString("DistributedCache");

services.AddHostedService<DatabaseInitializer>();
Expand All @@ -35,7 +35,7 @@ protected override void ConfigureServices(HostBuilderContext context, IServiceCo
services.TryAddSingleton<IDataConfiguration>(sp =>
new DataConfiguration(
SqlClientFactory.Instance,
trackerConnnection,
trackerConnection,
sp.GetService<IDataCache>(),
sp.GetService<IQueryGenerator>(),
sp.GetService<IDataQueryLogger>()
Expand Down

0 comments on commit b42701f

Please sign in to comment.