前言

.NET生态中有许多依赖注入容器。在大多数情况下,微软提供的内置容器在易用性和性能方面都非常优秀。外加ASP.NET Core默认使用内置容器,使用很方便。

但是笔者在使用中一直有一个头疼的问题:服务工厂无法提供请求的服务类型相关的信息。这在一般情况下并没有影响,但是内置容器支持注册开放泛型服务,此时会导致无法实现某些需求。

ASP.NET Core目前推荐使用上下文池访问EF Core上下文,但是某些功能需要直接使用上下文(例如Identity Core)。官方文档建议使用自定义工厂通过上下文池获取上下文。这其实是一种服务转发(或委托),可以确保服务实例只有一个最终提供点,简化管理。

但是当希望转发的服务是开放泛型时就会出现问题。在实际请求服务时,无法通过自定义工厂得知请求的泛型服务的实际类型参数,也就无法实现对开放泛型类型的转发。官方仓库也有一个相关Issue:Dependency Injection of Open Generics via factory #41050。然而几年过去后微软依然没有打算解决这个问题。键控服务这种完全新增的功能都做了,这个举手之劳确一直放着,我不理解。一番研究后笔者确定可以通过简单的改造来实现支持,因此有了本篇文章。

新书宣传

有关新书的更多介绍欢迎查看《C#与.NET6 开发从入门到实践》上市,作者亲自来打广告了!

正文

本来笔者打算使用继承来扩展功能,但是几经周折后发现微软把关键类型设置为内部类和密封类,彻底断了这条路。无奈只能克隆仓库直接改代码,好死不死这个库是运行时仓库的一部分,完整仓库包含大量无关代码,直接fork也会带来一堆麻烦,最后只能克隆仓库后复制需要的部分来修改。

CoreDX.Extensions.DependencyInjection.Abstractions

这是基础抽象包,用于扩展ServiceDescriptor为后续改造提供基础支持。

TypedImplementationFactoryServiceDescriptor

要实现能从自定义工厂获取服务类型的功能,自定义工厂需要一个Type类型的参数来传递类型信息,那么就需要ServiceDescriptor提供相应的构造方法重载。原始类型显然不可能,好在这是个普通公共类,可以继承,因此笔者继承内置类并扩展了相应的成员来承载工厂委托。

/// <inheritdoc />
[DebuggerDisplay("{DebuggerToString(),nq}")]
public class TypedImplementationFactoryServiceDescriptor : ServiceDescriptor
{
private object? _typedImplementationFactory; /// <summary>
/// Gets the typed factory used for creating service instances.
/// </summary>
public Func<IServiceProvider, Type, object>? TypedImplementationFactory
{
get
{
if (IsKeyedService)
{
throw new InvalidOperationException("This service descriptor is keyed. Your service provider may not support keyed services.");
}
return (Func<IServiceProvider, Type, object>?)_typedImplementationFactory;
}
} private object? _typedKeyedImplementationFactory; /// <summary>
/// Gets the typed keyed factory used for creating service instances.
/// </summary>
public Func<IServiceProvider, object?, Type, object>? TypedKeyedImplementationFactory
{
get
{
if (!IsKeyedService)
{
throw new InvalidOperationException("This service descriptor is not keyed.");
}
return (Func<IServiceProvider, object?, Type, object>?)_typedKeyedImplementationFactory;
}
} /// <summary>
/// Don't use this!
/// </summary>
/// <inheritdoc />
public TypedImplementationFactoryServiceDescriptor(
Type serviceType,
object instance)
: base(serviceType, instance)
{
ThrowCtor();
} /// <summary>
/// Don't use this!
/// </summary>
/// <inheritdoc />
public TypedImplementationFactoryServiceDescriptor(
Type serviceType,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
ServiceLifetime lifetime)
: base(serviceType, implementationType, lifetime)
{
ThrowCtor();
} /// <summary>
/// Don't use this!
/// </summary>
/// <inheritdoc />
public TypedImplementationFactoryServiceDescriptor(
Type serviceType,
object? serviceKey,
object instance)
: base(serviceType, serviceKey, instance)
{
ThrowCtor();
} /// <summary>
/// Don't use this!
/// </summary>
/// <inheritdoc />
public TypedImplementationFactoryServiceDescriptor(
Type serviceType,
Func<IServiceProvider, object> factory,
ServiceLifetime lifetime)
: base(serviceType, factory, lifetime)
{
ThrowCtor();
} /// <summary>
/// Don't use this!
/// </summary>
/// <inheritdoc />
public TypedImplementationFactoryServiceDescriptor(
Type serviceType,
object? serviceKey,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
ServiceLifetime lifetime)
: base(serviceType, serviceKey, implementationType, lifetime)
{
ThrowCtor();
} /// <summary>
/// Don't use this!
/// </summary>
/// <inheritdoc />
public TypedImplementationFactoryServiceDescriptor(
Type serviceType,
object? serviceKey,
Func<IServiceProvider, object?, object> factory,
ServiceLifetime lifetime)
: base(serviceType, serviceKey, factory, lifetime)
{
ThrowCtor();
} /// <summary>
/// Initializes a new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/> with the specified factory.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="factory">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service.</param>
/// <exception cref="ArgumentNullException"></exception>
/// <inheritdoc />
public TypedImplementationFactoryServiceDescriptor(
Type serviceType,
Func<IServiceProvider, Type, object> factory,
ServiceLifetime lifetime)
: base(serviceType, ThrowFactory, lifetime)
{
CheckOpenGeneric(serviceType);
_typedImplementationFactory = factory ?? throw new ArgumentNullException(nameof(factory));
} /// <summary>
/// Initializes a new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/> with the specified factory.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="factory">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service.</param>
/// <exception cref="ArgumentNullException"></exception>
/// <inheritdoc />
public TypedImplementationFactoryServiceDescriptor(
Type serviceType,
object? serviceKey,
Func<IServiceProvider, object?, Type, object> factory,
ServiceLifetime lifetime)
: base(serviceType, serviceKey, ThrowKeyedFactory, lifetime)
{
CheckOpenGeneric(serviceType);
_typedKeyedImplementationFactory = factory ?? throw new ArgumentNullException(nameof(factory));
} /// <summary>
/// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
/// with the specified service in <paramref name="implementationFactory"/> and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="implementationFactory">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
public static TypedImplementationFactoryServiceDescriptor Singleton(
Type serviceType,
Func<IServiceProvider, Type, object> implementationFactory)
{
return new(serviceType, implementationFactory, ServiceLifetime.Singleton);
} /// <summary>
/// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
/// with the specified service in <paramref name="implementationFactory"/> and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="implementationFactory">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
public static TypedImplementationFactoryServiceDescriptor Singleton<TService>(
Func<IServiceProvider, Type, object> implementationFactory)
where TService : class
{
return new(typeof(TService), implementationFactory, ServiceLifetime.Singleton);
} /// <summary>
/// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
/// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
public static TypedImplementationFactoryServiceDescriptor KeyedSingleton(
Type serviceType,
object? serviceKey,
Func<IServiceProvider, object?, Type, object> implementationType)
{
return new(serviceType, serviceKey, implementationType, ServiceLifetime.Singleton);
} /// <summary>
/// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
/// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
public static TypedImplementationFactoryServiceDescriptor KeyedSingleton<TService>(
object? serviceKey,
Func<IServiceProvider, object?, Type, object> implementationType)
where TService : class
{
return new(typeof(TService), serviceKey, implementationType, ServiceLifetime.Singleton);
} /// <summary>
/// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
/// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
public static TypedImplementationFactoryServiceDescriptor Scoped(
Type serviceType,
Func<IServiceProvider, Type, object> implementationType)
{
return new(serviceType, implementationType, ServiceLifetime.Scoped);
} /// <summary>
/// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
/// with the specified service in <paramref name="implementationFactory"/> and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="implementationFactory">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
public static TypedImplementationFactoryServiceDescriptor Scoped<TService>(
Func<IServiceProvider, Type, object> implementationFactory)
where TService : class
{
return new(typeof(TService), implementationFactory, ServiceLifetime.Scoped);
} /// <summary>
/// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
/// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
public static TypedImplementationFactoryServiceDescriptor KeyedScoped(
Type serviceType,
object? serviceKey,
Func<IServiceProvider, object?, Type, object> implementationType)
{
return new(serviceType, serviceKey, implementationType, ServiceLifetime.Scoped);
} /// <summary>
/// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
/// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
public static TypedImplementationFactoryServiceDescriptor KeyedScoped<TService>(
object? serviceKey,
Func<IServiceProvider, object?, Type, object> implementationType)
where TService : class
{
return new(typeof(TService), serviceKey, implementationType, ServiceLifetime.Scoped);
} /// <summary>
/// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
/// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
public static TypedImplementationFactoryServiceDescriptor Transient(
Type serviceType,
Func<IServiceProvider, Type, object> implementationType)
{
return new(serviceType, implementationType, ServiceLifetime.Transient);
} /// <summary>
/// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
/// with the specified service in <paramref name="implementationFactory"/> and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="implementationFactory">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
public static TypedImplementationFactoryServiceDescriptor Transient<TService>(
Func<IServiceProvider, Type, object> implementationFactory)
where TService : class
{
return new(typeof(TService), implementationFactory, ServiceLifetime.Transient);
} /// <summary>
/// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
/// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
public static TypedImplementationFactoryServiceDescriptor KeyedTransient(
Type serviceType,
object? serviceKey,
Func<IServiceProvider, object?, Type, object> implementationType)
{
return new(serviceType, serviceKey, implementationType, ServiceLifetime.Transient);
} /// <summary>
/// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
/// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
/// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
public static TypedImplementationFactoryServiceDescriptor KeyedTransient<TService>(
object? serviceKey,
Func<IServiceProvider, object?, Type, object> implementationType)
where TService : class
{
return new(typeof(TService), serviceKey, implementationType, ServiceLifetime.Transient);
} private string DebuggerToString()
{
string text = $"Lifetime = {Lifetime}, ServiceType = \"{ServiceType.FullName}\"";
if (IsKeyedService)
{
text += $", ServiceKey = \"{ServiceKey}\""; return text + $", TypedKeyedImplementationFactory = {TypedKeyedImplementationFactory!.Method}";
}
else
{
return text + $", TypedImplementationFactory = {TypedImplementationFactory!.Method}";
}
} private static void ThrowCtor()
{
throw new NotSupportedException($"{nameof(TypedImplementationFactoryServiceDescriptor)} only use for typed factory.");
} private static object ThrowFactory(IServiceProvider serviceProvider)
{
throw new InvalidOperationException("Please use typed factory instead.");
} private static object ThrowKeyedFactory(IServiceProvider serviceProvider, object? serviceKey)
{
throw new InvalidOperationException("Please use typed keyed factory instead.");
} private static void CheckOpenGeneric(Type serviceType)
{
if (!serviceType.IsGenericTypeDefinition)
throw new InvalidOperationException($"{nameof(TypedImplementationFactoryServiceDescriptor)} only used for generic type definition(open generic type).");
}
}

这个类很简单,就是增加了用于保存Func<IServiceProvider, Type, object>Func<IServiceProvider, object?, Type, object>工厂委托的字段和配套的构造方法和验证逻辑。基类提供的所有功能均直接抛出异常,专门负责新增功能。

ImplementationFactoryServiceTypeHolder

internal sealed class ImplementationFactoryServiceTypeHolder(Type serviceType)
{
private readonly Func<IServiceProvider, object?> _factory = sp => sp.GetService(serviceType); public Func<IServiceProvider, object?> Factory => _factory;
} internal sealed class KeyedImplementationFactoryServiceTypeHolder(Type serviceType)
{
private readonly Func<IServiceProvider, object?, object?> _factory = (sp, key) => (sp as IKeyedServiceProvider)?.GetKeyedService(serviceType, key); public Func<IServiceProvider, object?, object?> Factory => _factory;
} internal sealed class OpenGenericImplementationFactoryServiceTypeHolder(Type serviceType)
{
private readonly Func<IServiceProvider, Type, object?> _factory = serviceType.IsGenericTypeDefinition
? (sp, type) =>
{
var closed = serviceType.MakeGenericType(type.GenericTypeArguments);
return sp.GetService(closed);
}
: throw new ArgumentException($"{nameof(serviceType)} is not generic type definition."); public Func<IServiceProvider, Type, object?> Factory => _factory;
} internal sealed class KeyedOpenGenericImplementationFactoryServiceTypeHolder(Type serviceType)
{
private readonly Func<IServiceProvider, object?, Type, object?> _factory = serviceType.IsGenericTypeDefinition
? (sp, key, type) =>
{
var closed = serviceType.MakeGenericType(type.GenericTypeArguments);
return (sp as IKeyedServiceProvider)?.GetKeyedService(closed, key);
}
: throw new ArgumentException($"{nameof(serviceType)} is not generic type definition."); public Func<IServiceProvider, object?, Type, object?> Factory => _factory;
}

这个类也很简单,只负责持有服务类型,并把新的工厂类型转换到原始工厂类型方便集成进内置容器。并且这是内部辅助类型,对开发者是无感知的。

易用性扩展

最后就是提供扩展方法提供和内置容器相似的使用体验。由于本次扩展的主要目的是实现开放发型的服务转发,因此笔者专门准备了一套用来注册服务转发的AddForward系列扩展方便使用。此处只列出部分预览。

/// <summary>
/// Adds a service of the type specified in <paramref name="serviceType"/> with a factory
/// specified in <paramref name="implementationFactory"/> to the specified <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
/// <param name="serviceType">The type of the service to register.</param>
/// <param name="implementationFactory">The factory that creates the service.</param>
/// <param name="serviceLifetime">The <see cref="ServiceLifetime"/> of <paramref name="serviceType"/>.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
public static IServiceCollection AddTypedFactory(
this IServiceCollection services,
Type serviceType,
Func<IServiceProvider, Type, object> implementationFactory,
ServiceLifetime serviceLifetime)
{
services.Add(new TypedImplementationFactoryServiceDescriptor(serviceType, implementationFactory, serviceLifetime));
return services;
} /// <summary>
/// Adds a service of the type specified in <paramref name="serviceType"/> with a factory
/// specified in <paramref name="implementationFactory"/> to the specified <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
/// <param name="serviceType">The type of the service to register.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationFactory">The factory that creates the service.</param>
/// <param name="serviceLifetime">The <see cref="ServiceLifetime"/> of <paramref name="serviceType"/>.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
public static IServiceCollection AddKeyedTypedFactory(
this IServiceCollection services,
Type serviceType,
object? serviceKey,
Func<IServiceProvider, object?, Type, object> implementationFactory,
ServiceLifetime serviceLifetime)
{
services.Add(new TypedImplementationFactoryServiceDescriptor(serviceType, serviceKey, implementationFactory, serviceLifetime));
return services;
} /// <summary>
/// Adds a service of the type specified in <paramref name="serviceType"/> with a forward of the type
/// specified in <paramref name="forwardTargetServiceType"/> to the specified <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
/// <param name="serviceType">The type of the service to register.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="forwardTargetServiceType">The forward type of the service.</param>
/// <param name="serviceLifetime">The <see cref="ServiceLifetime"/> of <paramref name="serviceType"/>.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
public static IServiceCollection AddKeyedForward(
this IServiceCollection services,
Type serviceType,
object? serviceKey,
Type forwardTargetServiceType,
ServiceLifetime serviceLifetime)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(serviceType);
ArgumentNullException.ThrowIfNull(forwardTargetServiceType); if (serviceType.IsGenericTypeDefinition)
{
services.Add(new TypedImplementationFactoryServiceDescriptor(serviceType, serviceKey, new KeyedOpenGenericImplementationFactoryServiceTypeHolder(forwardTargetServiceType).Factory!, serviceLifetime));
}
else
{
services.Add(new ServiceDescriptor(serviceType, serviceKey, new KeyedImplementationFactoryServiceTypeHolder(forwardTargetServiceType).Factory!, serviceLifetime));
} return services;
}

