如何查看.NET IServiceProvider可以提供的所有服务?

时间:2015-11-26 17:02:51

标签: c# .net

这是关于.NET的一般性问题

我获得了IServiceProvider接口的实例,但是我几乎没有关于可以从中获取什么的文档。我如何找到它可能提供的所有服务的列表?

7 个答案:

答案 0 :(得分:5)

因为这仍然是 google 的最佳建议之一:

现在有一个 nuget 扩展集,您可以从 M$ 中提取它来扩展服务提供者并公开几个有用的端点,其中之一是“GetServices”,它根据您提供的类型返回一个 IEnumerable

https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection.Abstractions/

答案 1 :(得分:4)

System.IServiceProvider有一个方法.GetService(Type),它返回一个服务。它本质上是一个字典映射类型到服务,它不提供对所有键的访问,可能是因为它旨在通过线路实现。

实现类接口来公开允许发现它提供的服务的方法或属性 - 没有通用方法只使用接口查看所有提供的服务

解决方案:

  • 如果您可以控制服务提供商的来源,请创建一个允许您想要的子界面

    interface IBetterServiceProvider : System.IServiceProvider
       {
           IList<object> GetAllServices();
           IList<Type> GetAllServicedTypes();
       }
    

    并让您的服务实现它。

  • 如果您 无法控制服务提供商的来源,请转换为IServiceProveder实施类型,或使用反射来查找告诉您的属性或方法你想要什么。如果您正在使用的提供商中似乎存在一致的.GetServices()种方法,那么您可以使用动态调度 123 访问该方法而不进行强制转换。

也就是说,即使是微软自己的课程实现也只是一个兔子洞。引用文档,

  

IServiceProvider界面由多种类型实施,包括System.Web.HttpContextSystem.ComponentModel.LicenseContextSystem.ComponentModel.MarshalByValueComponentSystem.ComponentModel.Design.ServiceContainer

  • HttpContext实现了接口,但GetService(Type)方法仅记录为内部使用,并且它包含的唯一服务(至少在公共API中)是{{3} }。无法在此实现中查询所有服务。

  • PageInstrumentation实际上没有实现接口(尽管它具有该接口类型的ServiceContainer。)即使ServiceContainer没有实现接口,它 确实实现了这个方法,internal field。它确实证实了怀疑 - 它是一种美化字典,将类型映射到服务。同样,这种实现并没有提供获得它所拥有的所有服务的自己的方式。这是我期望的那个,因为它明确地是服务的容器。

  • LicenseContext.GetService(Type) it's a bit scary也许这个类的子类提供了一种获取所有服务的方法,但是这个没有。

我已经完成了对源和文档的挖掘。它看起来有点乱,但上面简短的回答有:旧名称或新的,伪实现或实际实现:没有办法单独从IServiceProvider接口获取所有服务,而且我发现的Microsoft实现都没有你也可以这样做。

答案 2 :(得分:4)

对于我的应用程序,我想一次迁移所有git add some-file && git commit。因此,在配置git status和构建DbContexts之后,我没有机会通过IServiceCollection访问它们。

以下代码段将完成此操作,但是:

这是非常实验性的,因此应实施IServiceProvider来记录Microsoft的更改并相应地调整方法!

IServiceProvider
UnitTest

