|  | 
| 2 | 2 | // The .NET Foundation licenses this file to you under the MIT license. | 
| 3 | 3 | 
 | 
| 4 | 4 | using System; | 
|  | 5 | +using System.Collections.Generic; | 
| 5 | 6 | using System.Linq; | 
| 6 | 7 | using System.Security.Cryptography; | 
| 7 | 8 | using Microsoft.Extensions.DependencyInjection.Specification.Fakes; | 
| @@ -286,6 +287,280 @@ public void ResolveKeyedServicesAnyKeyConsistencyWithAnyKeyRegistration() | 
| 286 | 287 |             Assert.Throws<InvalidOperationException>(() => provider2.GetKeyedService<IService>(KeyedService.AnyKey)); | 
| 287 | 288 |         } | 
| 288 | 289 | 
 | 
|  | 290 | +        [Theory] | 
|  | 291 | +        [InlineData(true)] | 
|  | 292 | +        [InlineData(false)] | 
|  | 293 | +        // Test ordering and slot assignments when DI calls the service's constructor | 
|  | 294 | +        // across keyed services with different service types and keys. | 
|  | 295 | +        public void ResolveWithAnyKeyQuery_Constructor(bool anyKeyQueryBeforeSingletonQueries) | 
|  | 296 | +        { | 
|  | 297 | +            var serviceCollection = new ServiceCollection(); | 
|  | 298 | + | 
|  | 299 | +            // Interweave these to check that the slot \ ordering logic is correct. | 
|  | 300 | +            // Each unique key + its service Type maintains their own slot in a AnyKey query. | 
|  | 301 | +            serviceCollection.AddKeyedSingleton<TestServiceA>("key1"); | 
|  | 302 | +            serviceCollection.AddKeyedSingleton<TestServiceB>("key1"); | 
|  | 303 | +            serviceCollection.AddKeyedSingleton<TestServiceA>("key2"); | 
|  | 304 | +            serviceCollection.AddKeyedSingleton<TestServiceB>("key2"); | 
|  | 305 | +            serviceCollection.AddKeyedSingleton<TestServiceA>("key3"); | 
|  | 306 | +            serviceCollection.AddKeyedSingleton<TestServiceB>("key3"); | 
|  | 307 | + | 
|  | 308 | +            var provider = CreateServiceProvider(serviceCollection); | 
|  | 309 | + | 
|  | 310 | +            TestServiceA[] allInstancesA = null; | 
|  | 311 | +            TestServiceB[] allInstancesB = null; | 
|  | 312 | + | 
|  | 313 | +            if (anyKeyQueryBeforeSingletonQueries) | 
|  | 314 | +            { | 
|  | 315 | +                DoAnyKeyQuery(); | 
|  | 316 | +            } | 
|  | 317 | + | 
|  | 318 | +            var serviceA1 = provider.GetKeyedService<TestServiceA>("key1"); | 
|  | 319 | +            var serviceB1 = provider.GetKeyedService<TestServiceB>("key1"); | 
|  | 320 | +            var serviceA2 = provider.GetKeyedService<TestServiceA>("key2"); | 
|  | 321 | +            var serviceB2 = provider.GetKeyedService<TestServiceB>("key2"); | 
|  | 322 | +            var serviceA3 = provider.GetKeyedService<TestServiceA>("key3"); | 
|  | 323 | +            var serviceB3 = provider.GetKeyedService<TestServiceB>("key3"); | 
|  | 324 | + | 
|  | 325 | +            if (!anyKeyQueryBeforeSingletonQueries) | 
|  | 326 | +            { | 
|  | 327 | +                DoAnyKeyQuery(); | 
|  | 328 | +            } | 
|  | 329 | + | 
|  | 330 | +            Assert.Equal( | 
|  | 331 | +                new[] { serviceA1, serviceA2, serviceA3 }, | 
|  | 332 | +                allInstancesA); | 
|  | 333 | + | 
|  | 334 | +            Assert.Equal( | 
|  | 335 | +                new[] { serviceB1, serviceB2, serviceB3 }, | 
|  | 336 | +                allInstancesB); | 
|  | 337 | + | 
|  | 338 | +            void DoAnyKeyQuery() | 
|  | 339 | +            { | 
|  | 340 | +                IEnumerable<TestServiceA> allA = provider.GetKeyedServices<TestServiceA>(KeyedService.AnyKey); | 
|  | 341 | +                IEnumerable<TestServiceB> allB = provider.GetKeyedServices<TestServiceB>(KeyedService.AnyKey); | 
|  | 342 | + | 
|  | 343 | +                // Verify caching returns the same IEnumerable<> instance. | 
|  | 344 | +                Assert.Same(allA, provider.GetKeyedServices<TestServiceA>(KeyedService.AnyKey)); | 
|  | 345 | +                Assert.Same(allB, provider.GetKeyedServices<TestServiceB>(KeyedService.AnyKey)); | 
|  | 346 | + | 
|  | 347 | +                allInstancesA = allA.ToArray(); | 
|  | 348 | +                allInstancesB = allB.ToArray(); | 
|  | 349 | +            } | 
|  | 350 | +        } | 
|  | 351 | + | 
|  | 352 | +        [Theory] | 
|  | 353 | +        [InlineData(true)] | 
|  | 354 | +        [InlineData(false)] | 
|  | 355 | +        // Test ordering and slot assignments when DI calls the service's constructor | 
|  | 356 | +        // across keyed services with different service types with duplicate keys. | 
|  | 357 | +        public void ResolveWithAnyKeyQuery_Constructor_Duplicates(bool anyKeyQueryBeforeSingletonQueries) | 
|  | 358 | +        { | 
|  | 359 | +            var serviceCollection = new ServiceCollection(); | 
|  | 360 | + | 
|  | 361 | +            // Interweave these to check that the slot \ ordering logic is correct. | 
|  | 362 | +            // Each unique key + its service Type maintains their own slot in a AnyKey query. | 
|  | 363 | +            serviceCollection.AddKeyedSingleton<TestServiceA>("key"); | 
|  | 364 | +            serviceCollection.AddKeyedSingleton<TestServiceB>("key"); | 
|  | 365 | +            serviceCollection.AddKeyedSingleton<TestServiceA>("key"); | 
|  | 366 | +            serviceCollection.AddKeyedSingleton<TestServiceB>("key"); | 
|  | 367 | +            serviceCollection.AddKeyedSingleton<TestServiceA>("key"); | 
|  | 368 | +            serviceCollection.AddKeyedSingleton<TestServiceB>("key"); | 
|  | 369 | + | 
|  | 370 | +            var provider = CreateServiceProvider(serviceCollection); | 
|  | 371 | + | 
|  | 372 | +            TestServiceA[] allInstancesA = null; | 
|  | 373 | +            TestServiceB[] allInstancesB = null; | 
|  | 374 | + | 
|  | 375 | +            if (anyKeyQueryBeforeSingletonQueries) | 
|  | 376 | +            { | 
|  | 377 | +                DoAnyKeyQuery(); | 
|  | 378 | +            } | 
|  | 379 | + | 
|  | 380 | +            var serviceA = provider.GetKeyedService<TestServiceA>("key"); | 
|  | 381 | +            Assert.Same(serviceA, provider.GetKeyedService<TestServiceA>("key")); | 
|  | 382 | + | 
|  | 383 | +            var serviceB = provider.GetKeyedService<TestServiceB>("key"); | 
|  | 384 | +            Assert.Same(serviceB, provider.GetKeyedService<TestServiceB>("key")); | 
|  | 385 | + | 
|  | 386 | +            if (!anyKeyQueryBeforeSingletonQueries) | 
|  | 387 | +            { | 
|  | 388 | +                DoAnyKeyQuery(); | 
|  | 389 | +            } | 
|  | 390 | + | 
|  | 391 | +            // An AnyKey query we get back the last registered service for duplicates. | 
|  | 392 | +            // The first and second services are effectively hidden unless we query all. | 
|  | 393 | +            Assert.Equal(3, allInstancesA.Length); | 
|  | 394 | +            Assert.Same(serviceA, allInstancesA[2]); | 
|  | 395 | +            Assert.NotSame(serviceA, allInstancesA[1]); | 
|  | 396 | +            Assert.NotSame(serviceA, allInstancesA[0]); | 
|  | 397 | +            Assert.NotSame(allInstancesA[0], allInstancesA[1]); | 
|  | 398 | + | 
|  | 399 | +            Assert.Equal(3, allInstancesB.Length); | 
|  | 400 | +            Assert.Same(serviceB, allInstancesB[2]); | 
|  | 401 | +            Assert.NotSame(serviceB, allInstancesB[1]); | 
|  | 402 | +            Assert.NotSame(serviceB, allInstancesB[0]); | 
|  | 403 | +            Assert.NotSame(allInstancesB[0], allInstancesB[1]); | 
|  | 404 | + | 
|  | 405 | +            void DoAnyKeyQuery() | 
|  | 406 | +            { | 
|  | 407 | +                IEnumerable<TestServiceA> allA = provider.GetKeyedServices<TestServiceA>(KeyedService.AnyKey); | 
|  | 408 | +                IEnumerable<TestServiceB> allB = provider.GetKeyedServices<TestServiceB>(KeyedService.AnyKey); | 
|  | 409 | + | 
|  | 410 | +                // Verify caching returns the same IEnumerable<> instances. | 
|  | 411 | +                Assert.Same(allA, provider.GetKeyedServices<TestServiceA>(KeyedService.AnyKey)); | 
|  | 412 | +                Assert.Same(allB, provider.GetKeyedServices<TestServiceB>(KeyedService.AnyKey)); | 
|  | 413 | + | 
|  | 414 | +                allInstancesA = allA.ToArray(); | 
|  | 415 | +                allInstancesB = allB.ToArray(); | 
|  | 416 | +            } | 
|  | 417 | +        } | 
|  | 418 | + | 
|  | 419 | +        [Theory] | 
|  | 420 | +        [InlineData(true)] | 
|  | 421 | +        [InlineData(false)] | 
|  | 422 | +        // Test ordering and slot assignments when service is provided | 
|  | 423 | +        // across keyed services with different service types and keys. | 
|  | 424 | +        public void ResolveWithAnyKeyQuery_InstanceProvided(bool anyKeyQueryBeforeSingletonQueries) | 
|  | 425 | +        { | 
|  | 426 | +            var serviceCollection = new ServiceCollection(); | 
|  | 427 | + | 
|  | 428 | +            TestServiceA serviceA1 = new(); | 
|  | 429 | +            TestServiceA serviceA2 = new(); | 
|  | 430 | +            TestServiceA serviceA3 = new(); | 
|  | 431 | +            TestServiceB serviceB1 = new(); | 
|  | 432 | +            TestServiceB serviceB2 = new(); | 
|  | 433 | +            TestServiceB serviceB3 = new(); | 
|  | 434 | + | 
|  | 435 | +            // Interweave these to check that the slot \ ordering logic is correct. | 
|  | 436 | +            // Each unique key + its service Type maintains their own slot in a AnyKey query. | 
|  | 437 | +            serviceCollection.AddKeyedSingleton<TestServiceA>("key1", serviceA1); | 
|  | 438 | +            serviceCollection.AddKeyedSingleton<TestServiceB>("key1", serviceB1); | 
|  | 439 | +            serviceCollection.AddKeyedSingleton<TestServiceA>("key2", serviceA2); | 
|  | 440 | +            serviceCollection.AddKeyedSingleton<TestServiceB>("key2", serviceB2); | 
|  | 441 | +            serviceCollection.AddKeyedSingleton<TestServiceA>("key3", serviceA3); | 
|  | 442 | +            serviceCollection.AddKeyedSingleton<TestServiceB>("key3", serviceB3); | 
|  | 443 | + | 
|  | 444 | +            var provider = CreateServiceProvider(serviceCollection); | 
|  | 445 | + | 
|  | 446 | +            TestServiceA[] allInstancesA = null; | 
|  | 447 | +            TestServiceB[] allInstancesB = null; | 
|  | 448 | + | 
|  | 449 | +            if (anyKeyQueryBeforeSingletonQueries) | 
|  | 450 | +            { | 
|  | 451 | +                DoAnyKeyQuery(); | 
|  | 452 | +            } | 
|  | 453 | + | 
|  | 454 | +            var fromServiceA1 = provider.GetKeyedService<TestServiceA>("key1"); | 
|  | 455 | +            var fromServiceA2 = provider.GetKeyedService<TestServiceA>("key2"); | 
|  | 456 | +            var fromServiceA3 = provider.GetKeyedService<TestServiceA>("key3"); | 
|  | 457 | +            Assert.Same(serviceA1, fromServiceA1); | 
|  | 458 | +            Assert.Same(serviceA2, fromServiceA2); | 
|  | 459 | +            Assert.Same(serviceA3, fromServiceA3); | 
|  | 460 | + | 
|  | 461 | +            var fromServiceB1 = provider.GetKeyedService<TestServiceB>("key1"); | 
|  | 462 | +            var fromServiceB2 = provider.GetKeyedService<TestServiceB>("key2"); | 
|  | 463 | +            var fromServiceB3 = provider.GetKeyedService<TestServiceB>("key3"); | 
|  | 464 | +            Assert.Same(serviceB1, fromServiceB1); | 
|  | 465 | +            Assert.Same(serviceB2, fromServiceB2); | 
|  | 466 | +            Assert.Same(serviceB3, fromServiceB3); | 
|  | 467 | + | 
|  | 468 | +            if (!anyKeyQueryBeforeSingletonQueries) | 
|  | 469 | +            { | 
|  | 470 | +                DoAnyKeyQuery(); | 
|  | 471 | +            } | 
|  | 472 | + | 
|  | 473 | +            Assert.Equal( | 
|  | 474 | +                new[] { serviceA1, serviceA2, serviceA3 }, | 
|  | 475 | +                allInstancesA); | 
|  | 476 | + | 
|  | 477 | +            Assert.Equal( | 
|  | 478 | +                new[] { serviceB1, serviceB2, serviceB3 }, | 
|  | 479 | +                allInstancesB); | 
|  | 480 | + | 
|  | 481 | +            void DoAnyKeyQuery() | 
|  | 482 | +            { | 
|  | 483 | +                IEnumerable<TestServiceA> allA = provider.GetKeyedServices<TestServiceA>(KeyedService.AnyKey); | 
|  | 484 | +                IEnumerable<TestServiceB> allB = provider.GetKeyedServices<TestServiceB>(KeyedService.AnyKey); | 
|  | 485 | + | 
|  | 486 | +                // Verify caching returns the same items. | 
|  | 487 | +                Assert.Equal(allA, provider.GetKeyedServices<TestServiceA>(KeyedService.AnyKey)); | 
|  | 488 | +                Assert.Equal(allB, provider.GetKeyedServices<TestServiceB>(KeyedService.AnyKey)); | 
|  | 489 | + | 
|  | 490 | +                allInstancesA = allA.ToArray(); | 
|  | 491 | +                allInstancesB = allB.ToArray(); | 
|  | 492 | +            } | 
|  | 493 | +        } | 
|  | 494 | + | 
|  | 495 | +        [Theory] | 
|  | 496 | +        [InlineData(true)] | 
|  | 497 | +        [InlineData(false)] | 
|  | 498 | +        // Test ordering and slot assignments when service is provided | 
|  | 499 | +        // across keyed services with different service types with duplicate keys. | 
|  | 500 | +        public void ResolveWithAnyKeyQuery_InstanceProvided_Duplicates(bool anyKeyQueryBeforeSingletonQueries) | 
|  | 501 | +        { | 
|  | 502 | +            var serviceCollection = new ServiceCollection(); | 
|  | 503 | + | 
|  | 504 | +            TestServiceA serviceA1 = new(); | 
|  | 505 | +            TestServiceA serviceA2 = new(); | 
|  | 506 | +            TestServiceA serviceA3 = new(); | 
|  | 507 | +            TestServiceB serviceB1 = new(); | 
|  | 508 | +            TestServiceB serviceB2 = new(); | 
|  | 509 | +            TestServiceB serviceB3 = new(); | 
|  | 510 | + | 
|  | 511 | +            // Interweave these to check that the slot \ ordering logic is correct. | 
|  | 512 | +            // Each unique key + its service Type maintains their own slot in a AnyKey query. | 
|  | 513 | +            serviceCollection.AddKeyedSingleton<TestServiceA>("key", serviceA1); | 
|  | 514 | +            serviceCollection.AddKeyedSingleton<TestServiceB>("key", serviceB1); | 
|  | 515 | +            serviceCollection.AddKeyedSingleton<TestServiceA>("key", serviceA2); | 
|  | 516 | +            serviceCollection.AddKeyedSingleton<TestServiceB>("key", serviceB2); | 
|  | 517 | +            serviceCollection.AddKeyedSingleton<TestServiceA>("key", serviceA3); | 
|  | 518 | +            serviceCollection.AddKeyedSingleton<TestServiceB>("key", serviceB3); | 
|  | 519 | + | 
|  | 520 | +            var provider = CreateServiceProvider(serviceCollection); | 
|  | 521 | + | 
|  | 522 | +            TestServiceA[] allInstancesA = null; | 
|  | 523 | +            TestServiceB[] allInstancesB = null; | 
|  | 524 | + | 
|  | 525 | +            if (anyKeyQueryBeforeSingletonQueries) | 
|  | 526 | +            { | 
|  | 527 | +                DoAnyKeyQuery(); | 
|  | 528 | +            } | 
|  | 529 | + | 
|  | 530 | +            // We get back the last registered service for duplicates. | 
|  | 531 | +            Assert.Same(serviceA3, provider.GetKeyedService<TestServiceA>("key")); | 
|  | 532 | +            Assert.Same(serviceB3, provider.GetKeyedService<TestServiceB>("key")); | 
|  | 533 | + | 
|  | 534 | +            if (!anyKeyQueryBeforeSingletonQueries) | 
|  | 535 | +            { | 
|  | 536 | +                DoAnyKeyQuery(); | 
|  | 537 | +            } | 
|  | 538 | + | 
|  | 539 | +            Assert.Equal( | 
|  | 540 | +                new[] { serviceA1, serviceA2, serviceA3 }, | 
|  | 541 | +                allInstancesA); | 
|  | 542 | + | 
|  | 543 | +            Assert.Equal( | 
|  | 544 | +                new[] { serviceB1, serviceB2, serviceB3 }, | 
|  | 545 | +                allInstancesB); | 
|  | 546 | + | 
|  | 547 | +            void DoAnyKeyQuery() | 
|  | 548 | +            { | 
|  | 549 | +                IEnumerable<TestServiceA> allA = provider.GetKeyedServices<TestServiceA>(KeyedService.AnyKey); | 
|  | 550 | +                IEnumerable<TestServiceB> allB = provider.GetKeyedServices<TestServiceB>(KeyedService.AnyKey); | 
|  | 551 | + | 
|  | 552 | +                // Verify caching returns the same items. | 
|  | 553 | +                Assert.Equal(allA, provider.GetKeyedServices<TestServiceA>(KeyedService.AnyKey)); | 
|  | 554 | +                Assert.Equal(allB, provider.GetKeyedServices<TestServiceB>(KeyedService.AnyKey)); | 
|  | 555 | + | 
|  | 556 | +                allInstancesA = allA.ToArray(); | 
|  | 557 | +                allInstancesB = allB.ToArray(); | 
|  | 558 | +            } | 
|  | 559 | +        } | 
|  | 560 | + | 
|  | 561 | +        private class TestServiceA { } | 
|  | 562 | +        private class TestServiceB { } | 
|  | 563 | + | 
| 289 | 564 |         [Fact] | 
| 290 | 565 |         public void ResolveKeyedServicesAnyKeyOrdering() | 
| 291 | 566 |         { | 
|  | 
0 commit comments