Browse Source

Support `keyed-services`.

Resolve #18708
pull/18777/head
maliming 2 years ago
parent
commit
c3dc38d1c9
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 10
      framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/AbpAspNetCoreAsyncIntegratedTestBase.cs
  2. 10
      framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/AbpWebApplicationFactoryIntegratedTest.cs
  3. 122
      framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacRegistration.cs
  4. 12
      framework/src/Volo.Abp.TestBase/Volo/Abp/AbpTestBaseWithServiceProvider.cs
  5. 37
      framework/test/Volo.Abp.Core.Tests/Microsoft/Extensions/DependencyInjection/DependencyInjection_Tests.cs

10
framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/AbpAspNetCoreAsyncIntegratedTestBase.cs

@ -35,6 +35,16 @@ public class AbpAspNetCoreAsyncIntegratedTestBase<TModule>
return ServiceProvider.GetRequiredService<T>();
}
protected virtual T? GetKeyedServices<T>(object? serviceKey)
{
return ServiceProvider.GetKeyedService<T>(serviceKey);
}
protected virtual T GetRequiredKeyedService<T>(object? serviceKey) where T : notnull
{
return ServiceProvider.GetRequiredKeyedService<T>(serviceKey);
}
public virtual async Task InitializeAsync()
{
var builder = WebApplication.CreateBuilder();

10
framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/AbpWebApplicationFactoryIntegratedTest.cs

@ -43,6 +43,16 @@ public abstract class AbpWebApplicationFactoryIntegratedTest<TProgram> : WebAppl
return Services.GetRequiredService<T>();
}
protected virtual T? GetKeyedServices<T>(object? serviceKey)
{
return ServiceProvider.GetKeyedService<T>(serviceKey);
}
protected virtual T GetRequiredKeyedService<T>(object? serviceKey) where T : notnull
{
return ServiceProvider.GetRequiredKeyedService<T>(serviceKey);
}
protected virtual void ConfigureServices(IServiceCollection services)
{

122
framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacRegistration.cs

@ -27,6 +27,7 @@ using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Autofac.Builder;
using Autofac.Core.Resolving.Pipeline;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.Modularity;
@ -97,6 +98,8 @@ public static class AutofacRegistration
builder.RegisterType<AutofacServiceProvider>()
.As<IServiceProvider>()
.As<IServiceProviderIsService>()
.As<IKeyedServiceProvider>()
.As<IServiceProviderIsKeyedService>()
.ExternallyOwned();
var autofacServiceScopeFactory = typeof(AutofacServiceProvider).Assembly.GetType("Autofac.Extensions.DependencyInjection.AutofacServiceScopeFactory");
@ -111,9 +114,43 @@ public static class AutofacRegistration
.As<IServiceScopeFactory>()
.SingleInstance();
// Shims for keyed service compatibility.
builder.RegisterServiceMiddlewareSource(new KeyedServiceMiddlewareSource());
builder.RegisterSource<AnyKeyRegistrationSource>();
Register(builder, services, lifetimeScopeTagForSingletons);
}
/// <summary>
/// Configures the exposed service type on a service registration.
/// </summary>
/// <typeparam name="TActivatorData">The activator data type.</typeparam>
/// <typeparam name="TRegistrationStyle">The object registration style.</typeparam>
/// <param name="registrationBuilder">The registration being built.</param>
/// <param name="descriptor">The service descriptor with service type and key information.</param>
/// <returns>
/// The <paramref name="registrationBuilder" />, configured with the proper service type,
/// and available for additional configuration.
/// </returns>
private static IRegistrationBuilder<object, TActivatorData, TRegistrationStyle> ConfigureServiceType<TActivatorData, TRegistrationStyle>(
this IRegistrationBuilder<object, TActivatorData, TRegistrationStyle> registrationBuilder,
ServiceDescriptor descriptor)
{
if (descriptor.IsKeyedService)
{
var key = descriptor.ServiceKey!;
// If it's keyed, the service key won't be null. A null key results in it _not_ being a keyed service.
registrationBuilder.Keyed(key, descriptor.ServiceType);
}
else
{
registrationBuilder.As(descriptor.ServiceType);
}
return registrationBuilder;
}
/// <summary>
/// Configures the lifecycle on a service registration.
/// </summary>
@ -187,47 +224,106 @@ public static class AutofacRegistration
foreach (var descriptor in services)
{
if (descriptor.ImplementationType != null)
var implementationType = descriptor.NormalizedImplementationType();
if (implementationType != null)
{
// Test if the an open generic type is being registered
var serviceTypeInfo = descriptor.ServiceType.GetTypeInfo();
if (serviceTypeInfo.IsGenericTypeDefinition)
{
builder
.RegisterGeneric(descriptor.ImplementationType)
.As(descriptor.ServiceType)
.RegisterGeneric(implementationType)
.ConfigureServiceType(descriptor)
.ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons)
.ConfigureAbpConventions(descriptor, moduleContainer, registrationActionList, activatedActionList);
}
else
{
builder
.RegisterType(descriptor.ImplementationType)
.As(descriptor.ServiceType)
.RegisterType(implementationType)
.ConfigureServiceType(descriptor)
.ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons)
.ConfigureAbpConventions(descriptor, moduleContainer, registrationActionList, activatedActionList);
}
continue;
}
else if (descriptor.ImplementationFactory != null)
if (descriptor.IsKeyedService && descriptor.KeyedImplementationFactory != null)
{
var registration = RegistrationBuilder.ForDelegate(descriptor.ServiceType, (context, parameters) =>
{
// At this point the context is always a ResolveRequestContext, which will expose the actual service type.
var requestContext = (ResolveRequestContext)context;
var serviceProvider = context.Resolve<IServiceProvider>();
var keyedService = (Autofac.Core.KeyedService)requestContext.Service;
var key = keyedService.ServiceKey;
return descriptor.KeyedImplementationFactory(serviceProvider, key);
})
.ConfigureServiceType(descriptor)
.ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons)
.CreateRegistration();
//TODO: ConfigureAbpConventions ?
builder.RegisterComponent(registration);
continue;
}
if (!descriptor.IsKeyedService && descriptor.ImplementationFactory != null)
{
var registration = RegistrationBuilder.ForDelegate(descriptor.ServiceType, (context, parameters) =>
{
var serviceProvider = context.Resolve<IServiceProvider>();
return descriptor.ImplementationFactory(serviceProvider);
})
.ConfigureServiceType(descriptor)
.ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons)
.CreateRegistration();
//TODO: ConfigureAbpConventions ?
builder.RegisterComponent(registration);
continue;
}
else
{
builder
.RegisterInstance(descriptor.ImplementationInstance!)
.As(descriptor.ServiceType)
.ConfigureLifecycle(descriptor.Lifetime, null);
}
// It's not a type or factory, so it must be an instance.
builder
.RegisterInstance(descriptor.NormalizedImplementationInstance()!)
.ConfigureServiceType(descriptor)
.ConfigureLifecycle(descriptor.Lifetime, null);
}
}
/// <summary>
/// Normalizes the implementation instance data between keyed and not keyed services.
/// </summary>
/// <param name="descriptor">
/// The <see cref="T:Microsoft.Extensions.DependencyInjection.ServiceDescriptor" /> to normalize.
/// </param>
/// <returns>
/// The appropriate implementation instance from the service descriptor.
/// </returns>
public static object? NormalizedImplementationInstance(this ServiceDescriptor descriptor)
{
return !descriptor.IsKeyedService ? descriptor.ImplementationInstance : descriptor.KeyedImplementationInstance;
}
/// <summary>
/// Normalizes the implementation type data between keyed and not keyed services.
/// </summary>
/// <param name="descriptor">
/// The <see cref="T:Microsoft.Extensions.DependencyInjection.ServiceDescriptor" /> to normalize.
/// </param>
/// <returns>
/// The appropriate implementation type from the service descriptor.
/// </returns>
public static Type? NormalizedImplementationType(this ServiceDescriptor descriptor)
{
return !descriptor.IsKeyedService ? descriptor.ImplementationType : descriptor.KeyedImplementationType;
}
}

