Skip to content

Commit b771bff

Browse files
author
Oren (electricessence)
committed
Updated to 5.7.0
Improved transaction methods. Added cancellation tokens where possible. Updated documentation.
1 parent 0ee5e2f commit b771bff

20 files changed

+2638
-1912
lines changed

ExpressiveCommandBase.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ public void Execute(Action<TCommand> handler)
298298
var c = cmd as TCommand;
299299
if (c == null) throw new InvalidCastException($"Actual command type ({cmd.GetType()}) is not compatible with expected command type ({typeof(TCommand)}).");
300300
AddParams(c);
301-
con.Open();
301+
con.EnsureOpen();
302302
handler(c);
303303
}
304304
});
@@ -318,7 +318,7 @@ public T Execute<T>(Func<TCommand, T> transform)
318318
var c = cmd as TCommand;
319319
if (c == null) throw new InvalidCastException($"Actual command type ({cmd.GetType()}) is not compatible with expected command type ({typeof(TCommand)}).");
320320
AddParams(c);
321-
con.Open();
321+
con.EnsureOpen();
322322
return transform(c);
323323
}
324324
});
@@ -339,7 +339,7 @@ public object ExecuteReturn()
339339
var returnParameter = c.CreateParameter();
340340
returnParameter.Direction = ParameterDirection.ReturnValue;
341341
c.Parameters.Add(returnParameter);
342-
con.Open();
342+
con.EnsureOpen();
343343
c.ExecuteNonQuery();
344344
return returnParameter.Value;
345345
}
@@ -368,7 +368,7 @@ protected IEnumerable<T> IterateReaderInternal<T>(Func<IDataRecord, T> transform
368368
var c = cmd as TCommand;
369369
if (c == null) throw new InvalidCastException($"Actual command type ({cmd.GetType()}) is not compatible with expected command type ({typeof(TCommand)}).");
370370
AddParams(c);
371-
con.Open();
371+
con.EnsureOpen();
372372
using (var reader = c.ExecuteReader(CommandBehavior.CloseConnection))
373373
{
374374
while (reader.Read())

ExpressiveDbCommandBase.cs

Lines changed: 432 additions & 415 deletions
Large diffs are not rendered by default.

Extensions.Results.cs

Lines changed: 53 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,76 @@
22
using System.Data;
33
using System.Data.Common;
44
using System.Linq;
5+
using System.Threading;
56
using System.Threading.Tasks;
67

78
namespace Open.Database.Extensions
89
{
910
public static partial class Extensions
1011
{
11-
/// <summary>
12-
/// Asynchronously returns all records and iteratively attempts to map the fields to type T.
13-
/// </summary>
14-
/// <typeparam name="T">The model type to map the values to (using reflection).</typeparam>
15-
/// <param name="reader">The IDataReader to read results from.</param>
16-
/// <param name="fieldMappingOverrides">An override map of field names to column names where the keys are the property names, and values are the column names.</param>
17-
/// <returns>A task containing the list of results.</returns>
18-
public static async Task<IEnumerable<T>> ResultsAsync<T>(this DbDataReader reader, IEnumerable<(string Field, string Column)> fieldMappingOverrides) where T : new()
12+
/// <summary>
13+
/// Asynchronously returns all records and iteratively attempts to map the fields to type T.
14+
/// </summary>
15+
/// <typeparam name="T">The model type to map the values to (using reflection).</typeparam>
16+
/// <param name="reader">The IDataReader to read results from.</param>
17+
/// <param name="fieldMappingOverrides">An override map of field names to column names where the keys are the property names, and values are the column names.</param>
18+
/// <param name="token">Optional cancellation token.</param>
19+
/// <returns>A task containing the list of results.</returns>
20+
public static async Task<IEnumerable<T>> ResultsAsync<T>(this DbDataReader reader, IEnumerable<(string Field, string Column)> fieldMappingOverrides, CancellationToken? token = null) where T : new()
1921
{
20-
if (!await reader.ReadAsync()) return Enumerable.Empty<T>();
22+
var t = token ?? CancellationToken.None;
23+
if (!await reader.ReadAsync(t)) return Enumerable.Empty<T>();
2124

2225
var x = new Transformer<T>(fieldMappingOverrides);
2326
// Ignore missing columns.
2427
var columns = reader.GetMatchingOrdinals(x.ColumnNames, true);
2528

26-
return x.AsDequeueingEnumerable(await RetrieveAsyncInternal(reader,
27-
columns.Select(c => c.Ordinal).ToArray(),
29+
return x.AsDequeueingEnumerable(await RetrieveAsyncInternal(reader, t,
30+
columns.Select(c => c.Ordinal).ToArray(),
2831
columns.Select(c => c.Name).ToArray(), readStarted: true));
2932
}
3033

31-
/// <summary>
32-
/// Asynchronously returns all records and iteratively attempts to map the fields to type T.
33-
/// </summary>
34-
/// <typeparam name="T">The model type to map the values to (using reflection).</typeparam>
35-
/// <param name="reader">The IDataReader to read results from.</param>
36-
/// <param name="fieldMappingOverrides">An override map of field names to column names where the keys are the property names, and values are the column names.</param>
37-
/// <returns>A task containing the list of results.</returns>
38-
public static Task<IEnumerable<T>> ResultsAsync<T>(this DbDataReader reader, IEnumerable<KeyValuePair<string, string>> fieldMappingOverrides) where T : new()
39-
=> ResultsAsync<T>(reader, fieldMappingOverrides?.Select(kvp => (kvp.Key, kvp.Value)));
40-
41-
/// <summary>
42-
/// Asynchronously returns all records and iteratively attempts to map the fields to type T.
43-
/// </summary>
44-
/// <typeparam name="T">The model type to map the values to (using reflection).</typeparam>
45-
/// <param name="reader">The IDataReader to read results from.</param>
46-
/// <param name="fieldMappingOverrides">An override map of field names to column names where the keys are the property names, and values are the column names.</param>
47-
/// <returns>A task containing the list of results.</returns>
48-
public static Task<IEnumerable<T>> ResultsAsync<T>(this DbDataReader reader, params (string Field, string Column)[] fieldMappingOverrides) where T : new()
34+
/// <summary>
35+
/// Asynchronously returns all records and iteratively attempts to map the fields to type T.
36+
/// </summary>
37+
/// <typeparam name="T">The model type to map the values to (using reflection).</typeparam>
38+
/// <param name="reader">The IDataReader to read results from.</param>
39+
/// <param name="fieldMappingOverrides">An override map of field names to column names where the keys are the property names, and values are the column names.</param>
40+
/// <param name="token">Optional cancellation token.</param>
41+
/// <returns>A task containing the list of results.</returns>
42+
public static Task<IEnumerable<T>> ResultsAsync<T>(this DbDataReader reader, IEnumerable<KeyValuePair<string, string>> fieldMappingOverrides, CancellationToken? token = null) where T : new()
43+
=> ResultsAsync<T>(reader, fieldMappingOverrides?.Select(kvp => (kvp.Key, kvp.Value)), token);
44+
45+
/// <summary>
46+
/// Asynchronously returns all records and iteratively attempts to map the fields to type T.
47+
/// </summary>
48+
/// <typeparam name="T">The model type to map the values to (using reflection).</typeparam>
49+
/// <param name="reader">The IDataReader to read results from.</param>
50+
/// <param name="fieldMappingOverrides">An override map of field names to column names where the keys are the property names, and values are the column names.</param>
51+
/// <returns>A task containing the list of results.</returns>
52+
public static Task<IEnumerable<T>> ResultsAsync<T>(this DbDataReader reader, params (string Field, string Column)[] fieldMappingOverrides) where T : new()
4953
=> ResultsAsync<T>(reader, fieldMappingOverrides as IEnumerable<(string Field, string Column)>);
5054

51-
/// <summary>
52-
/// Iterates each record and attempts to map the fields to type T.
53-
/// Data is temporarily stored (buffered in entirety) in a queue of dictionaries before applying the transform for each iteration.
54-
/// </summary>
55-
/// <typeparam name="T">The model type to map the values to (using reflection).</typeparam>
56-
/// <param name="reader">The IDataReader to read results from.</param>
57-
/// <param name="fieldMappingOverrides">An optional override map of field names to column names where the keys are the property names, and values are the column names.</param>
58-
/// <returns>The enumerable to pull the transformed results from.</returns>
59-
public static IEnumerable<T> Results<T>(this IDataReader reader, IEnumerable<(string Field, string Column)> fieldMappingOverrides)
55+
/// <summary>
56+
/// Asynchronously returns all records and iteratively attempts to map the fields to type T.
57+
/// </summary>
58+
/// <typeparam name="T">The model type to map the values to (using reflection).</typeparam>
59+
/// <param name="reader">The IDataReader to read results from.</param>
60+
/// <param name="token">A cancellation token.</param>
61+
/// <param name="fieldMappingOverrides">An override map of field names to column names where the keys are the property names, and values are the column names.</param>
62+
/// <returns>A task containing the list of results.</returns>
63+
public static Task<IEnumerable<T>> ResultsAsync<T>(this DbDataReader reader, CancellationToken token, params (string Field, string Column)[] fieldMappingOverrides) where T : new()
64+
=> ResultsAsync<T>(reader, fieldMappingOverrides as IEnumerable<(string Field, string Column)>, token);
65+
66+
/// <summary>
67+
/// Iterates each record and attempts to map the fields to type T.
68+
/// Data is temporarily stored (buffered in entirety) in a queue of dictionaries before applying the transform for each iteration.
69+
/// </summary>
70+
/// <typeparam name="T">The model type to map the values to (using reflection).</typeparam>
71+
/// <param name="reader">The IDataReader to read results from.</param>
72+
/// <param name="fieldMappingOverrides">An optional override map of field names to column names where the keys are the property names, and values are the column names.</param>
73+
/// <returns>The enumerable to pull the transformed results from.</returns>
74+
public static IEnumerable<T> Results<T>(this IDataReader reader, IEnumerable<(string Field, string Column)> fieldMappingOverrides)
6075
where T : new()
6176
{
6277
if (!reader.Read()) return Enumerable.Empty<T>();

Extensions.Retrieve.cs

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Data;
44
using System.Data.Common;
55
using System.Linq;
6+
using System.Threading;
67
using System.Threading.Tasks;
78

89
namespace Open.Database.Extensions
@@ -134,19 +135,21 @@ public static QueryResult<Queue<object[]>> Retrieve(this IDbCommand command, IEn
134135
public static QueryResult<Queue<object[]>> Retrieve(this IDbCommand command, string c, params string[] others)
135136
=> ExecuteReader(command, reader => Retrieve(reader, new string[1] { c }.Concat(others)));
136137

137-
/// <summary>
138-
/// Asynchronously enumerates all the remaining values of the current result set of a data reader.
139-
/// DBNull values are left unchanged (retained).
140-
/// </summary>
141-
/// <param name="reader">The reader to enumerate.</param>
142-
/// <returns>The QueryResult that contains a buffer block of the results and the column mappings.</returns>
143-
public static async Task<QueryResult<Queue<object[]>>> RetrieveAsync(this DbDataReader reader)
138+
/// <summary>
139+
/// Asynchronously enumerates all the remaining values of the current result set of a data reader.
140+
/// DBNull values are left unchanged (retained).
141+
/// </summary>
142+
/// <param name="reader">The reader to enumerate.</param>
143+
/// <param name="token">Optional cancellation token.</param>
144+
/// <returns>The QueryResult that contains a buffer block of the results and the column mappings.</returns>
145+
public static async Task<QueryResult<Queue<object[]>>> RetrieveAsync(this DbDataReader reader, CancellationToken? token = null)
144146
{
145-
var fieldCount = reader.FieldCount;
147+
var t = token ?? CancellationToken.None;
148+
var fieldCount = reader.FieldCount;
146149
var names = reader.GetNames(); // pull before first read.
147150
var buffer = new Queue<object[]>();
148151

149-
while (await reader.ReadAsync())
152+
while (await reader.ReadAsync(t))
150153
{
151154
var row = new object[fieldCount];
152155
reader.GetValues(row);
@@ -159,7 +162,7 @@ public static async Task<QueryResult<Queue<object[]>>> RetrieveAsync(this DbData
159162
buffer);
160163
}
161164

162-
static async Task<QueryResult<Queue<object[]>>> RetrieveAsyncInternal(DbDataReader reader, int[] ordinals, string[] columnNames = null, bool readStarted = false)
165+
static async Task<QueryResult<Queue<object[]>>> RetrieveAsyncInternal(DbDataReader reader, CancellationToken? token, int[] ordinals, string[] columnNames = null, bool readStarted = false)
163166
{
164167
var fieldCount = ordinals.Length;
165168
if (columnNames == null) columnNames = ordinals.Select(n => reader.GetName(n)).ToArray();
@@ -175,14 +178,15 @@ static async Task<QueryResult<Queue<object[]>>> RetrieveAsyncInternal(DbDataRead
175178
return row;
176179
};
177180

178-
var buffer = new Queue<object[]>();
179-
if (readStarted || await reader.ReadAsync())
181+
var t = token ?? CancellationToken.None;
182+
var buffer = new Queue<object[]>();
183+
if (readStarted || await reader.ReadAsync(t))
180184
{
181185
do
182186
{
183187
buffer.Enqueue(handler(reader));
184188
}
185-
while (await reader.ReadAsync());
189+
while (await reader.ReadAsync(t));
186190
}
187191

188192
return new QueryResult<Queue<object[]>>(
@@ -191,15 +195,16 @@ static async Task<QueryResult<Queue<object[]>>> RetrieveAsyncInternal(DbDataRead
191195
buffer);
192196
}
193197

194-
/// <summary>
195-
/// Asynchronously enumerates all the remaining values of the current result set of a data reader.
196-
/// DBNull values are left unchanged (retained).
197-
/// </summary>
198-
/// <param name="reader">The reader to enumerate.</param>
199-
/// <param name="ordinals">The limited set of ordinals to include. If none are specified, the returned objects will be empty.</param>
200-
/// <returns>The QueryResult that contains a buffer block of the results and the column mappings.</returns>
201-
public static Task<QueryResult<Queue<object[]>>> RetrieveAsync(this DbDataReader reader, IEnumerable<int> ordinals)
202-
=> RetrieveAsyncInternal(reader, ordinals as int[] ?? ordinals.ToArray());
198+
/// <summary>
199+
/// Asynchronously enumerates all the remaining values of the current result set of a data reader.
200+
/// DBNull values are left unchanged (retained).
201+
/// </summary>
202+
/// <param name="reader">The reader to enumerate.</param>
203+
/// <param name="ordinals">The limited set of ordinals to include. If none are specified, the returned objects will be empty.</param>
204+
/// <param name="token">Optional cancellation token.</param>
205+
/// <returns>The QueryResult that contains a buffer block of the results and the column mappings.</returns>
206+
public static Task<QueryResult<Queue<object[]>>> RetrieveAsync(this DbDataReader reader, IEnumerable<int> ordinals, CancellationToken? token = null)
207+
=> RetrieveAsyncInternal(reader, token, ordinals as int[] ?? ordinals.ToArray());
203208

204209
/// <summary>
205210
/// Asynchronously enumerates all the remaining values of the current result set of a data reader.
@@ -212,21 +217,35 @@ public static Task<QueryResult<Queue<object[]>>> RetrieveAsync(this DbDataReader
212217
public static Task<QueryResult<Queue<object[]>>> RetrieveAsync(this DbDataReader reader, int n, params int[] others)
213218
=> RetrieveAsync(reader, new int[1] { n }.Concat(others));
214219

215-
/// <summary>
216-
/// Asynchronously enumerates all records within the current result set using an IDataReader and returns the desired results.
217-
/// DBNull values are left unchanged (retained).
218-
/// </summary>
219-
/// <param name="reader">The IDataReader to read results from.</param>
220-
/// <param name="columnNames">The column names to select.</param>
221-
/// <param name="normalizeColumnOrder">Orders the results arrays by ordinal.</param>
222-
/// <returns>The QueryResult that contains all the results and the column mappings.</returns>
223-
public static Task<QueryResult<Queue<object[]>>> RetrieveAsync(this DbDataReader reader, IEnumerable<string> columnNames, bool normalizeColumnOrder = false)
220+
/// <summary>
221+
/// Asynchronously enumerates all the remaining values of the current result set of a data reader.
222+
/// DBNull values are left unchanged (retained).
223+
/// </summary>
224+
/// <param name="reader">The reader to enumerate.</param>
225+
/// <param name="n">The first ordinal to include in the request to the reader for each record.</param>
226+
/// <param name="token">A cancellation token.</param>
227+
/// <param name="others">The remaining ordinals to request from the reader for each record.</param>
228+
/// <returns>The QueryResult that contains a buffer block of the results and the column mappings.</returns>
229+
public static Task<QueryResult<Queue<object[]>>> RetrieveAsync(this DbDataReader reader, CancellationToken token, int n, params int[] others)
230+
=> RetrieveAsync(reader, new int[1] { n }.Concat(others), token);
231+
232+
/// <summary>
233+
/// Asynchronously enumerates all records within the current result set using an IDataReader and returns the desired results.
234+
/// DBNull values are left unchanged (retained).
235+
/// </summary>
236+
/// <param name="reader">The IDataReader to read results from.</param>
237+
/// <param name="columnNames">The column names to select.</param>
238+
/// <param name="normalizeColumnOrder">Orders the results arrays by ordinal.</param>
239+
/// <param name="token">Optional cancellation token.</param>
240+
/// <returns>The QueryResult that contains all the results and the column mappings.</returns>
241+
public static Task<QueryResult<Queue<object[]>>> RetrieveAsync(this DbDataReader reader, IEnumerable<string> columnNames, bool normalizeColumnOrder = false, CancellationToken? token = null)
224242
{
225243
// Validate columns first.
226244
var columns = reader.GetOrdinalMapping(columnNames, normalizeColumnOrder);
227245

228-
return RetrieveAsyncInternal(reader,
229-
columns.Select(c => c.Ordinal).ToArray(),
246+
return RetrieveAsyncInternal(reader, token,
247+
248+
columns.Select(c => c.Ordinal).ToArray(),
230249
columns.Select(c => c.Name).ToArray());
231250
}
232251

@@ -241,5 +260,16 @@ public static Task<QueryResult<Queue<object[]>>> RetrieveAsync(this DbDataReader
241260
public static Task<QueryResult<Queue<object[]>>> RetrieveAsync(this DbDataReader reader, string c, params string[] others)
242261
=> RetrieveAsync(reader, new string[1] { c }.Concat(others));
243262

244-
}
263+
/// <summary>
264+
/// Asynchronously enumerates all records within the current result set using an IDataReader and returns the desired results.
265+
/// DBNull values are left unchanged (retained).
266+
/// </summary>
267+
/// <param name="reader">The IDataReader to read results from.</param>
268+
/// <param name="token">Optional cancellation token.</param>
269+
/// <param name="c">The first column name to include in the request to the reader for each record.</param>
270+
/// <param name="others">The remaining column names to request from the reader for each record.</param>
271+
/// <returns>The QueryResult that contains all the results and the column mappings.</returns>
272+
public static Task<QueryResult<Queue<object[]>>> RetrieveAsync(this DbDataReader reader, CancellationToken token, string c, params string[] others)
273+
=> RetrieveAsync(reader, new string[1] { c }.Concat(others), false, token);
274+
}
245275
}

0 commit comments

Comments
 (0)