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

Cam*_*ute 4 .net c#

这是有关.NET的一般问题

我得到了IServiceProvider接口的实例,但是关于从中可以得到什么的信息很少。我如何找到它可能提供的所有服务的清单?

jBe*_*ger 14

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

在启动中:

    public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddSingleton(services);
    }
Run Code Online (Sandbox Code Playgroud)

这样您就可以将 IServiceCollection 注入到任何需要它的类中。


kdb*_*man 5

System.IServiceProvider具有单个方法,.GetService(Type)该方法返回单个服务。从本质上讲,它是将类型映射到服务的字典,并且不提供对所有键的访问,这可能是因为它打算通过电线实现。

实现该接口的类公开可以允许发现其提供的服务的方法或属性- 没有一般的方法可以单独使用该接口查看所有提供的服务。

解决方案:

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

    interface IBetterServiceProvider : System.IServiceProvider
       {
           IList<object> GetAllServices();
           IList<Type> GetAllServicedTypes();
       }
    
    Run Code Online (Sandbox Code Playgroud)

    并使您的服务实现它。

  • 如果您无法控制服务提供商的源代码,则可以强制转换为IServiceProveder实现类型,或使用反射来查找可告诉您所需内容的属性或方法。如果有似乎是一致的.GetServices()排序中,你正在使用的供应商的方法,那么你可以使用动态调度123,以访问该方法没有铸造。


也就是说,即使微软自己的类实现也有点麻烦。要引用文档,

IServiceProvider接口是由多种类型的,包括实现System.Web.HttpContextSystem.ComponentModel.LicenseContextSystem.ComponentModel.MarshalByValueComponent,和System.ComponentModel.Design.ServiceContainer

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

  • ServiceContainer实际上并没有实现该接口(尽管它确实具有该接口类型的内部字段。)即使ServiceContainer没有实现该接口,它也确实实现了该方法,这有点吓人。它确实证实了怀疑-这是将类型映射到服务的出色字典。同样,此实现并没有提供自己的方式来获取其拥有的所有服务。这是我期望的,因为它明确是服务的容器。

  • LicenseContext.GetService(Type) 除非被覆盖,否则仅返回null。 此类的某些子类也许提供了一种获取所有服务的方法,但是这种方法没有。

我已经完成了源代码和文档的挖掘。看起来有点混乱,但上面的简短答案仍然存在:旧名称或新名称,伪实现或实际实现:无法IServiceProvider单独从接口获取所有服务,我发现的Microsoft的实现都无法为您提供方法要么。


Dom*_*nas 5

对于我的应用程序,我想DbContexts一次迁移我的所有内容。所以在IServiceCollection配置和IServiceProvider构建之后,我没有机会通过IServiceProvider.

下面的代码片段会做到这一点,但是:

这是非常实验性的,因此UnitTest应该实施以注意 Microsoft 的更改并相应地调整方法!

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!");
    }
}
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)

获取所有的示例用法 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);

Run Code Online (Sandbox Code Playgroud)

从你的 IServiceProvier

var dbContexts = provider.GetAllServiceDescriptors().Where(d => d.Key.IsSubclassOf(typeof(DbContext))).ToList();
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

请随意扩展这个类或评论错误


Adr*_*man 5

因为这仍然是谷歌最重要的建议之一:

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

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