Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
102 changes: 102 additions & 0 deletions src/DbReader/DataReaderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Construction;
using Extensions;
using LightInject;
Expand Down Expand Up @@ -128,6 +131,105 @@ private static IEnumerable<T> ReadWithoutNavigationProperties<T>(IDataReader dat
return result;
}

/// <summary>
/// Reads the data from the given <paramref name="dataReader"/> asynchronously
/// and translates the data into an <see cref="IEnumerable{T}"/>.
/// </summary>
/// <typeparam name="T">The type of object to be created from the reader.</typeparam>
/// <param name="dataReader">The target <see cref="DbDataReader"/>.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A task representing an <see cref="IEnumerable{T}"/> that represents the data translated into objects.</returns>
public static Task<IEnumerable<T>> ReadAsync<T>(this DbDataReader dataReader, CancellationToken cancellationToken)
{
return TypeEvaluator<T>.HasNavigationProperties
? ReadWithNavigationPropertiesAsync<T>(dataReader, cancellationToken)
: ReadWithoutNavigationPropertiesAsync<T>(dataReader, cancellationToken);
}

private static async Task<IEnumerable<T>> ReadWithNavigationPropertiesAsync<T>(DbDataReader dataReader, CancellationToken cancellationToken)
{
List<T> result = new List<T>();
if (!await dataReader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
return result;
}

var container = containerFactory.Value;
using (var scope = container.BeginScope())
{
var instanceReader = scope.GetInstance<IInstanceReader<T>>();
result.TryAdd(instanceReader.Read(dataReader, string.Empty));
while (await dataReader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
result.TryAdd(instanceReader.Read(dataReader, string.Empty));
}
}
return result;
}

private static async Task<IEnumerable<T>> ReadWithoutNavigationPropertiesAsync<T>(DbDataReader dataReader, CancellationToken cancellationToken)
{
var result = new List<T>();

if (!await dataReader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
return result;
}

if (typeof(T).IsSimpleType())
{
if (ValueConverter.CanConvert(typeof(T)))
{
result.Add((T)ValueConverter.Convert<T>(dataReader, 0));
while (await dataReader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
result.Add((T)ValueConverter.Convert<T>(dataReader, 0));
}
}
else
{
result.Add((T)dataReader.GetValue(0));
while (await dataReader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
result.Add((T)dataReader.GetValue(0));
}
}

return result;
}

var propertyReaderDelegate = PropertyReaderDelegateCache<T>.Get(SqlStatement.Current);
if (propertyReaderDelegate == null)
{
var container = containerFactory.Value;
using (var scope = container.BeginScope())
{
var propertyReaderMethodBuilder =
scope.GetInstance<IReaderMethodBuilder<T>>("PropertyReaderMethodBuilder");
var ordinalsSelector = scope.GetInstance<IOrdinalSelector>();

propertyReaderDelegate = new PropertyReaderDelegate<T>()
{
Ordinals = ordinalsSelector.Execute(typeof(T), dataReader, string.Empty),
ReadMethod = propertyReaderMethodBuilder.CreateMethod()
};

PropertyReaderDelegateCache<T>.Put(SqlStatement.Current, propertyReaderDelegate);
}
}

var ordinals = propertyReaderDelegate.Ordinals;
var readMethod = propertyReaderDelegate.ReadMethod;

result.Add(readMethod(dataReader, ordinals));
while (await dataReader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
result.Add(readMethod(dataReader, ordinals));
}

return result;
}

internal static void SetContainer(IServiceContainer existingContainer)
{
containerFactory = new Lazy<IServiceContainer>(() => existingContainer);
Expand Down
4 changes: 2 additions & 2 deletions src/DbReader/DbConnectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@
string query,
object arguments = null, Action<IDbCommand> configureCommand = default)
{
using (var dataReader = await dbConnection.ExecuteReaderAsync(cancellationToken, query, arguments, configureCommand).ConfigureAwait(false))
using (var dataReader = (System.Data.Common.DbDataReader)await dbConnection.ExecuteReaderAsync(cancellationToken, query, arguments, configureCommand).ConfigureAwait(false))
{
SqlStatement.Current = query;
return dataReader.Read<T>();
return await dataReader.ReadAsync<T>(cancellationToken).ConfigureAwait(false);
}
}

Expand Down Expand Up @@ -267,7 +267,7 @@
/// <param name="arguments">An object that represents the query arguments.</param>
/// <param name="configureCommand">A function used to configure the underlying <see cref="IDbCommand"/>.</param>
/// <returns>The first instance of {T}/> that represents the result of the query or null if not found.</returns>
public static async Task<T> ReadFirstOrDefaultAsync<T>(this IDbConnection dbConnection, string query, object? arguments = null, Action<IDbCommand>? configureCommand = null)

Check warning on line 270 in src/DbReader/DbConnectionExtensions.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 270 in src/DbReader/DbConnectionExtensions.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 270 in src/DbReader/DbConnectionExtensions.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 270 in src/DbReader/DbConnectionExtensions.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
=> await dbConnection.ReadFirstOrDefaultAsync<T>(CancellationToken.None, query, arguments, configureCommand);

/// <summary>
Expand All @@ -280,7 +280,7 @@
/// <param name="arguments">An object that represents the query arguments.</param>
/// <param name="configureCommand">A function used to configure the underlying <see cref="IDbCommand"/>.</param>
/// <returns>The first instance of {T}/> that represents the result of the query or null if not found.</returns>
public static async Task<T> ReadFirstOrDefaultAsync<T>(this IDbConnection dbConnection, CancellationToken cancellationToken, string query, object? arguments = null, Action<IDbCommand>? configureCommand = null)

Check warning on line 283 in src/DbReader/DbConnectionExtensions.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 283 in src/DbReader/DbConnectionExtensions.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 283 in src/DbReader/DbConnectionExtensions.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 283 in src/DbReader/DbConnectionExtensions.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
=> (await dbConnection.ReadAsync<T>(cancellationToken, query, arguments, configureCommand)).FirstOrDefault();

private static T ReadScalarValue<T>(IDataReader reader)
Expand Down
Loading