.net core注册通用接口并使用getservice检索它们

问题描述 投票:0回答:3

我有接口

IOrder<T>
并且
FurnitureOrder<T>
实现了它

我需要注册多个这样的实现

services.AddSingleton(IOrder<Chair>, FurnitureOrder<Chair>())
services.AddSingleton(IOrder<Table>, FurnitureOrder<Table>())

现在我需要检索所有

IOrder
服务和类型(例如椅子、桌子)以对其进行一些背景工作。 我可以使用
GetServices
API 获取它吗? 如果不可能,请建议是否需要更改设计

c# .net asp.net-core .net-core dependency-injection
3个回答
3
投票

一种方法是引入一些常见的非通用接口

IOrder
并将所有服务注册为它(类似的框架用于托管服务注册和执行):

interface IOrder
{
}

interface IOrder<T> : IOrder
{    
}

services.TryAddEnumerable(ServiceDescriptor.Singleton<IOrder, FurnitureOrder<Chair>>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IOrder, FurnitureOrder<Table>>());

然后您可以将它们作为

IEnumerable<IOrder>
注入到您的依赖类中,或者使用
IServiceProvider.GetServices<IOrder>()
调用来解析它们。

另请注意,下次注册也应该有效:

services.AddSingleton<IOrder, FurnitureOrder<Chair>>()
services.AddSingleton<IOrder, FurnitureOrder<Table>>()

如果您需要通用注册,您也可以使用类似的方法自动执行非通用注册(在注册所有通用注册之后):

var serviceDescriptors = services
    .Where(descriptor => descriptor.ServiceType.IsConstructedGenericType)
    .Where(descriptor =>
        descriptor.ServiceType.GetGenericTypeDefinition() == typeof(IOrder<>));
foreach (var sd in serviceDescriptors)
{
    services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IOrder), 
        p => p.GetService(sd.ServiceType))); // or add desciptor creating new based on existing one  
}

1
投票

假设您是指在执行ConfigureServices期间? 你可以这样做:

    services.AddSingleton(typeof(IOrder<Chair>), f => new ChairOrder());

    var listOf = new List<ServiceDescriptor>();
    using var enumerator = services.GetEnumerator();
    while (enumerator.MoveNext())
                    {
                        var interfaceType = enumerator.Current.ServiceType;
                        if (!interfaceType.IsGenericType) continue;
                        if (interfaceType.Name.StartsWith("iorder", System.StringComparison.OrdinalIgnoreCase))
                            listOf.Add(enumerator.Current);                          
                    }
                    var sp = services.BuildServiceProvider();
                    foreach(var serviceDescriptor in listOf)
                    {
                        var interfaceIWant = serviceDescriptor.ServiceType;
                        var objectToUse = sp.GetRequiredService(serviceDescriptor.ServiceType);                            
                        //TODO:
                    }

0
投票

在尝试学习如何装饰/动态代理通用接口的任何注册实现时发现了这个问题。我找到了一个解决方案,将 @t-nielsen 的通用检查和 @guru-stron 的答案结合到

IServiceCollection
的简单扩展方法中(部分也受到 Rey 关于检索通用实现的问答的启发 here )。

扩展方法:

public static IEnumerable<Type?> GetGenericInterfaceImplementationTypes(
    this IServiceCollection services,
    Type genericInterfaceType)
{
    return services
        .Where(descriptor =>
            Array.Exists(descriptor.ServiceType.GetInterfaces(),
                i => i.IsGenericType && (i.GetGenericTypeDefinition() == genericInterfaceType)))
        .Select(d => d.ImplementationType);
}

使用示例:

public static IEnumerable<Type?> GetOrderImplementationTypes(this IServiceCollection services)
{
    return services.GetGenericInterfaceImplementationTypes(typeof(IOrder<>));
}
© www.soinside.com 2019 - 2024. All rights reserved.