Skip to content

Commit 3b7e122

Browse files
committed
Optimizations and edge-case fixes to DataReaderEnumerable
1 parent dca487a commit 3b7e122

9 files changed

Lines changed: 223 additions & 118 deletions

File tree

Simple.Data.Ado/AdoAdapter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,11 +275,11 @@ out IEnumerable<SimpleQueryClauseBase>
275275
return
276276
CommandBuilder.CreateCommand(
277277
_providerHelper.GetCustomProvider<IDbParameterFactory>(_schema.SchemaProvider), commandBuilders,
278-
connection).ToEnumerable(connection);
278+
connection).ToEnumerable(this.CreateConnection);
279279
}
280280
else
281281
{
282-
return commandBuilders.SelectMany(cb => cb.GetCommand(connection).ToEnumerable(connection));
282+
return commandBuilders.SelectMany(cb => cb.GetCommand(connection).ToEnumerable(this.CreateConnection));
283283
}
284284
}
285285

Simple.Data.Ado/AdoAdapterFinder.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,30 +128,42 @@ private IEnumerable<IDictionary<string, object>> ExecuteQuery(string sql, params
128128
return TryExecuteQuery(connection, command);
129129
}
130130

131-
private static IEnumerable<IDictionary<string, object>> TryExecuteQuery(IDbConnection connection, IDbCommand command)
131+
private IEnumerable<IDictionary<string, object>> TryExecuteQuery(IDbConnection connection, IDbCommand command)
132132
{
133133
try
134134
{
135-
return command.ToEnumerable(connection);
135+
return command.ToEnumerable(ConnectionCreator);
136136
}
137137
catch (DbException ex)
138138
{
139139
throw new AdoAdapterException(ex.Message, command);
140140
}
141141
}
142142

143-
private static IEnumerable<IDictionary<string, object>> TryExecuteQuery(IDbConnection connection, IDbCommand command, IDictionary<string, int> index)
143+
private IEnumerable<IDictionary<string, object>> TryExecuteQuery(IDbConnection connection, IDbCommand command, IDictionary<string, int> index)
144144
{
145145
try
146146
{
147-
return command.ToEnumerable(connection, index);
147+
return command.ToEnumerable(ConnectionCreator);
148148
}
149149
catch (DbException ex)
150150
{
151151
throw new AdoAdapterException(ex.Message, command);
152152
}
153153
}
154154

