C# 通过注释注册.NET核心单例服务

C# 通过注释注册.NET核心单例服务,c#,.net-core,dependency-injection,C#,.net Core,Dependency Injection,有没有办法通过注释将单例服务自动连接(自动注册)到C#DI容器() 例如,类似于、@injectable()注释in、自动连线in或自动连线in? 请参见下面的示例: import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class UserService { } 此时,我必须以这种方式手动将每个单例服务添加到ServiceCollection(有时会忘记这样做):

有没有办法通过注释将单例服务自动连接(自动注册)到C#DI容器()

例如,类似于、@injectable()注释in、自动连线in或自动连线in

请参见下面的示例:

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

@Injectable({
  providedIn: 'root',
})
export class UserService {
}
此时,我必须以这种方式手动将每个单例服务添加到ServiceCollection(有时会忘记这样做):

内部静态服务提供程序SetupDi()
{
返回新的ServiceCollection()//Microsoft.Extensions.DependencyInjection.ServiceCollection.ServiceCollection()
.AddDbContext()
.AddSingleton()
.AddSingleton();
}
所需的等效解决方案如下:

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

[Injectable()]
public class ServiceA
{
}

[Injectable()]
public class ServiceB
{
}
内部静态服务提供程序SetupDi()
{
返回新的ServiceCollection()//Microsoft.Extensions.DependencyInjection.ServiceCollection.ServiceCollection()
.AddDbContext();
}
[可注射()]
公共类服务
{
}
[可注射()]
公共类服务B
{
}

不,这实际上是出于设计

DI的全部要点在于,程序的配置方式并不令人惊讶:您在
ConfigureServices
中配置的所有内容(在您的情况下,
SetupDi
)正是您在运行时得到的。通过使用属性来配置DI,这将引入“非局部效应”,并且跟踪错误属性导致的不正确或错误配置的依赖项所引入的bug将更加困难

(在某种程度上,我不同意Angular的设计——但那是离题的)

(我还觉得.NETCore的DI系统也不完善——DI注入扩展方法背后隐藏了太多必要的细节,您需要使用ILSpy或Reflector来窥探)

作为一种解决方法,您可以在应用程序启动时“测试”DI服务,通过对项目中的每个
iSeries设备进行反思并尝试实例化实现来确保所有内容都已配置

以下是我在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
        }
    }
}
内部静态void TestAllServices(iSeries ViceProvider sp)
{
Assembly[]mySolutionAssemblies=new[]
{
类型(来自assemblyx.Foobar).assembly,
类型(FromAssembly Y.Foobar).Assemby,
类型(来自Assemblyz.Foobar).Assemby,
};
列出所有ServiceInterfaceTypes=MySolutionAssembly
.SelectMany(ass=>ass.GetTypes())
.Where(t=>t.IsInterface和&t.IsPublic)
.Where(/*筛选出您不想在此处验证的接口*/)
.ToList();
foreach(在serviceInterfaceTypes中键入serviceInterfaceType)
{
尝试
{
对象实现=sp.GetRequiredService(serviceInterfaceType);
if(实现是IDisposable disp)disp.Dispose();
}
捕获(例外情况除外)
{
//在此处记录错误或抛出或设置断点
}
}
}
注:

  • 不要将“真正的”IServiceProvider
传递到
TestAllServices
——而是创建一个单独的
IServiceProvider
实例,因为此方法将处理实现
IDisposable
的任何实现,即使它们是单例
  • 我建议将此代码包装在
    #if DEBUG
    中,以确保它不会投入生产
  • NET Core的默认DI系统的一个主要缺点是不可能静态区分“作用域”服务实现与单例服务和瞬态服务-您可以通过在服务实现上添加标记接口并相应地处理它们来解决这个问题。
    • 虽然为了公平起见,-但您需要使用
      .addcontrollerasservices()
  • 另一项说明:

    如果您真的愿意,您可以在
    ConfigureServices
    方法中使用相同的
    allServiceInterfaceTypes
    技术来枚举所有接口并检测实现每个接口的所有类型,并发现这些类型上的任何自定义属性,并自动将它们注册到DI容器中——但我不建议这样做,因为反射比预期使用DI慢——配置工厂方法之类的东西要困难得多

    您可以使用Scrutor将程序集扫描功能添加到 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
            }
        }
    }