从示例可以发现如果类型不是开放泛型,是直接使用原始类型进行注册的。也就是说如果安装这个抽象包,但是不使用开放泛型的相关功能,是可以直接用原始内置容器的。

CoreDX.Extensions.DependencyInjection

这是修改后的服务容器实现,增加了对带服务类型的自定义工厂的支持,其他内置功能完全不变。

CallSiteFactory

internal sealed partial class CallSiteFactory : IServiceProviderIsService, IServiceProviderIsKeyedService
{
// 其他原始代码 private void Populate()
{
foreach (ServiceDescriptor descriptor in _descriptors)
{
Type serviceType = descriptor.ServiceType; #region 验证可识别请求类型的服务实现工厂 if (descriptor is TypedImplementationFactoryServiceDescriptor typedFactoryDescriptor)
{
if(typedFactoryDescriptor.IsKeyedService && typedFactoryDescriptor.TypedKeyedImplementationFactory == null)
{
throw new ArgumentException(
$"Keyed open generic service {serviceType} requires {nameof(typedFactoryDescriptor.TypedKeyedImplementationFactory)}",
"descriptors");
}
else if (!typedFactoryDescriptor.IsKeyedService && typedFactoryDescriptor.TypedImplementationFactory == null)
{
throw new ArgumentException(
$"Open generic service {serviceType} requires {nameof(typedFactoryDescriptor.TypedImplementationFactory)}",
"descriptors");
}
} #endregion // 其他原始代码
}
} private ServiceCallSite? TryCreateExact(ServiceDescriptor descriptor, ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain, int slot)
{
if (serviceIdentifier.ServiceType == descriptor.ServiceType)
{
ServiceCacheKey callSiteKey = new ServiceCacheKey(serviceIdentifier, slot);
if (_callSiteCache.TryGetValue(callSiteKey, out ServiceCallSite? serviceCallSite))
{
return serviceCallSite;
} ServiceCallSite callSite;
var lifetime = new ResultCache(descriptor.Lifetime, serviceIdentifier, slot); // 其他原始代码 #region 为可识别请求类型的服务工厂注册服务实现工厂 else if (TryCreateTypedFactoryCallSite(lifetime, descriptor as TypedImplementationFactoryServiceDescriptor, descriptor.ServiceType) is ServiceCallSite factoryCallSite)
{
callSite = factoryCallSite;
} #endregion // 其他原始代码 return _callSiteCache[callSiteKey] = callSite;
} return null;
} [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:MakeGenericType",
Justification = "MakeGenericType here is used to create a closed generic implementation type given the closed service type. " +
"Trimming annotations on the generic types are verified when 'Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability' is set, which is set by default when PublishTrimmed=true. " +
"That check informs developers when these generic types don't have compatible trimming annotations.")]
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
Justification = "When ServiceProvider.VerifyAotCompatibility is true, which it is by default when PublishAot=true, " +
"this method ensures the generic types being created aren't using ValueTypes.")]
private ServiceCallSite? TryCreateOpenGeneric(ServiceDescriptor descriptor, ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain, int slot, bool throwOnConstraintViolation)
{
if (serviceIdentifier.IsConstructedGenericType &&
serviceIdentifier.ServiceType.GetGenericTypeDefinition() == descriptor.ServiceType)
{
ServiceCacheKey callSiteKey = new ServiceCacheKey(serviceIdentifier, slot);
if (_callSiteCache.TryGetValue(callSiteKey, out ServiceCallSite? serviceCallSite))
{
return serviceCallSite;
} Type? implementationType = descriptor.GetImplementationType();
//Debug.Assert(implementationType != null, "descriptor.ImplementationType != null"); // 延迟断言,此处可能是开放泛型工厂
var lifetime = new ResultCache(descriptor.Lifetime, serviceIdentifier, slot);
Type closedType;
try
{
Type[] genericTypeArguments = serviceIdentifier.ServiceType.GenericTypeArguments;
if (TypedImplementationFactoryServiceProvider.VerifyAotCompatibility)
{
VerifyOpenGenericAotCompatibility(serviceIdentifier.ServiceType, genericTypeArguments);
} #region 为开放式泛型服务添加可识别请求类型的服务实现工厂 if (descriptor is TypedImplementationFactoryServiceDescriptor typedFactoryDescriptor)
{
closedType = typedFactoryDescriptor.ServiceType.MakeGenericType(genericTypeArguments);
if (TryCreateTypedFactoryCallSite(lifetime, typedFactoryDescriptor, closedType) is ServiceCallSite factoryCallSite)
{
return _callSiteCache[callSiteKey] = factoryCallSite;
}
else
{
return null;
}
} // 断言移动到此处
Debug.Assert(implementationType != null, "descriptor.ImplementationType != null"); #endregion closedType = implementationType.MakeGenericType(genericTypeArguments);
}
catch (ArgumentException)
{
if (throwOnConstraintViolation)
{
throw;
} return null;
} return _callSiteCache[callSiteKey] = CreateConstructorCallSite(lifetime, serviceIdentifier, closedType, callSiteChain);
} return null;
} // 其他原始代码
}

这是整个改造的关键,理论上来说只要这个类是普通类的话完全可以直接通过继承把功能加上,可惜不是。此处只展示修改的部分。然后把辅助方法定义到另一个文件,方便利用部分类的特点尽量减少对原始代码的改动,方便将来同步官方代码。

internal sealed partial class CallSiteFactory
{
/// <summary>
/// 尝试创建可识别请求类型的工厂调用点
/// </summary>
/// <param name="lifetime"></param>
/// <param name="descriptor"></param>
/// <param name="serviceType">服务类型</param>
/// <returns></returns>
private static FactoryCallSite? TryCreateTypedFactoryCallSite(
ResultCache lifetime,
TypedImplementationFactoryServiceDescriptor? descriptor,
Type serviceType)
{
ArgumentNullException.ThrowIfNull(serviceType); if (descriptor == null) { }
else if (descriptor.IsKeyedService && descriptor.TypedKeyedImplementationFactory != null)
{
return new FactoryCallSite(lifetime, descriptor.ServiceType, descriptor.ServiceKey!, new TypedKeyedServiceImplementationFactoryHolder(descriptor.TypedKeyedImplementationFactory!, serviceType).Factory);
}
else if (!descriptor.IsKeyedService && descriptor.TypedImplementationFactory != null)
{
return new FactoryCallSite(lifetime, descriptor.ServiceType, new TypedServiceImplementationFactoryHolder(descriptor.TypedImplementationFactory!, serviceType).Factory);
} return null;
}
}

TypedServiceImplementationFactoryHolder

internal sealed class TypedServiceImplementationFactoryHolder
{
private readonly Func<IServiceProvider, Type, object> _factory;
private readonly Type _serviceType; internal TypedServiceImplementationFactoryHolder(Func<IServiceProvider, Type, object> factory, Type serviceType)
{
_factory = factory ?? throw new ArgumentNullException(nameof(factory));
_serviceType = serviceType ?? throw new ArgumentNullException(nameof(serviceType));
} internal Func<IServiceProvider, object> Factory => FactoryFunc; private object FactoryFunc(IServiceProvider provider)
{
return _factory(provider, _serviceType);
}
} internal sealed class TypedKeyedServiceImplementationFactoryHolder
{
private readonly Func<IServiceProvider, object?, Type, object> _factory;
private readonly Type _serviceType; internal TypedKeyedServiceImplementationFactoryHolder(Func<IServiceProvider, object?, Type, object> factory, Type serviceType)
{
_factory = factory ?? throw new ArgumentNullException(nameof(factory));
_serviceType = serviceType ?? throw new ArgumentNullException(nameof(serviceType));
} internal Func<IServiceProvider, object?, object> Factory => FactoryFunc; private object FactoryFunc(IServiceProvider provider, object? serviceKey)
{
return _factory(provider, serviceKey, _serviceType);
}
}