155+
private Func<IDbConnection> ConnectionCreator
156+
{
157+
get
158+
{
159+
if (_transaction != null)
160+
{
161+
return () => _transaction.Connection;
162+
}
163+
return _adapter.CreateConnection;
164+
}
165+
}
166+
155167
private static IDictionary<string, object> TryExecuteSingletonQuery(IDbConnection connection, IDbCommand command, IDictionary<string, int> index)
156168
{
157169
command.WriteTrace();

Simple.Data.Ado/AdoAdapterGetter.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,30 +125,42 @@ private IEnumerable<IDictionary<string, object>> ExecuteQuery(string sql, params
125125
return TryExecuteQuery(connection, command);
126126
}
127127

128-
private static IEnumerable<IDictionary<string, object>> TryExecuteQuery(IDbConnection connection, IDbCommand command)
128+
private IEnumerable<IDictionary<string, object>> TryExecuteQuery(IDbConnection connection, IDbCommand command)
129129
{
130130
try
131131
{
132-
return command.ToEnumerable(connection);
132+
return command.ToEnumerable(ConnectionCreator);
133133
}
134134
catch (DbException ex)
135135
{
136136
throw new AdoAdapterException(ex.Message, command);
137137
}
138138
}
139139

140-
private static IEnumerable<IDictionary<string, object>> TryExecuteQuery(IDbConnection connection, IDbCommand command, IDictionary<string, int> index)
140+
private IEnumerable<IDictionary<string, object>> TryExecuteQuery(IDbConnection connection, IDbCommand command, IDictionary<string, int> index)
141141
{
142142
try
143143
{
144-
return command.ToEnumerable(connection, index);
144+
return command.ToEnumerable(ConnectionCreator, index);
145145
}
146146
catch (DbException ex)
147147
{
148148
throw new AdoAdapterException(ex.Message, command);
149149
}
150150
}
151151

152+
private Func<IDbConnection> ConnectionCreator
153+
{
154+
get
155+
{
156+
if (_transaction != null)
157+
{
158+
return () => _transaction.Connection;
159+
}
160+
return _adapter.CreateConnection;
161+
}
162+
}
163+
152164
private static IDictionary<string, object> TryExecuteSingletonQuery(IDbConnection connection, IDbCommand command, IDictionary<string, int> index)
153165
{
154166
command.WriteTrace();
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
6+
namespace Simple.Data.Ado
7+
{
8+
using System.Collections;
9+
using System.Data;
10+
using System.Data.Common;
11+
using System.Threading;
12+
13+
internal class DataReaderEnumerable : IEnumerable<IDictionary<string, object>>
14+
{
15+
private IEnumerable<IDictionary<string, object>> _cache;
16+
private IDictionary<string, int> _index;
17+
private readonly IDbCommand _command;
18+
private readonly Func<IDbConnection> _createConnection;
19+
20+
public DataReaderEnumerable(IDbCommand command, Func<IDbConnection> createConnection)
21+
: this(command, createConnection, null)
22+
{
23+
}
24+
25+
public DataReaderEnumerable(IDbCommand command, Func<IDbConnection> createConnection, IDictionary<string, int> index)
26+
{
27+
_command = command;
28+
_createConnection = createConnection;
29+
_index = index;
30+
}
31+
32+
public IEnumerator<IDictionary<string, object>> GetEnumerator()
33+
{
34+
if (_cache != null) return _cache.GetEnumerator();
35+
36+
IDbCommand command;
37+
38+
var clonable = _command as ICloneable;
39+
if (clonable != null)
40+
{
41+
command = (IDbCommand) clonable.Clone();
42+
command.Connection = _createConnection();
43+
}
44+
else
45+
{
46+
command = _command;
47+
}
48+
49+
return new DataReaderEnumerator(command, _index, Cache, CacheIndex);
50+
}
51+
52+
private void Cache(IEnumerable<IDictionary<string,object>> cache)
53+
{
54+
Interlocked.CompareExchange(ref _cache, cache, null);
55+
}
56+
57+
private void CacheIndex(IDictionary<string,int> index)
58+
{
59+
Interlocked.CompareExchange(ref _index, index, null);
60+
}
61+
62+
IEnumerator IEnumerable.GetEnumerator()
63+
{
64+
return GetEnumerator();
65+
}
66+
67+
private class DataReaderEnumerator : IEnumerator<IDictionary<string, object>>
68+
{
69+
private readonly IDisposable _connectionDisposable;
70+
private IDictionary<string, int> _index;
71+
private readonly IDbCommand _command;
72+
private IList<IDictionary<string,object>> _cache = new List<IDictionary<string, object>>();
73+
private readonly Action<IEnumerable<IDictionary<string, object>>> _cacheAction;
74+
private readonly Action<IDictionary<string, int>> _cacheIndexAction;
75+
private IDataReader _reader;
76+
private IDictionary<string, object> _current;
77+
78+
public DataReaderEnumerator(IDbCommand command, IDictionary<string, int> index, Action<IEnumerable<IDictionary<string, object>>> cacheAction, Action<IDictionary<string, int>> cacheIndexAction)
79+
{
80+
_command = command;
81+
_cacheAction = cacheAction;
82+
_cacheIndexAction = cacheIndexAction;
83+
_connectionDisposable = _command.Connection.MaybeDisposable();
84+
_index = index;
85+
}
86+
87+
public void Dispose()
88+
{
89+
using (_connectionDisposable)
90+
using (_command)
91+
using (_reader)
92+
{
93+
/* NO-OP */
94+
}
95+
}
96+
97+
public bool MoveNext()
98+
{
99+
if (_reader == null)
100+
{
101+
ExecuteReader();
102+
if (_reader == null) return false;
103+
}
104+
105+
return _reader.Read() ? SetCurrent() : EndRead();
106+
}
107+
108+
private bool SetCurrent()
109+
{
110+
_current = _reader.ToDictionary(_index);
111+
if (_cache.Count < 100)
112+
{
113+
_cache.Add(_current);
114+
}
115+
else
116+
{
117+
// We don't want to cache more than 100 rows, too much memory getting used.
118+
_cache = null;
119+
}
120+
121+
return true;
122+
}
123+
124+
private bool EndRead()
125+
{
126+
_current = null;
127+
128+
// When reader is done, cache the results to the DataReaderEnumerable.
129+
if (_cache != null)
130+
{
131+
_cacheAction(_cache);
132+
}
133+
134+
return false;
135+
}
136+
137+
private void ExecuteReader()
138+
{
139+
try
140+
{
141+
_command.Connection.OpenIfClosed();
142+
_reader = _command.ExecuteReader();
143+
if (_reader != null && _index == null)
144+
{
145+
_index = _reader.CreateDictionaryIndex();
146+
_cacheIndexAction(_index);
147+
}
148+
}
149+
catch (DbException ex)
150+
{
151+
throw new AdoAdapterException(ex.Message, ex);
152+
}
153+
}
154+
155+
public void Reset()
156+
{
157+
if (_reader != null) _reader.Dispose();
158+
_cache.Clear();
159+
ExecuteReader();
160+
}
161+
162+
public IDictionary<string, object> Current
163+
{
164+
get
165+
{
166+
if (_current == null) throw new InvalidOperationException();
167+
return _current;
168+
}
169+
}
170+
171+
object IEnumerator.Current
172+
{
173+
get { return Current; }
174+
}
175+
}
176+
}
177+
}

Simple.Data.Ado/DataReaderEnumerator.cs

Lines changed: 0 additions & 94 deletions
This file was deleted.

0 commit comments

Comments
 (0)