前言
.NET生態(tài)中有許多依賴注入容器。在大多數(shù)情況下,微軟提供的內(nèi)置容器在易用性和性能方面都非常優(yōu)秀。外加ASP.NET Core默認(rèn)使用內(nèi)置容器,使用很方便。
但是筆者在使用中一直有一個頭疼的問題:服務(wù)工廠無法提供請求的服務(wù)類型相關(guān)的信息。這在一般情況下并沒有影響,但是內(nèi)置容器支持注冊開放泛型服務(wù),此時會導(dǎo)致無法實現(xiàn)某些需求。
ASP.NET Core目前推薦使用上下文池訪問EF Core上下文,但是某些功能需要直接使用上下文(例如Identity Core)。官方文檔建議使用自定義工廠通過上下文池獲取上下文。這其實是一種服務(wù)轉(zhuǎn)發(fā)(或委托),可以確保服務(wù)實例只有一個最終提供點,簡化管理。
但是當(dāng)希望轉(zhuǎn)發(fā)的服務(wù)是開放泛型時就會出現(xiàn)問題。在實際請求服務(wù)時,無法通過自定義工廠得知請求的泛型服務(wù)的實際類型參數(shù),也就無法實現(xiàn)對開放泛型類型的轉(zhuǎn)發(fā)。官方倉庫也有一個相關(guān)Issue:Dependency Injection of Open Generics via factory #41050。然而幾年過去后微軟依然沒有打算解決這個問題。鍵控服務(wù)這種完全新增的功能都做了,這個舉手之勞確一直放著,我不理解。一番研究后筆者確定可以通過簡單的改造來實現(xiàn)支持,因此有了本篇文章。
新書宣傳
有關(guān)新書的更多介紹歡迎查看《C#與.NET6 開發(fā)從入門到實踐》上市,作者親自來打廣告了!
正文
本來筆者打算使用繼承來擴(kuò)展功能,但是幾經(jīng)周折后發(fā)現(xiàn)微軟把關(guān)鍵類型設(shè)置為內(nèi)部類和密封類,徹底斷了這條路。無奈只能克隆倉庫直接改代碼,好死不死這個庫是運行時倉庫的一部分,完整倉庫包含大量無關(guān)代碼,直接fork也會帶來一堆麻煩,最后只能克隆倉庫后復(fù)制需要的部分來修改。
CoreDX.Extensions.DependencyInjection.Abstractions
這是基礎(chǔ)抽象包,用于擴(kuò)展ServiceDescriptor
為后續(xù)改造提供基礎(chǔ)支持。
TypedImplementationFactoryServiceDescriptor
要實現(xiàn)能從自定義工廠獲取服務(wù)類型的功能,自定義工廠需要一個Type
類型的參數(shù)來傳遞類型信息,那么就需要ServiceDescriptor
提供相應(yīng)的構(gòu)造方法重載。原始類型顯然不可能,好在這是個普通公共類,可以繼承,因此筆者繼承內(nèi)置類并擴(kuò)展了相應(yīng)的成員來承載工廠委托。
/// <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>
工廠委托的字段和配套的構(gòu)造方法和驗證邏輯。基類提供的所有功能均直接拋出異常,專門負(fù)責(zé)新增功能。
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;
}
這個類也很簡單,只負(fù)責(zé)持有服務(wù)類型,并把新的工廠類型轉(zhuǎn)換到原始工廠類型方便集成進(jìn)內(nèi)置容器。并且這是內(nèi)部輔助類型,對開發(fā)者是無感知的。
易用性擴(kuò)展
最后就是提供擴(kuò)展方法提供和內(nèi)置容器相似的使用體驗。由于本次擴(kuò)展的主要目的是實現(xiàn)開放發(fā)型的服務(wù)轉(zhuǎn)發(fā),因此筆者專門準(zhǔn)備了一套用來注冊服務(wù)轉(zhuǎn)發(fā)的AddForward
系列擴(kuò)展方便使用。此處只列出部分預(yù)覽。
/// <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;
}
從示例可以發(fā)現(xiàn)如果類型不是開放泛型,是直接使用原始類型進(jìn)行注冊的。也就是說如果安裝這個抽象包,但是不使用開放泛型的相關(guān)功能,是可以直接用原始內(nèi)置容器的。
CoreDX.Extensions.DependencyInjection
這是修改后的服務(wù)容器實現(xiàn),增加了對帶服務(wù)類型的自定義工廠的支持,其他內(nèi)置功能完全不變。
CallSiteFactory
internal sealed partial class CallSiteFactory : IServiceProviderIsService, IServiceProviderIsKeyedService
{
// 其他原始代碼
private void Populate()
{
foreach (ServiceDescriptor descriptor in _descriptors)
{
Type serviceType = descriptor.ServiceType;
#region 驗證可識別請求類型的服務(wù)實現(xiàn)工廠
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 為可識別請求類型的服務(wù)工廠注冊服務(wù)實現(xiàn)工廠
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 為開放式泛型服務(wù)添加可識別請求類型的服務(wù)實現(xiàn)工廠
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;
}
// 其他原始代碼
}
這是整個改造的關(guān)鍵,理論上來說只要這個類是普通類的話完全可以直接通過繼承把功能加上,可惜不是。此處只展示修改的部分。然后把輔助方法定義到另一個文件,方便利用部分類的特點盡量減少對原始代碼的改動,方便將來同步官方代碼。
internal sealed partial class CallSiteFactory
{
/// <summary>
/// 嘗試創(chuàng)建可識別請求類型的工廠調(diào)用點
/// </summary>
/// <param name="lifetime"></param>
/// <param name="descriptor"></param>
/// <param name="serviceType">服務(wù)類型</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);
}
}
因為筆者直接使用了內(nèi)置類型,因此需要把工廠委托轉(zhuǎn)換成內(nèi)置容器支持的簽名。Holder輔助類就可以把類型信息保存為內(nèi)部字段,對外暴露的工廠簽名就可以不需要類型參數(shù)了。
最后為避免引起誤解,筆者修改了類名,但保留文件名方便比對原始倉庫代碼。至此,改造其實已經(jīng)完成??梢钥闯龈膭诱娴暮苌?,不知道為什么微軟就是不改。
CoreDX.Extensions.DependencyInjection.Hosting.Abstractions
雖然經(jīng)過上面的改造后,改版容器已經(jīng)能用了,但是為了方便和通用主機(jī)系統(tǒng)集成還是要提供一個替換容器用的擴(kuò)展。
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);
});
}
}
至此,主機(jī)集成工作也完成了。本來打算就這么結(jié)束的,結(jié)果突然想起來,開放泛型問題解決了,鍵控服務(wù)也有了,之前一直不知道怎么辦的動態(tài)代理貌似是有戲了,就又研究起來了。
CoreDX.Extensions.DependencyInjection.Proxies.Abstractions
之前動態(tài)代理不好實現(xiàn)主要是因為代理服務(wù)和原始服務(wù)的注冊類型相同,實在是沒辦法。既然現(xiàn)在有鍵控服務(wù)了,那么把原始服務(wù)和代理服務(wù)用鍵分開就完美搞定,最后一個問題就是鍵要怎么處理。通過文檔可知鍵控服務(wù)的鍵可以是任意object
,只要實現(xiàn)合理的相等性判斷即可。因此筆者決定使用專用的類型來表示代理服務(wù)的鍵,并通過對string
類型的特殊處理來實現(xiàn)特性鍵指定的兼容。
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以上版本,把運算符實現(xiàn)為接口。
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;
}
除了隱式代理,筆者還準(zhǔn)備了顯式代理,這也是筆者要在內(nèi)置容器上擴(kuò)展而不是去用其他第三方容器的一個原因。第三方容器代理后原始服務(wù)就被隱藏了,在某些極端情況下萬一要用到原始服務(wù)就沒辦法了。
CastleDynamicProxyDependencyInjectionExtensions
此處只展示部分預(yù)覽。
/// <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;
使用擴(kuò)展方法把代理完全實現(xiàn)為普通服務(wù)注冊最大限度減少對服務(wù)容器的入侵。對于隱式代理,原始服務(wù)會被原地替換為代理服務(wù),原始服務(wù)則使用新鍵重新注冊,因此注冊代理前需要先注冊原始服務(wù)。
顯式代理的開放泛型處理
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();
}
顯式代理實際上是一個開放泛型服務(wù),因此對于注冊過代理的類型需要單獨記錄,否則無法判斷是否應(yīng)該生成相應(yīng)的封閉類型。而注冊記錄在實例化容器后就不應(yīng)該被修改,因此又提供了SolidifyOpenGenericServiceProxyRegister
方法用來固化記錄。
CoreDX.Extensions.DependencyInjection.Hosting.Proxies.Abstractions
因為代理服務(wù)有一個額外步驟,開發(fā)時可能忘記調(diào)用,為繼續(xù)簡化和通用主機(jī)的集成,把注冊記錄的固化工作放到主機(jī)的容器工廠中進(jìn)行。
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);
}
}
這個工廠唯一的工作就是在內(nèi)部完成固化操作,這樣就可以不用對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);
});
}
}
使用新的擴(kuò)展方法替換容器工廠即可。
簡單使用示例
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");
}
}
結(jié)語
內(nèi)置容器新的鍵控服務(wù)把非入侵式代理的服務(wù)類型區(qū)分問題解決之后讓筆者又想起來了這個一直如鯁在喉的問題,結(jié)果發(fā)現(xiàn)開放泛型還是搞不定,一番糾結(jié)后決定自己動手豐衣足食。做到最后感覺反正都這樣了,干脆做成Nuget包發(fā)布算了,為此又整理了半天文檔注釋,搞得頭暈眼花。
Nuget包版本和代碼對應(yīng)的原始包版本一致。
許可證: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
QQ群
讀者交流QQ群:540719365
歡迎讀者和廣大朋友一起交流,如發(fā)現(xiàn)本書錯誤也歡迎通過博客園、QQ群等方式告知筆者。文章來源:http://www.zghlxwxcb.cn/news/detail-854481.html
本文地址:https://www.cnblogs.com/coredx/p/18138360.html文章來源地址http://www.zghlxwxcb.cn/news/detail-854481.html
到了這里,關(guān)于一個.NET內(nèi)置依賴注入的小型強化版的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!