因为笔者直接使用了内置类型,因此需要把工厂委托转换成内置容器支持的签名。Holder辅助类就可以把类型信息保存为内部字段,对外暴露的工厂签名就可以不需要类型参数了。

最后为避免引起误解,笔者修改了类名,但保留文件名方便比对原始仓库代码。至此,改造其实已经完成。可以看出改动真的很少,不知道为什么微软就是不改。

CoreDX.Extensions.DependencyInjection.Hosting.Abstractions

虽然经过上面的改造后,改版容器已经能用了,但是为了方便和通用主机系统集成还是要提供一个替换容器用的扩展。

TypedImplementationFactoryHostingHostBuilderExtensions

public static class TypedImplementationFactoryHostingHostBuilderExtensions
{
/// <summary>
/// Specify the <see cref="IServiceProvider"/> to be the typed implementation factory supported one.
/// </summary>
/// <param name="hostBuilder">The <see cref="IHostBuilder"/> to configure.</param>
/// <returns>The <see cref="IHostBuilder"/>.</returns>
public static IHostBuilder UseTypedImplementationFactoryServiceProvider(
this IHostBuilder hostBuilder)
=> hostBuilder.UseTypedImplementationFactoryServiceProvider(static _ => { }); /// <summary>
/// Specify the <see cref="IServiceProvider"/> to be the typed implementation factory supported one.
/// </summary>
/// <param name="hostBuilder">The <see cref="IHostBuilder"/> to configure.</param>
/// <param name="configure">The delegate that configures the <see cref="IServiceProvider"/>.</param>
/// <returns>The <see cref="IHostBuilder"/>.</returns>
public static IHostBuilder UseTypedImplementationFactoryServiceProvider(
this IHostBuilder hostBuilder,
Action<ServiceProviderOptions> configure)
=> hostBuilder.UseTypedImplementationFactoryServiceProvider((context, options) => configure(options)); /// <summary>
/// Specify the <see cref="IServiceProvider"/> to be the typed implementation factory supported one.
/// </summary>
/// <param name="hostBuilder">The <see cref="IHostBuilder"/> to configure.</param>
/// <param name="configure">The delegate that configures the <see cref="IServiceProvider"/>.</param>
/// <returns>The <see cref="IHostBuilder"/>.</returns>
public static IHostBuilder UseTypedImplementationFactoryServiceProvider(
this IHostBuilder hostBuilder,
Action<HostBuilderContext, ServiceProviderOptions> configure)
{
return hostBuilder.UseServiceProviderFactory(context =>
{
var options = new ServiceProviderOptions();
configure(context, options);
return new TypedImplementationFactoryServiceProviderFactory(options);
});
}
}

至此,主机集成工作也完成了。本来打算就这么结束的,结果突然想起来,开放泛型问题解决了,键控服务也有了,之前一直不知道怎么办的动态代理貌似是有戏了,就又研究起来了。

CoreDX.Extensions.DependencyInjection.Proxies.Abstractions

之前动态代理不好实现主要是因为代理服务和原始服务的注册类型相同,实在是没办法。既然现在有键控服务了,那么把原始服务和代理服务用键分开就完美搞定,最后一个问题就是键要怎么处理。通过文档可知键控服务的键可以是任意object,只要实现合理的相等性判断即可。因此笔者决定使用专用的类型来表示代理服务的键,并通过对string类型的特殊处理来实现特性键指定的兼容。

ImplicitProxyServiceOriginalServiceKey

/// <summary>
/// Service key for access original service that already added as implicit proxy.
/// </summary>
public sealed class ImplicitProxyServiceOriginalServiceKey
: IEquatable<ImplicitProxyServiceOriginalServiceKey>
#if NET7_0_OR_GREATER
, IEqualityOperators<ImplicitProxyServiceOriginalServiceKey, ImplicitProxyServiceOriginalServiceKey, bool>
, IEqualityOperators<ImplicitProxyServiceOriginalServiceKey, object, bool>
#endif
{
private const int _hashCodeBase = 870983858; private readonly bool _isStringMode;
private readonly object? _originalServiceKey; private static readonly ImplicitProxyServiceOriginalServiceKey _default = CreateOriginalServiceKey(null);
private static readonly ImplicitProxyServiceOriginalServiceKey _stringDefault = CreateStringOriginalServiceKey(null); /// <summary>
/// Prefix for access original <see cref="string"/> based keyed service that already added as implicit proxy.
/// </summary>
public const string DefaultStringPrefix = $"[{nameof(CoreDX)}.{nameof(Extensions)}.{nameof(DependencyInjection)}.{nameof(Proxies)}.{nameof(ImplicitProxyServiceOriginalServiceKey)}](ImplicitDefault)"; /// <summary>
/// Default original service key for none keyed proxy service.
/// </summary>
public static ImplicitProxyServiceOriginalServiceKey Default => _default; /// <summary>
/// Default original service key for none <see cref="string"/> based keyed proxy service.
/// </summary>
public static ImplicitProxyServiceOriginalServiceKey StringDefault => _stringDefault; /// <summary>
/// Service key of original service.
/// </summary>
public object? OriginalServiceKey => _originalServiceKey; public bool Equals(ImplicitProxyServiceOriginalServiceKey? other)
{
return Equals((object?)other);
} public override bool Equals(object? obj)
{
if (_isStringMode && obj is string str) return $"{DefaultStringPrefix}{_originalServiceKey}" == str;
else
{
var isEquals = obj is not null and ImplicitProxyServiceOriginalServiceKey other
&& ((_originalServiceKey is null && other._originalServiceKey is null) || _originalServiceKey?.Equals(other._originalServiceKey) is true); return isEquals;
}
} public static bool operator ==(ImplicitProxyServiceOriginalServiceKey? left, ImplicitProxyServiceOriginalServiceKey? right)
{
return left?.Equals(right) is true;
} public static bool operator !=(ImplicitProxyServiceOriginalServiceKey? left, ImplicitProxyServiceOriginalServiceKey? right)
{
return !(left == right);
} public static bool operator ==(ImplicitProxyServiceOriginalServiceKey? left, object? right)
{
return left?.Equals(right) is true;
} public static bool operator !=(ImplicitProxyServiceOriginalServiceKey? left, object? right)
{
return !(left == right);
} public static bool operator ==(object? left, ImplicitProxyServiceOriginalServiceKey? right)
{
return right == left;
} public static bool operator !=(object? left, ImplicitProxyServiceOriginalServiceKey? right)
{
return right != left;
} public override int GetHashCode()
{
return _isStringMode
? $"{DefaultStringPrefix}{_originalServiceKey}".GetHashCode()
: HashCode.Combine(_hashCodeBase, _originalServiceKey);
} /// <summary>
/// Creates an instance of <see cref="ImplicitProxyServiceOriginalServiceKey"/> with the specified service key in <paramref name="originalServiceKey"/>.
/// </summary>
/// <param name="originalServiceKey"></param>
/// <returns>A new instance of <see cref="ImplicitProxyServiceOriginalServiceKey"/>.</returns>
public static ImplicitProxyServiceOriginalServiceKey CreateOriginalServiceKey(object? originalServiceKey)
{
return new(originalServiceKey, false);
} /// <summary>
/// Creates an instance of <see cref="ImplicitProxyServiceOriginalServiceKey"/> with the specified <see cref="string"/> based service key in <paramref name="originalServiceKey"/>.
/// </summary>
/// <param name="originalServiceKey"></param>
/// <returns>A new instance of <see cref="ImplicitProxyServiceOriginalServiceKey"/>.</returns>
public static ImplicitProxyServiceOriginalServiceKey CreateStringOriginalServiceKey(string? originalServiceKey)
{
return new(originalServiceKey, true);
} private ImplicitProxyServiceOriginalServiceKey(object? originalServiceKey, bool isStringMode)
{
_originalServiceKey = originalServiceKey;
_isStringMode = isStringMode;
}
}

