这是关于.NET的一般性问题
我获得了IServiceProvider接口的实例,但是我几乎没有关于可以从中获取什么的文档。我如何找到它可能提供的所有服务的列表?
答案 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()
种方法,那么您可以使用动态调度 1,2,3 访问该方法而不进行强制转换。
也就是说,即使是微软自己的课程实现也只是一个兔子洞。引用文档,
IServiceProvider
界面由多种类型实施,包括System.Web.HttpContext
,System.ComponentModel.LicenseContext
,System.ComponentModel.MarshalByValueComponent
和System.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);
请随时扩展此类或评论错误
答案 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...
}
}