12
framework/src/Volo.Abp.TestBase/Volo/Abp/AbpTestBaseWithServiceProvider.cs

@ -11,9 +11,19 @@ public abstract class AbpTestBaseWithServiceProvider
{
return ServiceProvider.GetService<T>();
}
protected virtual T GetRequiredService<T>() where T : notnull
{
return ServiceProvider.GetRequiredService<T>();
}
protected virtual T? GetKeyedServices<T>(object? serviceKey)
{
return ServiceProvider.GetKeyedService<T>(serviceKey);
}
protected virtual T GetRequiredKeyedService<T>(object? serviceKey) where T : notnull
{
return ServiceProvider.GetRequiredKeyedService<T>(serviceKey);
}
}

37
framework/test/Volo.Abp.Core.Tests/Microsoft/Extensions/DependencyInjection/DependencyInjection_Tests.cs

@ -88,6 +88,25 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest<Dep
ReferenceEquals(objectByInterfaceRef, objectByClassRef).ShouldBeTrue();
}
[Fact]
public void Should_Get_Keyed_Services()
{
var bigCache = GetRequiredKeyedService<ICache>("big");
var bigInstanceCache = GetRequiredKeyedService<ICache>("bigInstance");
var smallCache = GetRequiredKeyedService<ICache>("small");
var smallFactoryCache = GetRequiredKeyedService<ICache>("smallFactory");
bigCache.GetType().ShouldBe(typeof(BigCache));
bigInstanceCache.GetType().ShouldBe(typeof(BigCache));
smallCache.GetType().ShouldBe(typeof(SmallCache));
smallFactoryCache.GetType().ShouldBe(typeof(SmallCache));
bigCache.Get("key").ShouldBe("Resolving key from big cache.");
bigInstanceCache.Get("key").ShouldBe("Resolving key from big cache.");
smallCache.Get("key").ShouldBe("Resolving key from small cache.");
smallFactoryCache.Get("key").ShouldBe("Resolving key from small cache.");
}
public class MySingletonService : ISingletonDependency
{
public List<MyEmptyTransientService> TransientInstances { get; }
@ -164,6 +183,10 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest<Dep
context.Services.AddTransient(typeof(GenericServiceWithPropertyInject<>));
context.Services.AddTransient(typeof(GenericServiceWithDisablePropertyInjectionOnClass<>));
context.Services.AddTransient(typeof(GenericServiceWithDisablePropertyInjectionOnProperty<>));
context.Services.AddKeyedSingleton<ICache, BigCache>("big");
context.Services.AddKeyedSingleton<ICache, SmallCache>("small");
context.Services.AddKeyedSingleton<ICache>("bigInstance", new BigCache());
context.Services.AddKeyedSingleton<ICache>("smallFactory", (sp, key) => new SmallCache());
}
}
@ -215,4 +238,18 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest<Dep
public T Value { get; set; }
}
public interface ICache
{
object Get(string key);
}
public class BigCache : ICache
{
public object Get(string key) => $"Resolving {key} from big cache.";
}
public class SmallCache : ICache
{
public object Get(string key) => $"Resolving {key} from small cache.";
}
}

Loading…
Cancel
Save