diff --git a/src/Castle.Facilities.AspNet.Mvc.Tests/Castle.Facilities.AspNet.Mvc.Tests.csproj b/src/Castle.Facilities.AspNet.Mvc.Tests/Castle.Facilities.AspNet.Mvc.Tests.csproj
index ccb068647..11cd2e890 100644
--- a/src/Castle.Facilities.AspNet.Mvc.Tests/Castle.Facilities.AspNet.Mvc.Tests.csproj
+++ b/src/Castle.Facilities.AspNet.Mvc.Tests/Castle.Facilities.AspNet.Mvc.Tests.csproj
@@ -15,11 +15,11 @@
-
+
-
+
diff --git a/src/Castle.Facilities.AspNet.SystemWeb.Tests/Castle.Facilities.AspNet.SystemWeb.Tests.csproj b/src/Castle.Facilities.AspNet.SystemWeb.Tests/Castle.Facilities.AspNet.SystemWeb.Tests.csproj
index 76f9f3266..2667d9d4a 100644
--- a/src/Castle.Facilities.AspNet.SystemWeb.Tests/Castle.Facilities.AspNet.SystemWeb.Tests.csproj
+++ b/src/Castle.Facilities.AspNet.SystemWeb.Tests/Castle.Facilities.AspNet.SystemWeb.Tests.csproj
@@ -19,10 +19,10 @@
-
+
-
+
diff --git a/src/Castle.Facilities.AspNet.WebApi.Tests/Castle.Facilities.AspNet.WebApi.Tests.csproj b/src/Castle.Facilities.AspNet.WebApi.Tests/Castle.Facilities.AspNet.WebApi.Tests.csproj
index af3796890..dbd7491c2 100644
--- a/src/Castle.Facilities.AspNet.WebApi.Tests/Castle.Facilities.AspNet.WebApi.Tests.csproj
+++ b/src/Castle.Facilities.AspNet.WebApi.Tests/Castle.Facilities.AspNet.WebApi.Tests.csproj
@@ -15,11 +15,11 @@
-
+
-
+
diff --git a/src/Castle.Facilities.AspNetCore.Tests/Castle.Facilities.AspNetCore.Tests.csproj b/src/Castle.Facilities.AspNetCore.Tests/Castle.Facilities.AspNetCore.Tests.csproj
index 4efbaf31d..195b1176e 100644
--- a/src/Castle.Facilities.AspNetCore.Tests/Castle.Facilities.AspNetCore.Tests.csproj
+++ b/src/Castle.Facilities.AspNetCore.Tests/Castle.Facilities.AspNetCore.Tests.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/src/Castle.Facilities.WcfIntegration.Tests/Castle.Facilities.WcfIntegration.Tests.csproj b/src/Castle.Facilities.WcfIntegration.Tests/Castle.Facilities.WcfIntegration.Tests.csproj
index 11b5401dc..82b2bed24 100644
--- a/src/Castle.Facilities.WcfIntegration.Tests/Castle.Facilities.WcfIntegration.Tests.csproj
+++ b/src/Castle.Facilities.WcfIntegration.Tests/Castle.Facilities.WcfIntegration.Tests.csproj
@@ -12,9 +12,9 @@
-
+
-
+
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/Castle.Windsor.Extensions.DependencyInjection.Tests.csproj b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/Castle.Windsor.Extensions.DependencyInjection.Tests.csproj
index aa6c272d3..327125b31 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/Castle.Windsor.Extensions.DependencyInjection.Tests.csproj
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/Castle.Windsor.Extensions.DependencyInjection.Tests.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/CustomAssumptionTests.cs b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/CustomAssumptionTests.cs
index ac83c0f0a..df3456951 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/CustomAssumptionTests.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/CustomAssumptionTests.cs
@@ -1,6 +1,6 @@
#if NET8_0_OR_GREATER
+using Castle.MicroKernel;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Specification.Fakes;
using System;
using System.Linq;
using System.Threading;
@@ -110,6 +110,21 @@ public void Scoped_service_resolved_outside_scope()
Assert.Equal(resolvedOutsideScope, resolvedAgainOutsideScope);
}
+ [Fact]
+ public void Mix_of_keyed_and_not_keyed()
+ {
+ var serviceCollection = GetServiceCollection();
+ serviceCollection.AddSingleton();
+ serviceCollection.AddKeyedSingleton("bla");
+
+ _serviceProvider = BuildServiceProvider(serviceCollection);
+
+ //can resolve the non-keyed
+ var nonKeyed = _serviceProvider.GetRequiredService();
+ Assert.NotNull(nonKeyed);
+ Assert.IsType(nonKeyed);
+ }
+
[Fact]
public void Scoped_service_resolved_outside_scope_in_another_thread()
{
@@ -167,7 +182,8 @@ public async void Simulate_async_timer_without_wait()
ITestService resolvedInThread = null;
async Task ExecuteAsync()
{
- while (!stop)
+ DateTime start = DateTime.UtcNow;
+ while (!stop && DateTime.UtcNow.Subtract(start).TotalSeconds < 10)
{
await Task.Delay(100);
if (shouldResolve)
@@ -178,10 +194,7 @@ async Task ExecuteAsync()
}
}
//fire and forget
-#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
var task = ExecuteAsync();
-#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
-
await Task.Delay(500);
var serviceCollection = GetServiceCollection();
@@ -353,7 +366,6 @@ public void TryToResolveScopedInOtherThread()
Assert.True(task.Result);
}
-
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
@@ -364,16 +376,12 @@ protected override void Dispose(bool disposing)
}
}
- internal class TestService : ITestService
- {
- }
+ internal class TestService : ITestService;
- internal class AnotherTestService : ITestService
- {
- }
+ internal class AnotherTestService : ITestService;
- internal interface ITestService
- {
- }
+ internal class ThirdTestService : ITestService;
+
+ internal interface ITestService;
}
#endif
\ No newline at end of file
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/WindsorKeyedDependencyInjectionSpecificationTests.cs b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/WindsorKeyedDependencyInjectionSpecificationTests.cs
index 6fc401577..c4bcdb1da 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/WindsorKeyedDependencyInjectionSpecificationTests.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/WindsorKeyedDependencyInjectionSpecificationTests.cs
@@ -2,7 +2,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Specification;
using System;
-using Xunit;
namespace Castle.Windsor.Extensions.DependencyInjection.Tests
{
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/WindsorScopedServiceProviderCustomWindsorContainerTests.cs b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/WindsorScopedServiceProviderCustomWindsorContainerTests.cs
index 54f909b97..34bf9b424 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/WindsorScopedServiceProviderCustomWindsorContainerTests.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/WindsorScopedServiceProviderCustomWindsorContainerTests.cs
@@ -53,6 +53,29 @@ public void Dispose()
GC.SuppressFinalize(this);
}
+ /////
+ ///// To verify when a single test failed, open the corresponding test in dotnet/runtime repository,
+ ///// then copy the test here, change name and execute with debugging etc etc.
+ ///// This helps because source link support seems to be not to easy to use from the test runner
+ ///// and this tricks makes everything really simpler.
+ /////
+ //[Fact]
+ //public void ClosedServicesPreferredOverOpenGenericServices_custom()
+ //{
+ // // Arrange
+ // var collection = new TestServiceCollection();
+ // collection.AddTransient(typeof(IFakeOpenGenericService), typeof(FakeService));
+ // collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(FakeOpenGenericService<>));
+ // collection.AddSingleton();
+ // var provider = CreateServiceProvider(collection);
+
+ // // Act
+ // var service = provider.GetService>();
+
+ // // Assert
+ // Assert.IsType(service);
+ //}
+
#if NET6_0_OR_GREATER
#endif
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopedServiceProvider.cs b/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopedServiceProvider.cs
index 1ccb1935e..7e2c1a696 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopedServiceProvider.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopedServiceProvider.cs
@@ -14,6 +14,7 @@
namespace Castle.Windsor.Extensions.DependencyInjection
{
+ using Castle.MicroKernel;
using Castle.MicroKernel.Handlers;
using Castle.Windsor;
using Castle.Windsor.Extensions.DependencyInjection.Scope;
@@ -99,15 +100,53 @@ private object ResolveInstanceOrNull(Type serviceType, bool isOptional)
//this is complicated by the concept of keyed service, because if you are about to resolve WITHOUTH KEY you do not
//need to resolve keyed services. Now Keyed services are available only in version 8 but we register with an helper
//all registered services so we can know if a service was really registered with keyed service or not.
- var componentRegistration = container.Kernel.GetHandler(serviceType);
- if (componentRegistration.ComponentModel.Name.StartsWith(KeyedRegistrationHelper.KeyedRegistrationPrefix))
+ var componentRegistrations = container.Kernel.GetHandlers(serviceType);
+
+ //now since the caller requested a NON Keyed component, we need to skip all keyed components.
+ var realRegistrations = componentRegistrations.Where(x => !x.ComponentModel.Name.StartsWith(KeyedRegistrationHelper.KeyedRegistrationPrefix)).ToList();
+ string registrationName = null;
+ if (realRegistrations.Count == 1)
+ {
+ registrationName = realRegistrations[0].ComponentModel.Name;
+ }
+ else if (realRegistrations.Count == 0)
+ {
+ //No component is registered for the interface without key, resolution cannot be done.
+ registrationName = null;
+ }
+ else if (realRegistrations.Count > 1)
+ {
+ //more than one component is registered for the interface without key, we have some ambiguity that is resolved, based on test
+ //found in framework with this rule.
+ //1. Last component win.
+ //2. closed service are preferred over open generic.
+
+ //take first non generic
+ for (int i = realRegistrations.Count - 1; i >= 0; i--)
+ {
+ if (!realRegistrations[i].ComponentModel.Implementation.IsGenericTypeDefinition)
+ {
+ registrationName = realRegistrations[i].ComponentModel.Name;
+ break;
+ }
+ }
+
+ //if we did not find any non generic, take the last one.
+ if (registrationName == null)
+ {
+ registrationName = realRegistrations[realRegistrations.Count - 1].ComponentModel.Name;
+ }
+ }
+
+ if (registrationName == null)
{
- //Component was registered as keyed component, so we really need to resolve with null because this is the old interface
- //so no key is provided.
return null;
}
-#endif
+ return container.Resolve(registrationName, serviceType);
+#else
+ //no keyed component in previous framework, just resolve.
return container.Resolve(serviceType);
+#endif
}
if (serviceType.GetTypeInfo().IsGenericType && serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
diff --git a/src/Castle.Windsor.Tests/Castle.Windsor.Tests.csproj b/src/Castle.Windsor.Tests/Castle.Windsor.Tests.csproj
index 474f70187..1a30263e0 100644
--- a/src/Castle.Windsor.Tests/Castle.Windsor.Tests.csproj
+++ b/src/Castle.Windsor.Tests/Castle.Windsor.Tests.csproj
@@ -47,7 +47,7 @@
-
+