对.NET 7以上版本,把运算符实现为接口。

ProxyService

/// <summary>
/// The interface for get explicit proxy service.
/// </summary>
/// <typeparam name="TService">The type of original service to get explicit proxy.</typeparam>
public interface IProxyService<out TService>
where TService : class
{
/// <summary>
/// Get proxy service instance of type <typeparamref name="TService"/>.
/// </summary>
TService Proxy { get; }
} /// <summary>
/// The type for get explicit proxy service.
/// </summary>
/// <typeparam name="TService">The type of original service to get explicit proxy.</typeparam>
/// <param name="service">Object instance of original service to be proxy.</param>
internal sealed class ProxyService<TService>(TService service) : IProxyService<TService>
where TService : class
{
public TService Proxy { get; } = service;
}

除了隐式代理,笔者还准备了显式代理,这也是笔者要在内置容器上扩展而不是去用其他第三方容器的一个原因。第三方容器代理后原始服务就被隐藏了,在某些极端情况下万一要用到原始服务就没办法了。

CastleDynamicProxyDependencyInjectionExtensions

此处只展示部分预览。

/// <summary>
/// Adds a explicit proxy for the type specified in <paramref name="serviceType"/> with interceptors
/// specified in <paramref name="interceptorTypes"/> to the specified <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the service proxy to.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="serviceType">The type of the service to add proxy.</param>
/// <param name="serviceLifetime">The <see cref="ServiceLifetime"/> of <paramref name="serviceType"/> and <paramref name="interceptorTypes"/>.</param>
/// <param name="interceptorTypes">The interceptor types of the service proxy.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
/// <remarks>Use <see cref="IProxyService{TService}"/> to get proxy service.</remarks>
public static IServiceCollection AddKeyedExplicitProxy(
this IServiceCollection services,
Type serviceType,
object? serviceKey,
ServiceLifetime serviceLifetime,
params Type[] interceptorTypes)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(serviceType);
CheckInterface(serviceType);
CheckInterceptor(interceptorTypes); if (serviceType.IsGenericTypeDefinition)
{
services.TryAddKeyedSingleton<IStartupOpenGenericServiceProxyRegister>(serviceKey, new StartupOpenGenericServiceProxyRegister()); var startupOpenGenericServiceProxyRegister = services
.LastOrDefault(service => service.IsKeyedService && service.ServiceKey == serviceKey && service.ServiceType == typeof(IStartupOpenGenericServiceProxyRegister))
?.KeyedImplementationInstance as IStartupOpenGenericServiceProxyRegister
?? throw new InvalidOperationException($"Can not found keyed(key value: {serviceKey}) service of type {nameof(IStartupOpenGenericServiceProxyRegister)}"); startupOpenGenericServiceProxyRegister?.Add(serviceType); services.TryAdd(new TypedImplementationFactoryServiceDescriptor(
typeof(IProxyService<>),
serviceKey,
(provider, serviceKey, requestedServiceType) =>
{
var proxyServiceType = requestedServiceType.GenericTypeArguments[0]; var registered = CheckKeyedOpenGenericServiceProxyRegister(provider, serviceKey, proxyServiceType.GetGenericTypeDefinition());
if (!registered) return null!; var proxy = CreateKeyedProxyObject(provider, proxyServiceType, serviceKey, interceptorTypes); return Activator.CreateInstance(typeof(ProxyService<>).MakeGenericType(proxy.GetType()), proxy)!;
},
serviceLifetime));
}
else
{
services.Add(new ServiceDescriptor(
typeof(IProxyService<>).MakeGenericType(serviceType),
serviceKey,
(provider, serviceKey) =>
{
var proxy = CreateKeyedProxyObject(provider, serviceType, serviceKey, interceptorTypes);
return Activator.CreateInstance(typeof(ProxyService<>).MakeGenericType(proxy.GetType()), proxy)!;
},
serviceLifetime));
} services.TryAddKeyedInterceptors(serviceKey, serviceLifetime, interceptorTypes); return services;
} /// <summary>
/// Adds a implicit proxy for the type specified in <paramref name="serviceType"/> with interceptors
/// specified in <paramref name="interceptorTypes"/> to the specified <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the service proxy to.</param>
/// <param name="serviceType">The type of the service to add proxy.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="serviceLifetime">The <see cref="ServiceLifetime"/> of <paramref name="serviceType"/> and <paramref name="interceptorTypes"/>.</param>
/// <param name="interceptorTypes">The interceptor types of the service proxy.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
/// <remarks>
/// Use key <see cref="ImplicitProxyServiceOriginalServiceKey.CreateOriginalServiceKey(object?)"/>
/// or <see cref="ImplicitProxyServiceOriginalServiceKey.CreateStringOriginalServiceKey(string?)"/> if <paramref name="serviceKey"/> is <see cref="string"/>
/// or <see cref="ImplicitProxyServiceOriginalServiceKey.DefaultStringPrefix"/> + <paramref name="serviceKey"/> if <paramref name="serviceKey"/>
/// is <see cref="string"/>(eg. Constant value for <see cref="FromKeyedServicesAttribute"/>.) to get original service.
/// </remarks>
public static IServiceCollection AddKeyedImplicitProxy(
this IServiceCollection services,
Type serviceType,
object? serviceKey,
ServiceLifetime serviceLifetime,
params Type[] interceptorTypes)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(serviceType);
CheckInterface(serviceType);
CheckInterceptor(interceptorTypes); var originalServiceDescriptor = services.LastOrDefault(service => service.IsKeyedService && service.ServiceKey == serviceKey && service.ServiceType == serviceType && service.Lifetime == serviceLifetime)
?? throw new ArgumentException($"Not found registered keyed(key value: {serviceKey}) \"{Enum.GetName(serviceLifetime)}\" service of type {serviceType.Name}.", nameof(serviceType)); var newServiceKey = CreateOriginalServiceKey(serviceKey);
var serviceDescriptorIndex = services.IndexOf(originalServiceDescriptor);
if (originalServiceDescriptor is TypedImplementationFactoryServiceDescriptor typedServiceDescriptor)
{
services.Insert(
serviceDescriptorIndex,
new TypedImplementationFactoryServiceDescriptor(
typedServiceDescriptor.ServiceType,
newServiceKey,
(serviceProvider, serviceKey, requestedServiceType) =>
{
Debug.Assert(serviceKey is ImplicitProxyServiceOriginalServiceKey, $"Implicit proxy not use {nameof(ImplicitProxyServiceOriginalServiceKey)}"); return typedServiceDescriptor.TypedKeyedImplementationFactory!(
serviceProvider,
(serviceKey as ImplicitProxyServiceOriginalServiceKey)?.OriginalServiceKey ?? serviceKey,
requestedServiceType);
},
originalServiceDescriptor.Lifetime)
);
}
else if (originalServiceDescriptor.KeyedImplementationInstance is not null)
{
services.Insert(
serviceDescriptorIndex,
new ServiceDescriptor(
originalServiceDescriptor.ServiceType,
newServiceKey,
originalServiceDescriptor.KeyedImplementationInstance)
);
}
else if (originalServiceDescriptor.KeyedImplementationType is not null)
{
services.Insert(
serviceDescriptorIndex,
new ServiceDescriptor(
originalServiceDescriptor.ServiceType,
newServiceKey,
originalServiceDescriptor.KeyedImplementationType,
originalServiceDescriptor.Lifetime)
);
}
else if (originalServiceDescriptor.KeyedImplementationFactory is not null)
{
services.Insert(
serviceDescriptorIndex,
new ServiceDescriptor(
originalServiceDescriptor.ServiceType,
newServiceKey,
(serviceProvider, serviceKey) =>
{
return originalServiceDescriptor.KeyedImplementationFactory(
serviceProvider,
serviceKey);
},
originalServiceDescriptor.Lifetime)
);
}
else throw new Exception("Add proxy service fail."); if (serviceType.IsGenericTypeDefinition)
{
services.Add(new TypedImplementationFactoryServiceDescriptor(
serviceType,
serviceKey,
(provider, serviceKey, requestedServiceType) =>
{
var newLocalServiceKey = CreateOriginalServiceKey(serviceKey);
var proxy = CreateKeyedProxyObject(provider, requestedServiceType, newLocalServiceKey, interceptorTypes); return proxy;
},
serviceLifetime));
}
else
{
services.Add(new ServiceDescriptor(
serviceType,
serviceKey,
(provider, serviceKey) =>
{
var newLocalServiceKey = CreateOriginalServiceKey(serviceKey);
var proxy = CreateKeyedProxyObject(provider, serviceType, newLocalServiceKey, interceptorTypes); return proxy;
},
serviceLifetime));
} services.TryAddKeyedInterceptors(newServiceKey, serviceLifetime, interceptorTypes); services.Remove(originalServiceDescriptor); return services;
} /// <summary>
/// Solidify open generic service proxy register for the specified <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="containerBuilder">The <see cref="IServiceCollection"/> to solidify register.</param>
/// <remarks>Should call after last add proxy. If used for host, needn't call.</remarks>
public static void SolidifyOpenGenericServiceProxyRegister(this IServiceCollection containerBuilder)
{
var openGenericServiceProxyRegisters = containerBuilder
.Where(service => service.ServiceType == typeof(IStartupOpenGenericServiceProxyRegister))
.ToList(); var readOnlyOpenGenericServiceProxyRegisters = openGenericServiceProxyRegisters
.Where(service => service.Lifetime == ServiceLifetime.Singleton)
.Select(service =>
{
return service.IsKeyedService switch
{
true => ServiceDescriptor.KeyedSingleton<IOpenGenericServiceProxyRegister>(service.ServiceKey, new OpenGenericServiceProxyRegister((service.KeyedImplementationInstance! as IStartupOpenGenericServiceProxyRegister)!)),
false => ServiceDescriptor.Singleton<IOpenGenericServiceProxyRegister>(new OpenGenericServiceProxyRegister((service.ImplementationInstance! as IStartupOpenGenericServiceProxyRegister)!)),
};
}); foreach (var register in openGenericServiceProxyRegisters)
{
containerBuilder.Remove(register);
} foreach (var readOnlyRegister in readOnlyOpenGenericServiceProxyRegisters)
{
containerBuilder.Add(readOnlyRegister);
}
} private static object CreateProxyObject(
IServiceProvider provider,
Type serviceType,
Type[] interceptorTypes)
{
var target = provider.GetRequiredService(serviceType);
var interceptors = interceptorTypes.Select(t => GetInterceptor(provider.GetRequiredService(t))).ToArray();
var proxyGenerator = provider.GetRequiredService<IProxyGenerator>(); var proxy = proxyGenerator.CreateInterfaceProxyWithTarget(serviceType, target, interceptors);
return proxy;
} private static object CreateKeyedProxyObject(
IServiceProvider provider,
Type serviceType,
object? serviceKey,
Type[] interceptorTypes)
{
var target = provider.GetRequiredKeyedService(serviceType, serviceKey);
var interceptors = interceptorTypes.Select(t => GetInterceptor(provider.GetRequiredKeyedService(t, serviceKey))).ToArray();
var proxyGenerator = provider.GetRequiredService<IProxyGenerator>(); var proxy = proxyGenerator.CreateInterfaceProxyWithTarget(serviceType, target, interceptors);
return proxy;
} private static ImplicitProxyServiceOriginalServiceKey CreateOriginalServiceKey(object? serviceKey)
{
return serviceKey switch
{
string stringKey => ImplicitProxyServiceOriginalServiceKey.CreateStringOriginalServiceKey(stringKey),
_ => ImplicitProxyServiceOriginalServiceKey.CreateOriginalServiceKey(serviceKey)
};
} private static void TryAddInterceptors(
this IServiceCollection services,
ServiceLifetime lifetime,
params Type[] interceptorTypes)
{
services.TryAddSingleton<IProxyGenerator, ProxyGenerator>(); foreach (var interceptorType in interceptorTypes)
{
services.TryAdd(new ServiceDescriptor(interceptorType, interceptorType, lifetime));
}
} private static void TryAddKeyedInterceptors(
this IServiceCollection services,
object? serviceKey,
ServiceLifetime lifetime,
params Type[] interceptorTypes)
{
services.TryAddSingleton<IProxyGenerator, ProxyGenerator>(); foreach (var interceptorType in interceptorTypes)
{
services.TryAdd(new ServiceDescriptor(interceptorType, serviceKey, interceptorType, lifetime));
}
} private static IInterceptor GetInterceptor(object interceptor)
{
return (interceptor as IInterceptor)
?? (interceptor as IAsyncInterceptor)?.ToInterceptor()
?? throw new InvalidCastException($"{nameof(interceptor)} is not {nameof(IInterceptor)} or {nameof(IAsyncInterceptor)}.");
} private static void CheckInterface(Type serviceType)
{
if (!serviceType.IsInterface)
throw new InvalidOperationException($"Proxy need interface but {nameof(serviceType)} is not interface.");
} private static void CheckInterceptor(params Type[] types)
{
foreach (var type in types)
{
if (!(type.IsAssignableTo(typeof(IInterceptor)) || type.IsAssignableTo(typeof(IAsyncInterceptor))))
throw new ArgumentException($"Exist element in {nameof(types)} is not {nameof(IInterceptor)} or {nameof(IAsyncInterceptor)}.", $"{nameof(types)}");
}
} private static bool CheckOpenGenericServiceProxyRegister(IServiceProvider serviceProvider, Type serviceType)
{
var register = serviceProvider.GetService<IOpenGenericServiceProxyRegister>();
CheckOpenGenericServiceProxyRegister(register);
return register!.Contains(serviceType);
} private static bool CheckKeyedOpenGenericServiceProxyRegister(IServiceProvider serviceProvider, object? serviceKey, Type serviceType)
{
var register = serviceProvider.GetKeyedService<IOpenGenericServiceProxyRegister>(serviceKey);
CheckOpenGenericServiceProxyRegister(register);
return register!.Contains(serviceType);
} private static void CheckOpenGenericServiceProxyRegister(IOpenGenericServiceProxyRegister? register)
{
if (register is null) throw new InvalidOperationException($"Can not found required service of type {nameof(IOpenGenericServiceProxyRegister)}. Maybe you forgot to call method named {nameof(IServiceCollection)}.{nameof(SolidifyOpenGenericServiceProxyRegister)}().");
} private sealed class StartupOpenGenericServiceProxyRegister : List<Type>, IStartupOpenGenericServiceProxyRegister;

