C# 通过注释注册.NET核心单例服务
有没有办法通过注释将单例服务自动连接(自动注册)到C#DI容器() 例如,类似于、@injectable()注释in、自动连线in或自动连线in? 请参见下面的示例: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(有时会忘记这样做):
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
中,以确保它不会投入生产- 虽然为了公平起见,-但您需要使用
.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
}
}
}