Skip to content

Commit 4bb67a7

Browse files
authored
Clear reference to DbDataReader from RelationalDataReader (#28989)
Fixes #28988
1 parent d453d3f commit 4bb67a7

File tree

2 files changed

+102
-10
lines changed

2 files changed

+102
-10
lines changed

src/EFCore.Relational/Storage/RelationalDataReader.cs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,22 @@ public virtual void Dispose()
173173
{
174174
_disposed = true;
175175

176-
if (!interceptionResult.IsSuppressed)
176+
try
177177
{
178-
_reader.Dispose();
179-
_command.Parameters.Clear();
180-
_command.Dispose();
181-
_relationalConnection.Close();
178+
if (!interceptionResult.IsSuppressed)
179+
{
180+
_reader.Dispose();
181+
_command.Parameters.Clear();
182+
_command.Dispose();
183+
_relationalConnection.Close();
184+
}
185+
}
186+
finally
187+
{
188+
_reader = null!;
189+
_command = null!;
190+
_relationalConnection = null!;
191+
_logger = null;
182192
}
183193
}
184194
}
@@ -259,12 +269,22 @@ public virtual async ValueTask DisposeAsync()
259269
{
260270
_disposed = true;
261271

262-
if (!interceptionResult.IsSuppressed)
272+
try
273+
{
274+
if (!interceptionResult.IsSuppressed)
275+
{
276+
await _reader.DisposeAsync().ConfigureAwait(false);
277+
_command.Parameters.Clear();
278+
await _command.DisposeAsync().ConfigureAwait(false);
279+
await _relationalConnection.CloseAsync().ConfigureAwait(false);
280+
}
281+
}
282+
finally
263283
{
264-
await _reader.DisposeAsync().ConfigureAwait(false);
265-
_command.Parameters.Clear();
266-
await _command.DisposeAsync().ConfigureAwait(false);
267-
await _relationalConnection.CloseAsync().ConfigureAwait(false);
284+
_reader = null!;
285+
_command = null!;
286+
_relationalConnection = null!;
287+
_logger = null;
268288
}
269289
}
270290
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.EntityFrameworkCore.Storage.Internal;
5+
using Microsoft.EntityFrameworkCore.TestUtilities.FakeProvider;
6+
7+
// ReSharper disable MethodHasAsyncOverload
8+
9+
namespace Microsoft.EntityFrameworkCore.Storage;
10+
11+
public class RelationalDataReaderTest
12+
{
13+
[ConditionalTheory]
14+
[MemberData(nameof(IsAsyncData))]
15+
public async Task Does_not_hold_reference_to_DbDataReader_after_dispose(bool async)
16+
{
17+
var fakeConnection = CreateConnection();
18+
var relationalCommand = CreateRelationalCommand(commandText: "CommandText");
19+
20+
var reader = relationalCommand.ExecuteReader(new(
21+
fakeConnection,
22+
new Dictionary<string, object>(),
23+
readerColumns: null,
24+
context: null,
25+
logger: null));
26+
27+
Assert.NotNull(reader.DbDataReader);
28+
29+
if (async)
30+
{
31+
await reader.DisposeAsync();
32+
}
33+
else
34+
{
35+
reader.Dispose();
36+
}
37+
38+
Assert.Null(reader.DbDataReader);
39+
}
40+
41+
private const string ConnectionString = "Fake Connection String";
42+
43+
private static FakeRelationalConnection CreateConnection(IDbContextOptions options = null)
44+
=> new(options ?? CreateOptions());
45+
46+
private static IDbContextOptions CreateOptions(
47+
RelationalOptionsExtension optionsExtension = null)
48+
{
49+
var optionsBuilder = new DbContextOptionsBuilder();
50+
51+
((IDbContextOptionsBuilderInfrastructure)optionsBuilder)
52+
.AddOrUpdateExtension(
53+
optionsExtension
54+
?? new FakeRelationalOptionsExtension().WithConnectionString(ConnectionString));
55+
56+
return optionsBuilder.Options;
57+
}
58+
59+
private IRelationalCommand CreateRelationalCommand(
60+
string commandText = "Command Text",
61+
IReadOnlyList<IRelationalParameter> parameters = null)
62+
=> new RelationalCommand(
63+
new RelationalCommandBuilderDependencies(
64+
new TestRelationalTypeMappingSource(
65+
TestServiceFactory.Instance.Create<TypeMappingSourceDependencies>(),
66+
TestServiceFactory.Instance.Create<RelationalTypeMappingSourceDependencies>()),
67+
new ExceptionDetector()),
68+
commandText,
69+
parameters ?? Array.Empty<IRelationalParameter>());
70+
71+
public static IEnumerable<object[]> IsAsyncData = new[] { new object[] { false }, new object[] { true } };
72+
}

0 commit comments

Comments
 (0)