@@ -96,6 +96,76 @@ public void Can_update_an_existing_extension()
9696 Assert . Same ( extension2 , optionsBuilder . Options . FindExtension < FakeDbContextOptionsExtension1 > ( ) ) ;
9797 }
9898
99+ [ ConditionalFact ]
100+ public void Can_remove_an_existing_extension ( )
101+ {
102+ var optionsBuilder = new DbContextOptionsBuilder ( ) ;
103+
104+ var extension1 = new FakeDbContextOptionsExtension1 ( ) ;
105+ var extension2 = new FakeDbContextOptionsExtension2 ( ) ;
106+
107+ ( ( IDbContextOptionsBuilderInfrastructure ) optionsBuilder ) . AddOrUpdateExtension ( extension1 ) ;
108+ ( ( IDbContextOptionsBuilderInfrastructure ) optionsBuilder ) . AddOrUpdateExtension ( extension2 ) ;
109+
110+ Assert . Equal ( 2 , optionsBuilder . Options . Extensions . Count ( ) ) ;
111+
112+ ( ( IDbContextOptionsBuilderInfrastructure ) optionsBuilder ) . RemoveExtension < FakeDbContextOptionsExtension1 > ( ) ;
113+
114+ Assert . Single ( optionsBuilder . Options . Extensions ) ;
115+ Assert . Null ( optionsBuilder . Options . FindExtension < FakeDbContextOptionsExtension1 > ( ) ) ;
116+ Assert . Same ( extension2 , optionsBuilder . Options . FindExtension < FakeDbContextOptionsExtension2 > ( ) ) ;
117+ }
118+
119+ [ ConditionalFact ]
120+ public void Removing_non_existent_extension_is_no_op ( )
121+ {
122+ var optionsBuilder = new DbContextOptionsBuilder ( ) ;
123+
124+ var extension = new FakeDbContextOptionsExtension1 ( ) ;
125+ ( ( IDbContextOptionsBuilderInfrastructure ) optionsBuilder ) . AddOrUpdateExtension ( extension ) ;
126+
127+ Assert . Single ( optionsBuilder . Options . Extensions ) ;
128+
129+ ( ( IDbContextOptionsBuilderInfrastructure ) optionsBuilder ) . RemoveExtension < FakeDbContextOptionsExtension2 > ( ) ;
130+
131+ Assert . Single ( optionsBuilder . Options . Extensions ) ;
132+ Assert . Same ( extension , optionsBuilder . Options . FindExtension < FakeDbContextOptionsExtension1 > ( ) ) ;
133+ }
134+
135+ [ ConditionalFact ]
136+ public void Removing_extension_from_middle_renormalizes_ordinals_and_preserves_insertion_order ( )
137+ {
138+ var optionsBuilder = new DbContextOptionsBuilder ( ) ;
139+
140+ var extension1 = new FakeDbContextOptionsExtension1 ( ) ;
141+ var extension2 = new FakeDbContextOptionsExtension2 ( ) ;
142+ var extension3 = new FakeDbContextOptionsExtension3 ( ) ;
143+
144+ ( ( IDbContextOptionsBuilderInfrastructure ) optionsBuilder ) . AddOrUpdateExtension ( extension1 ) ;
145+ ( ( IDbContextOptionsBuilderInfrastructure ) optionsBuilder ) . AddOrUpdateExtension ( extension2 ) ;
146+ ( ( IDbContextOptionsBuilderInfrastructure ) optionsBuilder ) . AddOrUpdateExtension ( extension3 ) ;
147+
148+ Assert . Equal ( 3 , optionsBuilder . Options . Extensions . Count ( ) ) ;
149+
150+ // Remove the middle extension
151+ ( ( IDbContextOptionsBuilderInfrastructure ) optionsBuilder ) . RemoveExtension < FakeDbContextOptionsExtension2 > ( ) ;
152+
153+ Assert . Equal ( 2 , optionsBuilder . Options . Extensions . Count ( ) ) ;
154+ var extensionsList = optionsBuilder . Options . Extensions . ToList ( ) ;
155+ Assert . Same ( extension1 , extensionsList [ 0 ] ) ;
156+ Assert . Same ( extension3 , extensionsList [ 1 ] ) ;
157+
158+ // Add a new extension after removing the middle one - ordinals should stay contiguous
159+ var extension2New = new FakeDbContextOptionsExtension2 ( ) ;
160+ ( ( IDbContextOptionsBuilderInfrastructure ) optionsBuilder ) . AddOrUpdateExtension ( extension2New ) ;
161+
162+ Assert . Equal ( 3 , optionsBuilder . Options . Extensions . Count ( ) ) ;
163+ extensionsList = optionsBuilder . Options . Extensions . ToList ( ) ;
164+ Assert . Same ( extension1 , extensionsList [ 0 ] ) ;
165+ Assert . Same ( extension3 , extensionsList [ 1 ] ) ;
166+ Assert . Same ( extension2New , extensionsList [ 2 ] ) ;
167+ }
168+
99169 [ ConditionalFact ]
100170 public void IsConfigured_returns_true_if_any_provider_extensions_have_been_added ( )
101171 {
@@ -199,6 +269,42 @@ public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
199269 }
200270 }
201271
272+ private class FakeDbContextOptionsExtension3 : IDbContextOptionsExtension
273+ {
274+ private DbContextOptionsExtensionInfo _info ;
275+
276+ public DbContextOptionsExtensionInfo Info
277+ => _info ??= new ExtensionInfo ( this ) ;
278+
279+ public bool AppliedServices { get ; private set ; }
280+
281+ public virtual void ApplyServices ( IServiceCollection services )
282+ => AppliedServices = true ;
283+
284+ public virtual void Validate ( IDbContextOptions options )
285+ {
286+ }
287+
288+ private sealed class ExtensionInfo ( IDbContextOptionsExtension extension ) : DbContextOptionsExtensionInfo ( extension )
289+ {
290+ public override bool IsDatabaseProvider
291+ => false ;
292+
293+ public override int GetServiceProviderHashCode ( )
294+ => 0 ;
295+
296+ public override bool ShouldUseSameServiceProvider ( DbContextOptionsExtensionInfo other )
297+ => true ;
298+
299+ public override string LogFragment
300+ => "" ;
301+
302+ public override void PopulateDebugInfo ( IDictionary < string , string > debugInfo )
303+ {
304+ }
305+ }
306+ }
307+
202308 [ ConditionalFact ]
203309 public void UseModel_on_generic_builder_returns_generic_builder ( )
204310 {
0 commit comments