获取所有public static class IServiceProviderExtensions { /// <summary> /// Get all registered <see cref="ServiceDescriptor"/> /// </summary> /// <param name="provider"></param> /// <returns></returns> public static Dictionary<Type, ServiceDescriptor> GetAllServiceDescriptors(this IServiceProvider provider) { if (provider is ServiceProvider serviceProvider) { var result = new Dictionary<Type, ServiceDescriptor>(); var engine = serviceProvider.GetFieldValue("_engine"); var callSiteFactory = engine.GetPropertyValue("CallSiteFactory"); var descriptorLookup = callSiteFactory.GetFieldValue("_descriptorLookup"); if (descriptorLookup is IDictionary dictionary) { foreach (DictionaryEntry entry in dictionary) { result.Add((Type)entry.Key, (ServiceDescriptor)entry.Value.GetPropertyValue("Last")); } } return result; } throw new NotSupportedException($"Type '{provider.GetType()}' is not supported!"); } }

的示例用法

注册所有public static class ReflectionHelper { // ########################################################################################## // Get / Set Field // ########################################################################################## #region Get / Set Field public static object GetFieldValue(this object obj, string fieldName) { if (obj == null) throw new ArgumentNullException(nameof(obj)); Type objType = obj.GetType(); var fieldInfo = GetFieldInfo(objType, fieldName); if (fieldInfo == null) throw new ArgumentOutOfRangeException(fieldName, $"Couldn't find field {fieldName} in type {objType.FullName}"); return fieldInfo.GetValue(obj); } public static void SetFieldValue(this object obj, string fieldName, object val) { if (obj == null) throw new ArgumentNullException(nameof(obj)); Type objType = obj.GetType(); var fieldInfo = GetFieldInfo(objType, fieldName); if (fieldInfo == null) throw new ArgumentOutOfRangeException(fieldName, $"Couldn't find field {fieldName} in type {objType.FullName}"); fieldInfo.SetValue(obj, val); } private static FieldInfo GetFieldInfo(Type type, string fieldName) { FieldInfo fieldInfo = null; do { fieldInfo = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); type = type.BaseType; } while (fieldInfo == null && type != null); return fieldInfo; } #endregion // ########################################################################################## // Get / Set Property // ########################################################################################## #region Get / Set Property public static object GetPropertyValue(this object obj, string propertyName) { if (obj == null) throw new ArgumentNullException(nameof(obj)); Type objType = obj.GetType(); var propertyInfo = GetPropertyInfo(objType, propertyName); if (propertyInfo == null) throw new ArgumentOutOfRangeException(propertyName, $"Couldn't find property {propertyName} in type {objType.FullName}"); return propertyInfo.GetValue(obj, null); } public static void SetPropertyValue(this object obj, string propertyName, object val) { if (obj == null) throw new ArgumentNullException(nameof(obj)); Type objType = obj.GetType(); var propertyInfo = GetPropertyInfo(objType, propertyName); if (propertyInfo == null) throw new ArgumentOutOfRangeException(propertyName, $"Couldn't find property {propertyName} in type {objType.FullName}"); propertyInfo.SetValue(obj, val, null); } private static PropertyInfo GetPropertyInfo(Type type, string propertyName) { PropertyInfo propertyInfo = null; do { propertyInfo = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); type = type.BaseType; } while (propertyInfo == null && type != null); return propertyInfo; } #endregion }

DbContext

从您的DbContext

获取它们
services.AddDbContext<ProductionDbContext>(optionsBuilder => optionsBuilder.UseSqlite($"Data Source={Path.Combine(Directories.Data, "ProductionDb.sqlite")}"), ServiceLifetime.Transient);
services.AddDbContext<ProductionArchiveDbContext>(optionsBuilder => optionsBuilder.UseSqlite($"Data Source={Path.Combine(Directories.Data, "ProductionArchiveDb.sqlite")}"), ServiceLifetime.Transient);
services.AddDbContext<RecipeDbContext>(optionsBuilder => optionsBuilder.UseSqlite($"Data Source={Path.Combine(Directories.Data, "RecipesDb.sqlite")}"), ServiceLifetime.Transient);
services.AddDbContext<SecurityDbContext>(optionsBuilder => optionsBuilder.UseSqlite($"Data Source={Path.Combine(Directories.Data, "SecurityDb.sqlite")}"), ServiceLifetime.Transient);
services.AddDbContext<TranslationDbContext>(optionsBuilder => optionsBuilder.UseSqlite($"Data Source={Path.Combine(Directories.Data, "TranslationDb.sqlite")}"), ServiceLifetime.Transient);
services.AddDbContext<AlarmsDbContext>(optionsBuilder => optionsBuilder.UseSqlite($"Data Source={Path.Combine(Directories.Data, "AlarmsDb.sqlite")}"), ServiceLifetime.Transient);

enter image description here

请随时扩展此类或评论错误

答案 3 :(得分:4)

如果您使用的是核心 Web 应用程序,可能有一个简单的解决方案。这就是我最终做的事情。

在启动中:

    public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddSingleton(services);
    }

通过这种方式,您可以将 IServiceCollection 注入任何需要它的类。

答案 4 :(得分:1)

此问题没有通用解决方案,如果有任何方法可用于查找服务列表,则取决于您使用的实现。

答案 5 :(得分:1)

如果您使用的是ASP.net Core的DI,则可以安装Microsoft.Extensions.DependencyInjection.Abstractions软件包,为您提供所需的方法。

public static class ServiceProviderServiceExtensions
{
    public static IServiceScope CreateScope(this IServiceProvider provider);

    public static object GetRequiredService(this IServiceProvider provider, Type serviceType);

    public static T GetRequiredService<T>(this IServiceProvider provider);

    public static T GetService<T>(this IServiceProvider provider);

    public static IEnumerable<T> GetServices<T>(this IServiceProvider provider);

    public static IEnumerable<object> GetServices(this IServiceProvider provider, Type serviceType);
}

答案 6 :(得分:-1)

也许有点晚,但是我还需要该功能来查看服务是否已注册。下面我的解决方案。我知道这不是可选的,但对我来说很好。

我为IServiceProvider提出了两种扩展方法:

public static class DiExtension
{
    private static IServiceCollection _serviceCollection;

    public static void AddServiceCollection(this IServiceProvider services, IServiceCollection serviceCollection)
    {
        _serviceCollection = serviceCollection;
    }

    public static bool HasService(this IServiceProvider services, Type serviceType)
    {
        return _serviceCollection.Any(s => s.ServiceType.FullName == serviceType.FullName);
    }
}

您可以看到,这里是我通过调用IServiceCollection方法来引用AddServiceCollection的地方。像这样在Startup.cs方法的Configure中调用此方法:

Startup.cs

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, Domain.Logging.ILogger logger, IServiceProvider serviceProvider)
{
    // Notice that I inject IServiceProvider into the method
    serviceProvider.AddServiceCollection(_serviceCollection);
}

要获取对IServiceCollection的引用,请在ConfigureServices方法中进行设置:

public void ConfigureServices(IServiceCollection services)
{
   // register all the things you need      

    _serviceCollection = services;
}

并且场外_serviceCollection 是Startup.cs中的一个私有字段:

private IServiceCollection _serviceCollection;

现在,当一切设置完成后,我可以使用以下方法检查服务是否已注册:

public class SomeClass
{
    private readonly IServiceProvider _serviceProvider;

    public SomeClass(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void SomeMethod(Type service)
    {
        var hasType = _serviceProvider.HasService(service);

        // Do what you have to do...
    }
}