使用扩展方法把代理完全实现为普通服务注册最大限度减少对服务容器的入侵。对于隐式代理,原始服务会被原地替换为代理服务,原始服务则使用新键重新注册,因此注册代理前需要先注册原始服务。

显式代理的开放泛型处理

public interface IOpenGenericServiceProxyRegister : IReadOnlySet<Type>;

internal interface IStartupOpenGenericServiceProxyRegister : IList<Type>;

public sealed class OpenGenericServiceProxyRegister(IEnumerable<Type> types) : IOpenGenericServiceProxyRegister
{
private readonly ImmutableHashSet<Type> _types = types.Distinct().ToImmutableHashSet(); public int Count => _types.Count; public bool Contains(Type item) => _types.Contains(item); public bool IsProperSubsetOf(IEnumerable<Type> other) => _types.IsProperSubsetOf(other); public bool IsProperSupersetOf(IEnumerable<Type> other) => _types.IsProperSupersetOf(other); public bool IsSubsetOf(IEnumerable<Type> other) => _types.IsSubsetOf(other); public bool IsSupersetOf(IEnumerable<Type> other) => _types.IsSupersetOf(other); public bool Overlaps(IEnumerable<Type> other) => _types.Overlaps(other); public bool SetEquals(IEnumerable<Type> other) => _types.SetEquals(other); public IEnumerator<Type> GetEnumerator() => _types.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

显式代理实际上是一个开放泛型服务,因此对于注册过代理的类型需要单独记录,否则无法判断是否应该生成相应的封闭类型。而注册记录在实例化容器后就不应该被修改,因此又提供了SolidifyOpenGenericServiceProxyRegister方法用来固化记录。

CoreDX.Extensions.DependencyInjection.Hosting.Proxies.Abstractions

因为代理服务有一个额外步骤,开发时可能忘记调用,为继续简化和通用主机的集成,把注册记录的固化工作放到主机的容器工厂中进行。

ProxyTypedImplementationFactoryServiceProviderFactory

public class ProxyTypedImplementationFactoryServiceProviderFactory : TypedImplementationFactoryServiceProviderFactory
{
public ProxyTypedImplementationFactoryServiceProviderFactory() : base() { } public ProxyTypedImplementationFactoryServiceProviderFactory(ServiceProviderOptions options) : base(options) { } public override IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder)
{
containerBuilder.SolidifyOpenGenericServiceProxyRegister(); return base.CreateServiceProvider(containerBuilder);
}
}

