diff --git a/src/DbReader/DataReaderExtensions.cs b/src/DbReader/DataReaderExtensions.cs index cee9260..a0d44fa 100644 --- a/src/DbReader/DataReaderExtensions.cs +++ b/src/DbReader/DataReaderExtensions.cs @@ -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; @@ -128,6 +131,105 @@ private static IEnumerable ReadWithoutNavigationProperties(IDataReader dat return result; } + /// + /// Reads the data from the given asynchronously + /// and translates the data into an . + /// + /// The type of object to be created from the reader. + /// The target . + /// The token to monitor for cancellation requests. + /// A task representing an that represents the data translated into objects. + public static Task> ReadAsync(this DbDataReader dataReader, CancellationToken cancellationToken) + { + return TypeEvaluator.HasNavigationProperties + ? ReadWithNavigationPropertiesAsync(dataReader, cancellationToken) + : ReadWithoutNavigationPropertiesAsync(dataReader, cancellationToken); + } + + private static async Task> ReadWithNavigationPropertiesAsync(DbDataReader dataReader, CancellationToken cancellationToken) + { + List result = new List(); + if (!await dataReader.ReadAsync(cancellationToken).ConfigureAwait(false)) + { + return result; + } + + var container = containerFactory.Value; + using (var scope = container.BeginScope()) + { + var instanceReader = scope.GetInstance>(); + 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> ReadWithoutNavigationPropertiesAsync(DbDataReader dataReader, CancellationToken cancellationToken) + { + var result = new List(); + + if (!await dataReader.ReadAsync(cancellationToken).ConfigureAwait(false)) + { + return result; + } + + if (typeof(T).IsSimpleType()) + { + if (ValueConverter.CanConvert(typeof(T))) + { + result.Add((T)ValueConverter.Convert(dataReader, 0)); + while (await dataReader.ReadAsync(cancellationToken).ConfigureAwait(false)) + { + result.Add((T)ValueConverter.Convert(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.Get(SqlStatement.Current); + if (propertyReaderDelegate == null) + { + var container = containerFactory.Value; + using (var scope = container.BeginScope()) + { + var propertyReaderMethodBuilder = + scope.GetInstance>("PropertyReaderMethodBuilder"); + var ordinalsSelector = scope.GetInstance(); + + propertyReaderDelegate = new PropertyReaderDelegate() + { + Ordinals = ordinalsSelector.Execute(typeof(T), dataReader, string.Empty), + ReadMethod = propertyReaderMethodBuilder.CreateMethod() + }; + + PropertyReaderDelegateCache.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(() => existingContainer); diff --git a/src/DbReader/DbConnectionExtensions.cs b/src/DbReader/DbConnectionExtensions.cs index 042715a..c5e6272 100644 --- a/src/DbReader/DbConnectionExtensions.cs +++ b/src/DbReader/DbConnectionExtensions.cs @@ -83,10 +83,10 @@ public static async Task> ReadAsync( string query, object arguments = null, Action 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(); + return await dataReader.ReadAsync(cancellationToken).ConfigureAwait(false); } }