Skip to content

Commit eb4348d

Browse files
committed
Make IQueryExpressionInterceptor an ISingletonInterceptor
Fixes #36127
1 parent 7a337e9 commit eb4348d

File tree

2 files changed

+48
-35
lines changed

2 files changed

+48
-35
lines changed

src/EFCore/Diagnostics/IQueryExpressionInterceptor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace Microsoft.EntityFrameworkCore.Diagnostics;
2020
/// See <see href="https://aka.ms/efcore-docs-interceptors">EF Core interceptors</see> for more information and examples.
2121
/// </para>
2222
/// </remarks>
23-
public interface IQueryExpressionInterceptor : IInterceptor
23+
public interface IQueryExpressionInterceptor : ISingletonInterceptor
2424
{
2525
/// <summary>
2626
/// Called with the LINQ expression tree for a query before it is compiled.

test/EFCore.Specification.Tests/QueryExpressionInterceptionTestBase.cs

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,55 +9,43 @@ public abstract class QueryExpressionInterceptionTestBase(InterceptionTestBase.I
99
: InterceptionTestBase(fixture)
1010
{
1111
[ConditionalTheory]
12-
[InlineData(false, false)]
13-
[InlineData(true, false)]
14-
[InlineData(false, true)]
15-
[InlineData(true, true)]
16-
public virtual async Task Intercept_query_passively(bool async, bool inject)
12+
[MemberData(nameof(IsAsyncData))]
13+
public virtual async Task Intercept_query_passively(bool async)
1714
{
18-
var (context, interceptor) = await CreateContextAsync<TestQueryExpressionInterceptor>(inject);
15+
var (context, interceptor) = await CreateContextAsync<TestQueryExpressionInterceptor>(inject: true);
1916

2017
using var _ = context;
2118

2219
var query = context.Set<Singularity>().Where(e => e.Type == "Black Hole");
23-
var results = async ? await query.ToListAsync() : query.ToList();
20+
var result = async ? await query.SingleAsync() : query.Single();
2421

25-
Assert.Single(results);
26-
Assert.Equal("Black Hole", results[0].Type);
22+
Assert.Equal("Black Hole", result.Type);
2723

2824
AssertNormalOutcome(context, interceptor);
2925

30-
Assert.Contains(@".Where(e => e.Type == ""Black Hole"")", interceptor.QueryExpression);
26+
Assert.Contains(""".Where(e => e.Type == "Black Hole")""", interceptor.QueryExpression);
3127
}
3228

3329
[ConditionalTheory]
34-
[InlineData(false, false)]
35-
[InlineData(true, false)]
36-
[InlineData(false, true)]
37-
[InlineData(true, true)]
38-
public virtual async Task Intercept_query_with_multiple_interceptors(bool async, bool inject)
30+
[MemberData(nameof(IsAsyncData))]
31+
public virtual async Task Intercept_query_with_multiple_interceptors(bool async)
3932
{
4033
var interceptor1 = new TestQueryExpressionInterceptor();
4134
var interceptor2 = new QueryChangingExpressionInterceptor();
42-
var interceptor3 = new TestQueryExpressionInterceptor();
43-
var interceptor4 = new TestQueryExpressionInterceptor();
4435

4536
using var context = await CreateContextAsync(
46-
new IInterceptor[] { new TestQueryExpressionInterceptor(), interceptor1, interceptor2 },
47-
new IInterceptor[] { interceptor3, interceptor4, new TestQueryExpressionInterceptor() });
37+
appInterceptor: null,
38+
[interceptor1, interceptor2]);
4839

4940
using var listener = Fixture.SubscribeToDiagnosticListener(context.ContextId);
5041

5142
var query = context.Set<Singularity>().Where(e => e.Type == "Bing Bang");
52-
var results = async ? await query.ToListAsync() : query.ToList();
43+
var result = async ? await query.SingleAsync() : query.Single();
5344

54-
Assert.Single(results);
55-
Assert.Equal("Bing Bang", results[0].Type);
45+
Assert.Equal("Bing Bang", result.Type);
5646

5747
AssertNormalOutcome(context, interceptor1);
5848
AssertNormalOutcome(context, interceptor2);
59-
AssertNormalOutcome(context, interceptor3);
60-
AssertNormalOutcome(context, interceptor4);
6149

6250
listener.AssertEventsInOrder(
6351
CoreEventId.QueryCompilationStarting.Name,
@@ -67,25 +55,48 @@ public virtual async Task Intercept_query_with_multiple_interceptors(bool async,
6755
}
6856

6957
[ConditionalTheory]
70-
[InlineData(false, false)]
71-
[InlineData(true, false)]
72-
[InlineData(false, true)]
73-
[InlineData(true, true)]
74-
public virtual async Task Intercept_to_change_query_expression(bool async, bool inject)
58+
[MemberData(nameof(IsAsyncData))]
59+
public virtual async Task Intercept_to_change_query_expression(bool async)
7560
{
76-
var (context, interceptor) = await CreateContextAsync<QueryChangingExpressionInterceptor>(inject);
61+
var (context, interceptor) = await CreateContextAsync<QueryChangingExpressionInterceptor>(inject: true);
7762

7863
using var _ = context;
7964

8065
var query = context.Set<Singularity>().Where(e => e.Type == "Black Hole");
81-
var results = async ? await query.ToListAsync() : query.ToList();
66+
var result = async ? await query.SingleAsync() : query.Single();
8267

83-
Assert.Single(results);
84-
Assert.Equal("Bing Bang", results[0].Type);
68+
Assert.Equal("Bing Bang", result.Type);
8569

8670
AssertNormalOutcome(context, interceptor);
8771

88-
Assert.Contains(@".Where(e => e.Type == ""Bing Bang"")", interceptor.QueryExpression);
72+
Assert.Contains(""".Where(e => e.Type == "Bing Bang")""", interceptor.QueryExpression);
73+
}
74+
75+
[ConditionalTheory]
76+
[MemberData(nameof(IsAsyncData))]
77+
public virtual async Task Interceptor_does_not_leak_across_contexts(bool async)
78+
{
79+
// Create one context with QueryChangingExpressionInterceptor, and another with TestQueryExpressionInterceptor (which is a no-op).
80+
// Note that we don't use the regular suite infra for creating the contexts, as that creates separate service providers for each
81+
// one, but that's exactly what we want to test here.
82+
using var context1 = new UniverseContext(
83+
Fixture.AddOptions(
84+
Fixture.TestStore.AddProviderOptions(
85+
new DbContextOptionsBuilder<DbContext>().AddInterceptors(new QueryChangingExpressionInterceptor())))
86+
.Options);
87+
using var context2 = new UniverseContext(
88+
Fixture.AddOptions(
89+
Fixture.TestStore.AddProviderOptions(
90+
new DbContextOptionsBuilder<DbContext>().AddInterceptors(new TestQueryExpressionInterceptor())))
91+
.Options);
92+
93+
var query1 = context1.Set<Singularity>().Where(e => e.Type == "Black Hole");
94+
var result1 = async ? await query1.SingleAsync() : query1.Single();
95+
Assert.Equal("Bing Bang", result1.Type);
96+
97+
var query2 = context2.Set<Singularity>().Where(e => e.Type == "Black Hole");
98+
var result2 = async ? await query2.SingleAsync() : query2.Single();
99+
Assert.Equal("Black Hole", result2.Type);
89100
}
90101

91102
protected class QueryChangingExpressionInterceptor : TestQueryExpressionInterceptor
@@ -128,4 +139,6 @@ public virtual Expression QueryCompilationStarting(
128139
return queryExpression;
129140
}
130141
}
142+
143+
public static readonly IEnumerable<object[]> IsAsyncData = [[false], [true]];
131144
}

0 commit comments

Comments
 (0)