-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathDbCommandEnumerable.cs
More file actions
115 lines (101 loc) · 3.22 KB
/
Copy pathDbCommandEnumerable.cs
File metadata and controls
115 lines (101 loc) · 3.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace DataBoss.Data
{
public class DbCommandEnumerable<TCommand, TReader, T> : IEnumerable<T>
where TCommand : IDbCommand
where TReader : IDataReader
{
static T NoRowsReturned() => throw new InvalidOperationException("No rows returned.");
readonly Func<TCommand> getCommand;
readonly Func<TCommand, TReader> executeReader;
readonly Func<TReader, object, Func<TReader, T>> converterFactory;
readonly object factoryState;
public DbCommandEnumerable(Func<TCommand> getCommand, Func<TCommand, TReader> executeReader, Func<TReader, object, Func<TReader, T>> converterFactory, object factoryState) {
this.getCommand = getCommand;
this.executeReader = executeReader;
this.converterFactory = converterFactory;
this.factoryState = factoryState;
}
public List<T> ToList(RetryStrategy retry) =>
retry.Execute(() => new List<T>(this));
public T Single(RetryStrategy retry) => SingleCore(retry, NoRowsReturned);
public T SingleOrDefault(RetryStrategy retry) => SingleCore(retry, Lambdas.Default<T>);
T SingleCore(RetryStrategy retry, Func<T> handleDefault) {
for(var n = 1;; ++n) {
var it = GetEnumerator();
try {
if(!it.MoveNext())
goto NoRow;
var r = it.Current;
if(it.MoveNext())
goto TooManyRows;
return r;
} catch(Exception e) {
if(!retry(n, e))
throw;
} finally {
it.Dispose();
}
}
NoRow: return handleDefault();
TooManyRows: throw new InvalidOperationException("More than one result row.");
}
public IEnumerator<T> GetEnumerator() {
var q = getCommand();
if (string.IsNullOrEmpty(q.CommandText))
return Enumerable.Empty<T>().GetEnumerator();
var e = new DbReaderEnumerator(q, this);
e.Reset();
return e;
}
Func<TReader, T> CreateMaterializer(TReader reader) => converterFactory(reader, factoryState);
TReader ExecuteReader(TCommand command) => executeReader(command);
class DbReaderEnumerator : IEnumerator<T>
{
readonly DbCommandEnumerable<TCommand, TReader, T> parent;
readonly TCommand command;
Func<TReader, T> materialize;
TReader reader;
public DbReaderEnumerator(TCommand command, DbCommandEnumerable<TCommand, TReader, T> parent) {
this.parent = parent;
this.command = command;
}
public T Current => materialize(reader);
object IEnumerator.Current => Current;
public void Dispose() {
if(reader != null) {
command.Cancel();
reader.Dispose();
}
command.Dispose();
}
public bool MoveNext() {
if(reader == null)
return false;
read: if(reader.Read())
return true;
if(reader.NextResult()) {
materialize = parent.CreateMaterializer(reader);
goto read;
}
reader.Dispose();
reader = default(TReader);
return false;
}
public void Reset() {
reader = parent.ExecuteReader(command);
try {
materialize = parent.CreateMaterializer(reader);
} catch {
reader.Dispose();
throw;
}
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}