简体   繁体   English

通过注解注册 .NET Core singleton 服务

[英]Register .NET Core singleton service via annotation

Is there any way to autowire (automatically register) singleton services to a C# DI container ( Microsoft.Extensions.DependencyInjection ) by annotation?有没有办法通过注释自动连接(自动注册) singleton 服务到 C# DI 容器Microsoft.Extensions.DependencyInjection

Eg something like ProvidedIn option in @Injectable() annotation in Angular , @injectable() annotation in InversifyJS (Node.js) , autowiring in Spring (Java) or autowiring in Symfony Framework (PHP) ?例如,Angular 中的 @Injectable() 注释中的 ProvidedIn 选项, InversifyJS (Node.js)中的 @injectable() 注释, Spring (Java)的自动装配或 Z878D60C139E73E2B6 中的自动装配

See Angular example below:请参见下面的 Angular 示例:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class UserService {
}

At this time I have to add each singleton service to ServiceCollection manually this way (and forget to do it sometimes):此时我必须以这种方式手动将每个 singleton 服务添加到 ServiceCollection (有时忘记这样做):

internal static ServiceProvider SetupDi()
{
    return new ServiceCollection() // Microsoft.Extensions.DependencyInjection.ServiceCollection.ServiceCollection()
        .AddDbContext<DbContext>()
        .AddSingleton<ServiceA>()
        .AddSingleton<ServiceB>();
}

The desired equivalent solution would be something like this:所需的等效解决方案将是这样的:

internal static ServiceProvider SetupDi()
{
    return new ServiceCollection() // Microsoft.Extensions.DependencyInjection.ServiceCollection.ServiceCollection()
        .AddDbContext<DbContext>();
}

[Injectable()]
public class ServiceA
{
}

[Injectable()]
public class ServiceB
{
}

No, and that's actually by-design.不,这实际上是设计使然。

The whole point of DI is that there are no surprises with how your program is configured: everything you configure in ConfigureServices (your in your case, SetupDi ) is exactly what you get at runtime. DI 的全部意义在于您的程序的配置方式并不令人意外:您在ConfigureServices中配置的所有内容(在您的情况下为SetupDi )正是您在运行时获得的。 By using attributes to configure DI then that would introduce "non-local effects" and it would be much harder to track-down bugs introduced by incorrect or misconfigured dependencies caused by an errant attribute.通过使用属性来配置 DI,这将引入“非本地影响”,并且更难追踪由错误属性引起的不正确或错误配置的依赖项引入的错误。

(To that extent, I disagree with Angular's design - but that's off-topic). (在这个程度上,我不同意 Angular 的设计——但那是题外话)。

(I also feel that .NET Core's DI system is also imperfect - too many necessary details are hidden behind DI injection extension-methods that you need to use ILSpy or Reflector to peer into). (我也觉得.NET Core的DI系统也不完善——DI注入扩展背后隐藏了太多必要的细节——需要使用ILSpy或Reflector才能窥视)。

As a workaround, you can "test" your DI services at app startup to ensure everything is configured by reflecting over every IService in your project and trying to instantiate the implementation.作为一种解决方法,您可以在应用程序启动时“测试”您的 DI 服务,以确保通过反映项目中的每个IService并尝试实例化实现来配置所有内容。

Here's the code I use in my ASP.NET and ASP.NET Core projects to verify DI is thoroughly configured:这是我在 ASP.NET 和 ASP.NET 核心项目中使用的代码,用于验证 DI 是否已完全配置:

internal static void TestAllServices( IServiceProvider sp )
{
    Assembly[] mySolutionAssemblies = new[]
    {
        typeof(FromAssemblyX.Foobar).Assemby,
        typeof(FromAssemblyY.Foobar).Assemby,
        typeof(FromAssemblyZ.Foobar).Assemby,
    };

    List<Type> allServiceInterfaceTypes = mySolutionAssemblies
        .SelectMany( ass => ass.GetTypes() )
        .Where( t => t.IsInterface && t.IsPublic )
        .Where( /* Filter out interfaces you don't want to verify here */ )
        .ToList();

    foreach( Type serviceInterfaceType in serviceInterfaceTypes )
    {
        try
        {
            Object implementation = sp.GetRequiredService( serviceInterfaceType );
            if( implementation is IDisposable disp ) disp.Dispose();
        }
        catch( Exception ex )
        {
            // Log an error or throw or set a breakpoint here
        }
    }
}

Notes:笔记:

  • Don't pass in your "real" IServiceProvider into TestAllServices - instead create a separate IServiceProvider instance, because this method will dispose any implementation that implements IDisposable , even if they're singletons.不要将您的“真实” IServiceProvider传递到TestAllServices - 而是创建一个单独的IServiceProvider实例,因为此方法将处理任何实现IDisposable的实现,即使它们是单例。
  • I recommend having this code wrapped in #if DEBUG to ensure it doesn't go into production.我建议将此代码包含在#if DEBUG中,以确保它不会 go 投入生产。
  • A major downside to .NET Core's default DI system is that it's impossible to statically differentiate "scoped" service implementations from singletons and transient services - you could work-around this by adding marker-interfaces on the service implementations and handling those accordingly. .NET Core 的默认 DI 系统的一个主要缺点是不可能静态区分“范围”服务实现与单例和瞬态服务 - 您可以通过在服务实现上添加标记接口并相应地处理这些来解决这个问题。

Another note:另一个注意事项:

If you really wanted to, you could use the same allServiceInterfaceTypes technique above inside your ConfigureServices method to enumerate over all interface and detect all types implementing each interface, and discover any custom attributes on those types and automatically register them in the DI container - but I don't recommend doing that because reflection is slower than using DI as-intended - and configuring things like factory-methods would be much harder.如果你真的想要,你可以在ConfigureServices方法中使用上面相同的allServiceInterfaceTypes技术来枚举所有接口并检测实现每个接口的所有类型,并发现这些类型的任何自定义属性并自动将它们注册到 DI 容器中 - 但我不建议这样做,因为反射比按预期使用 DI 慢 - 并且配置像工厂方法这样的东西会更难。

You can use Scrutor to add assembly scanning capabilities to the ASP.NET Core DI container.您可以使用 Scrutor 向 ASP.NET Core DI 容器添加程序集扫描功能。

For details you can check this blog https://andrewlock.net/using-scrutor-to-automatically-register-your-services-with-the-asp-net-core-di-container/有关详细信息,您可以查看此博客https://andrewlock.net/using-scrutor-to-automatically-register-your-services-with-the-asp-net-core-di-container/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM