这是有关.NET的一般问题
我得到了IServiceProvider接口的实例,但是关于从中可以得到什么的信息很少。我如何找到它可能提供的所有服务的清单?
jBe*_*ger 14
如果您使用核心 Web 应用程序,可能有一个简单的解决方案。这就是我最终所做的。
在启动中:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton(services);
}
Run Code Online (Sandbox Code Playgroud)
这样您就可以将 IServiceCollection 注入到任何需要它的类中。
System.IServiceProvider具有单个方法,.GetService(Type)该方法返回单个服务。从本质上讲,它是将类型映射到服务的字典,并且不提供对所有键的访问,这可能是因为它打算通过电线实现。
由实现该接口的类公开可以允许发现其提供的服务的方法或属性- 没有一般的方法可以单独使用该接口查看所有提供的服务。
如果您可以控制服务提供商的来源,请创建一个子界面,该界面允许您所需
interface IBetterServiceProvider : System.IServiceProvider
{
IList<object> GetAllServices();
IList<Type> GetAllServicedTypes();
}
Run Code Online (Sandbox Code Playgroud)
并使您的服务实现它。
如果您无法控制服务提供商的源代码,则可以强制转换为IServiceProveder实现类型,或使用反射来查找可告诉您所需内容的属性或方法。如果有似乎是一致的.GetServices()排序中,你正在使用的供应商的方法,那么你可以使用动态调度1,2,3,以访问该方法没有铸造。
也就是说,即使微软自己的类实现也有点麻烦。要引用文档,
该
IServiceProvider接口是由多种类型的,包括实现System.Web.HttpContext,System.ComponentModel.LicenseContext,System.ComponentModel.MarshalByValueComponent,和System.ComponentModel.Design.ServiceContainer。
HttpContext实现该接口,但该GetService(Type)方法仅作为内部使用记录在案,并且它包含的唯一服务(至少在公共API中是)PageInstrumentation。在此实现中,无法查询所有服务。
ServiceContainer实际上并没有实现该接口(尽管它确实具有该接口类型的内部字段。)即使ServiceContainer没有实现该接口,它也确实实现了该方法,这有点吓人。它确实证实了怀疑-这是将类型映射到服务的出色字典。同样,此实现并没有提供自己的方式来获取其拥有的所有服务。这是我期望的,因为它明确是服务的容器。
LicenseContext.GetService(Type) 除非被覆盖,否则仅返回null。 此类的某些子类也许提供了一种获取所有服务的方法,但是这种方法没有。
我已经完成了源代码和文档的挖掘。看起来有点混乱,但上面的简短答案仍然存在:旧名称或新名称,伪实现或实际实现:无法IServiceProvider单独从接口获取所有服务,我发现的Microsoft的实现都无法为您提供方法要么。
对于我的应用程序,我想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)
请随意扩展这个类或评论错误
因为这仍然是谷歌最重要的建议之一:
现在有一个 nuget 扩展集,您可以从 M$ 下拉它扩展服务提供者并公开几个有用的端点,其中之一是“GetServices”,它根据您提供的类型返回一个 IEnumerable
https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection.Abstractions/
| 归档时间: |
|
| 查看次数: |
4695 次 |
| 最近记录: |