@@ -317,6 +317,10 @@ public override Task CleanAsync(DbContext context, bool createTables = true)
317317 private async Task CleanAsyncImpl ( DbContext context , bool createTables )
318318 {
319319 var created = await EnsureCreatedAsync ( context ) . ConfigureAwait ( false ) ;
320+
321+ // Containers are deleted and recreated below only when the database already existed. A freshly-created
322+ // database has brand-new containers whose metadata cannot be stale.
323+ var containersRecreated = ! created ;
320324 try
321325 {
322326 if ( ! created )
@@ -334,12 +338,23 @@ private async Task CleanAsyncImpl(DbContext context, bool createTables)
334338 created = await context . Database . EnsureCreatedAsync ( ) . ConfigureAwait ( false ) ;
335339 if ( ! created )
336340 {
341+ if ( containersRecreated )
342+ {
343+ await RefreshContainerMetadataAsync ( context ) . ConfigureAwait ( false ) ;
344+ }
345+
337346 await SeedAsync ( context ) . ConfigureAwait ( false ) ;
338347 }
339348 }
340349 else
341350 {
342351 await CreateContainersAsync ( context ) . ConfigureAwait ( false ) ;
352+
353+ if ( containersRecreated )
354+ {
355+ await RefreshContainerMetadataAsync ( context ) . ConfigureAwait ( false ) ;
356+ }
357+
343358 await SeedAsync ( context ) . ConfigureAwait ( false ) ;
344359 }
345360 }
@@ -357,6 +372,68 @@ private async Task CleanAsyncImpl(DbContext context, bool createTables)
357372 }
358373 }
359374
375+ // Deleting and recreating a container gives it a new resource id (_rid), but the shared CosmosClient caches each
376+ // container's _rid (and the partition key ranges keyed by it) by container name. The first operation on the
377+ // recreated container - whether it is the reseed below or the next test's first query - can then fail with
378+ // "NotFound (404) ... GetTargetPartitionKeyRanges ... failed due to stale cache", and the query pipeline does not
379+ // transparently refresh and retry. Prime the client's caches here, right after recreating the containers and before
380+ // any test touches them. Each attempt re-reads the container by name (refreshing the collection cache with the new
381+ // _rid) and then runs a query - the exact path that otherwise fails. Because the recreate can take a moment to
382+ // become consistent on the emulator gateway, retry with a short delay until a query succeeds. This is the single
383+ // place that handles the stale-metadata problem for the emulator; it is fully best-effort and must never fail the
384+ // clean, so all errors are ultimately swallowed.
385+ private async Task RefreshContainerMetadataAsync ( DbContext context )
386+ {
387+ if ( CosmosTestEnvironment . UseTokenCredential )
388+ {
389+ return ;
390+ }
391+
392+ const int maxAttempts = 10 ;
393+
394+ try
395+ {
396+ var cosmosClient = context . Database . GetCosmosClient ( ) ;
397+ var database = cosmosClient . GetDatabase ( Name ) ;
398+ var model = context . GetService < IDesignTimeModel > ( ) . Model ;
399+ var countQuery = new QueryDefinition ( "SELECT VALUE COUNT(1) FROM c" ) ;
400+
401+ foreach ( var containerProperties in GetContainersToCreate ( model ) )
402+ {
403+ var container = database . GetContainer ( containerProperties . Id ) ;
404+
405+ for ( var attempt = 1 ; attempt <= maxAttempts ; attempt ++ )
406+ {
407+ try
408+ {
409+ await container . ReadContainerAsync ( ) . ConfigureAwait ( false ) ;
410+
411+ using var iterator = container . GetItemQueryIterator < int > ( countQuery ) ;
412+ while ( iterator . HasMoreResults )
413+ {
414+ await iterator . ReadNextAsync ( ) . ConfigureAwait ( false ) ;
415+ }
416+
417+ break ;
418+ }
419+ catch when ( attempt < maxAttempts )
420+ {
421+ // The recreate has not become consistent yet; wait for the gateway to catch up and retry.
422+ await Task . Delay ( 200 ) . ConfigureAwait ( false ) ;
423+ }
424+ catch
425+ {
426+ // Best-effort priming; never let it fail the clean.
427+ }
428+ }
429+ }
430+ }
431+ catch
432+ {
433+ // Never let cache priming fail the clean.
434+ }
435+ }
436+
360437 private async Task CreateContainersAsync ( DbContext context )
361438 {
362439 var databaseAccount = await GetDBAccount ( ) . ConfigureAwait ( false ) ;
0 commit comments