这个工厂唯一的工作就是在内部完成固化操作,这样就可以不用对Startup类做任何修改了。

ProxyTypedImplementationFactoryHostingHostBuilderExtensions

public static class ProxyTypedImplementationFactoryHostingHostBuilderExtensions
{
/// <summary>
/// Specify the <see cref="IServiceProvider"/> to be the dynamic proxy and typed implementation factory supported one.
/// </summary>
/// <param name="hostBuilder">The <see cref="IHostBuilder"/> to configure.</param>
/// <returns>The <see cref="IHostBuilder"/>.</returns>
public static IHostBuilder UseProxyTypedImplementationFactoryServiceProvider(
this IHostBuilder hostBuilder)
=> hostBuilder.UseProxyTypedImplementationFactoryServiceProvider(static _ => { }); /// <summary>
/// Specify the <see cref="IServiceProvider"/> to be the dynamic proxy and typed implementation factory supported one.
/// </summary>
/// <param name="hostBuilder">The <see cref="IHostBuilder"/> to configure.</param>
/// <param name="configure">The delegate that configures the <see cref="IServiceProvider"/>.</param>
/// <returns>The <see cref="IHostBuilder"/>.</returns>
public static IHostBuilder UseProxyTypedImplementationFactoryServiceProvider(
this IHostBuilder hostBuilder,
Action<ServiceProviderOptions> configure)
=> hostBuilder.UseProxyTypedImplementationFactoryServiceProvider((context, options) => configure(options)); /// <summary>
/// Specify the <see cref="IServiceProvider"/> to be the dynamic proxy and typed implementation factory supported one.
/// </summary>
/// <param name="hostBuilder">The <see cref="IHostBuilder"/> to configure.</param>
/// <param name="configure">The delegate that configures the <see cref="IServiceProvider"/>.</param>
/// <returns>The <see cref="IHostBuilder"/>.</returns>
public static IHostBuilder UseProxyTypedImplementationFactoryServiceProvider(
this IHostBuilder hostBuilder,
Action<HostBuilderContext, ServiceProviderOptions> configure)
{
return hostBuilder.UseServiceProviderFactory(context =>
{
var options = new ServiceProviderOptions();
configure(context, options);
return new ProxyTypedImplementationFactoryServiceProviderFactory(options);
});
}
}

使用新的扩展方法替换容器工厂即可。

简单使用示例

internal class Program
{
private static void Main(string[] args)
{
Explicit();
KeyedExplicit();
Implicit();
KeyedImplicit();
} private static void Explicit()
{
IServiceCollection services = new ServiceCollection(); services.AddScoped(typeof(IB<>), typeof(B<>));
services.AddScopedForward(typeof(IA<>), typeof(IB<>));
services.AddScopedExplicitProxy(typeof(IA<>), typeof(MyInterceptor));
services.AddScopedExplicitProxy(typeof(IB<>), typeof(MyInterceptor)); services.SolidifyOpenGenericServiceProxyRegister(); var sp = services.BuildServiceProvider(); for(int i = 0; i < 2; i++)
{
object? a1 = null, b1 = null;
using (var scope = sp.CreateScope())
{
var b = scope.ServiceProvider.GetRequiredService<IB<int>>();
var a = scope.ServiceProvider.GetRequiredService<IA<int>>();
var eqa = ReferenceEquals(a, a1);
var eqb = ReferenceEquals(b, b1); a1 = a;
b1 = b;
var eq = ReferenceEquals(a, b); var pa = scope.ServiceProvider.GetRequiredService<IProxyService<IA<int>>>();
var pb = scope.ServiceProvider.GetRequiredService<IProxyService<IB<int>>>();
}
}
} private static void KeyedExplicit()
{
IServiceCollection services = new ServiceCollection(); var serviceKey = "Keyed";
services.AddKeyedScoped(typeof(IB<>), serviceKey, typeof(B<>));
services.AddKeyedScopedForward(typeof(IA<>), serviceKey, typeof(IB<>));
services.AddKeyedScopedExplicitProxy(typeof(IA<>), serviceKey, typeof(MyInterceptor));
services.AddKeyedScopedExplicitProxy(typeof(IB<>), serviceKey, typeof(MyInterceptor)); services.SolidifyOpenGenericServiceProxyRegister(); var sp = services.BuildServiceProvider(); for (int i = 0; i < 2; i++)
{
object? a1 = null, b1 = null;
using (var scope = sp.CreateScope())
{
var b = scope.ServiceProvider.GetRequiredKeyedService<IB<int>>(serviceKey);
var a = scope.ServiceProvider.GetRequiredKeyedService<IA<int>>(serviceKey);
var eqa = ReferenceEquals(a, a1);
var eqb = ReferenceEquals(b, b1); a1 = a;
b1 = b;
var eq = ReferenceEquals(a, b); var pa = scope.ServiceProvider.GetRequiredKeyedService<IProxyService<IA<int>>>(serviceKey);
var pb = scope.ServiceProvider.GetRequiredKeyedService<IProxyService<IB<int>>>(serviceKey);
}
}
} private static void Implicit()
{
IServiceCollection services = new ServiceCollection(); services.AddScoped(typeof(IB<>), typeof(B<>));
services.AddScopedForward(typeof(IA<>), typeof(IB<>));
services.AddScopedImplicitProxy(typeof(IA<>), typeof(MyInterceptor));
services.AddScopedImplicitProxy(typeof(IB<>), typeof(MyInterceptor)); services.SolidifyOpenGenericServiceProxyRegister(); var sp = services.BuildServiceProvider(); for (int i = 0; i < 2; i++)
{
object? a1 = null, b1 = null;
using (var scope = sp.CreateScope())
{
var b = scope.ServiceProvider.GetRequiredService<IB<int>>();
var a = scope.ServiceProvider.GetRequiredService<IA<int>>();
var eqa = ReferenceEquals(a, a1);
var eqb = ReferenceEquals(b, b1); a1 = a;
b1 = b;
var eq = ReferenceEquals(a, b); var ra = scope.ServiceProvider.GetRequiredKeyedService<IA<int>>(ImplicitProxyServiceOriginalServiceKey.StringDefault);
var rb = scope.ServiceProvider.GetRequiredKeyedService<IB<int>>(ImplicitProxyServiceOriginalServiceKey.DefaultStringPrefix);
}
}
} private static void KeyedImplicit()
{
IServiceCollection services = new ServiceCollection(); var serviceKey = "Keyed";
services.AddKeyedScoped(typeof(IB<>), serviceKey, typeof(B<>));
services.AddKeyedScopedForward(typeof(IA<>), serviceKey, typeof(IB<>));
services.AddKeyedScopedImplicitProxy(typeof(IA<>), serviceKey, typeof(MyInterceptor));
services.AddKeyedScopedImplicitProxy(typeof(IB<>), serviceKey, typeof(MyInterceptor)); services.SolidifyOpenGenericServiceProxyRegister(); var sp = services.BuildServiceProvider(); for (int i = 0; i < 2; i++)
{
object? a1 = null, b1 = null;
using (var scope = sp.CreateScope())
{
var b = scope.ServiceProvider.GetRequiredKeyedService<IB<int>>(serviceKey);
var a = scope.ServiceProvider.GetRequiredKeyedService<IA<int>>(serviceKey);
var eqa = ReferenceEquals(a, a1);
var eqb = ReferenceEquals(b, b1); a1 = a;
b1 = b;
var eq = ReferenceEquals(a, b); var ra = scope.ServiceProvider.GetRequiredKeyedService<IA<int>>(ImplicitProxyServiceOriginalServiceKey.CreateStringOriginalServiceKey(serviceKey));
var rb = scope.ServiceProvider.GetRequiredKeyedService<IB<int>>($"{ImplicitProxyServiceOriginalServiceKey.DefaultStringPrefix}{serviceKey}"); a.M1();
b.M1(); ra.M1();
rb.M1();
}
}
}
} public interface IA<T>
{
void M1();
} public interface IB<T> : IA<T>; public class B<T> : IB<T>
{
public void M1()
{
Console.WriteLine("B.M1");
}
} public class MyInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("MyInterceptorBefore");
invocation.Proceed();
Console.WriteLine("MyInterceptorAfter");
}
}

