ASP.NET 中是否有一种方法可以获取所有的单例对象?

huangapple go评论63阅读模式
英文:

Is there a way in ASP.NET to get all singletons?

问题

In ASP.NET 中,有没有一种方法可以获取所有的单例对象?

我想要实现一个健康检查,该检查将实例化所有已注册的单例对象,然后才返回健康状态。

我尝试从 IServiceProvider 获取所有的单例对象,但似乎只有一个方法,即 object? GetService(Type serviceType);

所以,也许有人知道在 C# 中获取所有单例对象的方法?

我尝试这样做:

_servicesProvider
  .GetServices<object>()
  .Where(service => _servicesProvider.GetService(service.GetType()) == service)

但似乎还会返回作用域服务。

似乎像这样:

_servicesProvider
  .GetServices<object>()
  .ToList();
return new HealthCheckResult(HealthStatus.Healthy);

应该可以工作。但我仍然希望有一个更优化的解决方案。

英文:

Is there a way in ASP.NET to get all singletons?

I want to implement a health check which would instantiate all the registered singletons and only after that return a healthy status.

I am trying to get all the singletons from the IServiceProvider, but it seems to have only one method, i.e. the object? GetService(Type serviceType);.

So, maybe someone knows a way in C# to get all singletons?

I am trying to do like so:

_servicesProvider
  .GetServices&lt;object&gt;()
  .Where(service =&gt; _servicesProvider.GetService(service.GetType()) == service)

But it seems to also give me back scoped services.

Seems like this:

_servicesProvider
  .GetServices&lt;object&gt;()
  .ToList();
return new HealthCheckResult(HealthStatus.Healthy);

should work. But I am still hoping for a more optimal solution.

答案1

得分: 2

我不相信有内置的方法来做到这一点。

这个答案 对类似的问题表示同意,并建议使用反射作为可能的解决方案。

另一种选择是通过.AddSingleton&lt;IServiceCollection&gt;(serviceCollection)将您的ServiceCollection注册到自身。然后,您可以通过IServiceProvider注入它或从中请求它,并查询它以获取具有单例生命周期的所有服务描述:

var serviceCollection = serviceProvider.GetRequiredService&lt;IServiceCollection&gt;();
var singletons = serviceCollection
    .Where(descriptor =&gt; descriptor.Lifetime == ServiceLifetime.Singleton)
    .Select(descriptor =&gt; serviceProvider.GetRequiredService(descriptor.ServiceType))
    .ToList();

foreach(object singleton in singletons)
{
    // 对单例执行某些操作
}

根据您的用例,可能更好的方法是创建一个具有缩小接口的自己的实现,而不是将整个IServiceCollection暴露给DI容器的使用者。

英文:

I don't believe there is a built in way to do this.

This answer to a similar question agrees, and suggest reflection as a possible solution.

Another option is to register your ServiceCollection with itself via .AddSingleton&lt;IServiceCollection&gt;(serviceCollection). You can then inject this or request it from the IServiceProvider and query it to get all the service descriptions with singleton lifetime:

var serviceCollection = serviceProvider.GetRequiredService&lt;IServiceCollection&gt;();
var singletons = serviceCollection
    .Where(descriptor =&gt; descriptor.Lifetime == ServiceLifetime.Singleton)
    .Select(descriptor =&gt; serviceProvider.GetRequiredService(descriptor.ServiceType))
    .ToList();

foreach(object singleton in singletons)
{
    // do something with the singleton
}

Depending on your use case it may be better to make your own implemention with a reduced interface, rather than exposing all of IServiceCollection to the consumer of the DI container.

答案2

得分: 2

@DisplayName已经给出了可能作为起点的答案。这里是一个不同的解决方案:

IEnumerable&lt;object&gt; singletons = (
    from service in services
    group service by service.ServiceType into g
    where g.Any(descriptor =&gt; descriptor.Lifetime == ServiceLifetime.Singleton)
    from pair in serviceProvider.GetServices(g.Key).Zip(g)
    where pair.Second.Lifetime == ServiceLifetime.Singleton
    select pair.First)
    .Distinct();

与@DisplayName的答案相比,此解决方案将检索所有已注册的单例,即使它们是集合的一部分。但是,该解决方案也存在一些不幸的缺点,可能不会存在于@DisplayName的答案中:

  1. 如果存在同一服务类型的多个注册,其中注册除了单例之外还包含瞬态和/或作用域注册,那些注册也将从容器中解析。它们将被过滤掉,不会成为单例列表的一部分,但它们仍然会被创建。这可能会导致性能问题,因为健康检查现在可能会导致大量的内存分配(取决于应用程序的大小)。
  2. 当与插入到MS.DI基础结构中的DI容器(如Autofac或Lamar)结合使用时,此解决方案可能不会产生正确的结果。它们支持诸如动态拦截和装饰之类的功能,特别通过这些容器的API进行的更改可能会被忽略。

选择最适合您情况的解决方案。

英文:

@DisplayName already gave an answer that might work as a starting point. Here is a different solution:

IEnumerable&lt;object&gt; singletons = (
    from service in services
    group service by service.ServiceType into g
    where g.Any(descriptor =&gt; descriptor.Lifetime == ServiceLifetime.Singleton)
    from pair in serviceProvider.GetServices(g.Key).Zip(g)
    where pair.Second.Lifetime == ServiceLifetime.Singleton
    select pair.First)
    .Distinct();

Compared to @DisplayName's answer, this solution will retrieve all registered singletons, even those that are part of a collection. This solution, however, exhibits a few unfortunate downsides that might not exist in @DisplayName's answer:

  1. If multiple registrations for the same service type exist, where the registrations contain -besides singletons- transient and/or scoped registrations, those registrations will be resolved from the container as well. They will be filtered out and not be part of the list of singletons, but they will still be created nonetheless. This might cause performance issues as the health check can now cause a lot of memory allocation (depending on the size of the application).
  2. This solution might not yield the correct results when used in combination with a DI Container that plugs into the MS.DI infrastructure (such as Autofac or Lamar). They enable features like Dynamic Interception and Decoration, and changes made specifically through the API of those containers might be missed.

Pick the solution that works best for your situation.

huangapple
  • 本文由 发表于 2023年7月5日 00:46:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76614560.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定