Skip to content

Commit 22e4f25

Browse files
committed
Prevent disposing of enumerators returned by Statement.Query from disposing the underlying Statement.
1 parent 9275a12 commit 22e4f25

2 files changed

Lines changed: 55 additions & 2 deletions

File tree

SQLitePCL.pretty.tests/ImplementationTests.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,12 +357,18 @@ public void TestQuery()
357357
using (var stmt = db.PrepareStatement("SELECT * from FOO WHERE v < ?"))
358358
{
359359
var result = stmt.Query(50).Count();
360+
361+
// Ensure that enumerating the Query Enumerable doesn't dispose the stmt
362+
Assert.DoesNotThrow(() => { var x = stmt.IsBusy; });
360363
Assert.AreEqual(result, 50);
361364
}
362365

363366
using (var stmt = db.PrepareStatement("SELECT * from FOO WHERE v < 50"))
364367
{
365368
var result = stmt.Query().Count();
369+
370+
// Ensure that enumerating the Query Enumerable doesn't dispose the stmt
371+
Assert.DoesNotThrow(() => { var x = stmt.IsBusy; });
366372
Assert.AreEqual(result, 50);
367373
}
368374
}

SQLitePCL.pretty/Statement.cs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ limitations under the License.
1616
*/
1717

1818
using System;
19+
using System.Collections;
1920
using System.Collections.Generic;
2021
using System.Diagnostics.Contracts;
2122
using System.IO;
@@ -111,7 +112,9 @@ public static IEnumerable<IReadOnlyList<IResultSetValue>> Query(
111112
This.Reset();
112113
This.ClearBindings();
113114
This.Bind(values);
114-
return This;
115+
116+
// Prevent the statement from being disposed when the enumerator is disposed
117+
return new NonDisposingEnumerator<IReadOnlyList<IResultSetValue>>(This);
115118
});
116119
}
117120

@@ -127,8 +130,52 @@ public static IEnumerable<IReadOnlyList<IResultSetValue>> Query(this IStatement
127130
return new DelegatingEnumerable<IReadOnlyList<IResultSetValue>>(() =>
128131
{
129132
This.Reset();
130-
return This;
133+
134+
// Prevent the statement from being disposed when the enumerator is disposed
135+
return new NonDisposingEnumerator<IReadOnlyList<IResultSetValue>>(This);
131136
});
132137
}
133138
}
139+
140+
// An IEnumerator that wraps a delegate, and prevents unintentional disposing of the delegate
141+
internal sealed class NonDisposingEnumerator<T> : IEnumerator<T>
142+
{
143+
private readonly IEnumerator<T> deleg;
144+
145+
internal NonDisposingEnumerator(IEnumerator<T> deleg)
146+
{
147+
this.deleg = deleg;
148+
}
149+
150+
public void Dispose()
151+
{
152+
// Intentionally left blank
153+
}
154+
155+
public bool MoveNext()
156+
{
157+
return deleg.MoveNext();
158+
}
159+
160+
public void Reset()
161+
{
162+
deleg.Reset();
163+
}
164+
165+
object IEnumerator.Current
166+
{
167+
get
168+
{
169+
return deleg.Current;
170+
}
171+
}
172+
173+
public T Current
174+
{
175+
get
176+
{
177+
return deleg.Current;
178+
}
179+
}
180+
}
134181
}

0 commit comments

Comments
 (0)