结语

内置容器新的键控服务把非入侵式代理的服务类型区分问题解决之后让笔者又想起来了这个一直如鲠在喉的问题,结果发现开放泛型还是搞不定,一番纠结后决定自己动手丰衣足食。做到最后感觉反正都这样了,干脆做成Nuget包发布算了,为此又整理了半天文档注释,搞得头晕眼花。

Nuget包版本和代码对应的原始包版本一致。

许可证:MIT

代码仓库:CoreDX.Extensions.DependencyInjection - Github

Nuget:CoreDX.Extensions.DependencyInjection

Nuget:CoreDX.Extensions.DependencyInjection.Abstractions(这个一般不直接用,部分功能依赖改版容器)

Nuget:CoreDX.Extensions.DependencyInjection.Hosting.Abstractions

Nuget:CoreDX.Extensions.DependencyInjection.Hosting.Proxies.Abstractions

Nuget:CoreDX.Extensions.DependencyInjection.Proxies.Abstractions

一个.NET内置依赖注入的小型强化版的更多相关文章

  1. 第四节:配置的读取、StartUp类、内置依赖注入和扩展改造

    一. 配置的读取 在Asp.Net Core中,有一个 appsettings.json 文件,用于存储相应的配置信息,读取的时,要通过构造函数注入:IConfiguration Configurat ...

  2. .Net Core 3.0 内置依赖注入:举例

    原文:.Net Core 3.0 内置依赖注入:举例 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn ...

  3. Angular 行内式依赖注入

    var app = angular.module('myApp', ['ng']); //创建一个自定义服务app.factory('$Debug', function () { return { d ...

  4. Objection, 一个轻量级的Objective-C依赖注入框架

    简介 项目主页:https://github.com/atomicobject/objection 实例下载: https://github.com/ios122/ios122 Objection 是 ...

  5. 写一个android内置android程序

    当我们编译完毕android源代码之后,就须要对他做点什么事情,我如今正在看老罗的"Android源代码情景分析"一书.在这里主要是记录一些书中没有说清楚的地方. 相同.我们创建一 ...

  6. C#反射与特性(六):设计一个仿ASP.NETCore依赖注入Web

    目录 1,编写依赖注入框架 1.1 路由索引 1.2 依赖实例化 1.3 实例化类型.依赖注入.调用方法 2,编写控制器和参数类型 2.1 编写类型 2.2 实现控制器 3,实现低配山寨 ASP.NE ...

  7. 非常小的一个东西,Spring依赖注入Bean类型的8种情况

    大家好,我是三友~~ 今天来讲一个可能看似没有用但是实际又有点用的一个小东西,那就是@Autowired支持注入哪些Bean的类型. 为啥要讲这个呢? 故事说起来可能就比较长了. 不过长话可以短说,仅 ...

  8. 深入理解ASP.NET 5的依赖注入

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:ASP.NET 5整个底层都架构于依赖注入机制之下,今天介绍的文章详细介绍了内置依赖注 ...

  9. 如何在.NET Core控制台程序中使用依赖注入

    背景介绍 依赖注入(Dependency Injection), 是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度.在.NET Core MVC中 我们可以在Startup.cs文件的Co ...

  10. net5学习笔记---依赖注入

    小王的故事 小王去上班 ​ 小王是个程序员,每个工作日他都要去上班,诸多交通工具他最喜欢的交通工具是骑电车.在骑行的过程中放空自己使他很快. ​ 突然有一天天气预报说近期有很大的雨,小王再想骑电车去上 ...

随机推荐

  1. 【MongoDB详细步骤】(内附源码)

    第01章-MongoDB 1.安装和启动(docker方式) 1.1.拉取镜像 docker pull mongo:4.4.8 1.2.创建和启动容器 docker run -d --restart= ...

  2. aardio 背景透明的3种方式(透明窗体1 webview2,透明窗体2-win-region-bitmap,透明窗体3-winform-transparent-color)

    aardio 背景透明的3种方式(透明窗体1 webview2,透明窗体2-win-region-bitmap,透明窗体3-winform-transparent-color) 3种透明窗体,主要分成 ...

  3. 基于python的每日固定时间执行实例解析

    一 概念 datetime模块属性如下所示:   二 源码解析 该源码中,对时间库datetime应用十分到位,可以看作如下所示: import datetime import threading d ...

  4. C# NAudio 播放多个MP3文件

    C# 使用 NAudio 来播放多个MP3文件.上代码 1.引入NAudio:using NAudio.Wave; 2.定义变量: private WaveOutEvent outputDevice; ...

  5. 一张图搞清楚wait、sleep、join、yield四者区别,面试官直接被征服!

    写在开头 在线程的生命周期中,不同状态之间切换时,可以通过调用sleep().wait().join().yield()等方法进行线程状态控制,针对这一部分知识点,面试官们也会做做文章,比如问你这些方 ...

  6. netty Recycler对象池

    前言 池化思想在实际开发中有很多应用,指的是针对一些创建成本高,创建频繁的对象,用完不弃,将其缓存在对象池子里,下次使用时优先从池子里获取,如果获取到则可以直接使用,以此降低创建对象的开销. 我们最熟 ...

  7. sqlplus清屏方法

    cmd中使用:host cls 或 clear screen或 clear scre或clea scr

  8. JSF之常用注解

    @ManagedBean 以托管 bean 的形式注册一个类实例,然后将其放入到使用其中一个 @...Scoped 注释指定的范围内.如果没有指定任何范围,JSF 将把此 bean 放入请求范围,如果 ...

  9. .Net Core 使用 TagProvider 与 Enricher 丰富日志

    TagProvider  [LogProperties] 与 [LogPropertyIgnore] 如果用在DTO不存在任何问题,如果用在Domain实体上,可能有点混乱. 您可能不希望因日志记录问 ...

  10. Python实现简易版Netcat

    Netcat Netcat是一种网络工具,也称为"nc",可用于在计算机网络之间进行TCP/IP或UDP连接.它可以用于连接到其他计算机上的端口,发送和接收数据,扫描端口以及创建服 ...