using Microsoft.Extensions.DependencyInjection; using System; using System.Linq; using System.Reflection; // 自定义属性来标记服务类型 [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public class ServiceTypeAttribute : Attribute { public ServiceLifetime Lifetime { get; } public ServiceTypeAttribute(ServiceLifetime lifetime) { Lifetime = lifetime; } } public static class DependencyInjectionExtensions { /// <summary> /// 批量注册服务 /// </summary> /// <param name="services">IServiceCollection</param> /// <param name="servicePrefix">服务类的前缀,例如 "Service"</param> /// <param name="interfacePrefix">接口的前缀,例如 "I"</param> public static void RegisterServices(this IServiceCollection services, string servicePrefix, string interfacePrefix) { if (services == null) { throw new ArgumentNullException(nameof(services)); } // 获取当前程序集中所有类 var types = Assembly.GetExecutingAssembly().GetTypes(); // 筛选出符合服务命名约定的类 var serviceTypes = types .Where(t => t.Name.StartsWith(servicePrefix) && !t.IsInterface && !t.IsAbstract) .ToList(); // 遍历所有服务类 foreach (var serviceType in serviceTypes) { // 尝试查找对应的接口 var interfaceType = types.FirstOrDefault(t => t.Name == interfacePrefix + serviceType.Name.Substring(servicePrefix.Length) && t.IsInterface); if (interfaceType != null) { // 获取服务类型上的 ServiceTypeAttribute var serviceTypeAttribute = serviceType.GetCustomAttribute<ServiceTypeAttribute>(); var lifetime = serviceTypeAttribute?.Lifetime ?? ServiceLifetime.Scoped; // 默认使用 Scoped // 根据不同的生命周期注册服务 switch (lifetime) { case ServiceLifetime.Singleton: services.AddSingleton(interfaceType, serviceType); break; case ServiceLifetime.Scoped: services.AddScoped(interfaceType, serviceType); break; case ServiceLifetime.Transient: services.AddTransient(interfaceType, serviceType); break; default: services.AddScoped(interfaceType, serviceType); // 默认使用 Scoped break; } } else { // 如果没有找到对应的接口,则可以选择只注册服务本身,或者抛出异常 // 这里选择抛出异常,提示开发者需要有对应的接口 throw new InvalidOperationException($"Service {serviceType.Name} does not have a corresponding interface (expected interface name: {interfacePrefix}{serviceType.Name.Substring(servicePrefix.Length)})"); } } } } // 示例接口和服务 public interface IUserService { void DoSomething(); } [ServiceType(ServiceLifetime.Scoped)] // 使用属性标记生命周期 public class UserService : IUserService { public void DoSomething() { Console.WriteLine("UserService.DoSomething() called."); } } public interface IOrderService { void ProcessOrder(); } [ServiceType(ServiceLifetime.Transient)] public class OrderService: IOrderService { public void ProcessOrder() { Console.WriteLine("OrderService.ProcessOrder() called."); } } public class Program { public static void Main(string[] args) { // 创建 ServiceCollection IServiceCollection services = new ServiceCollection(); // 注册服务 services.RegisterServices("Service", "I"); // 添加其他必要的服务,例如 MVC services.AddMvc(); // 如果是 ASP.NET Core MVC 项目 // 构建 ServiceProvider IServiceProvider serviceProvider = services.BuildServiceProvider(); // 从 ServiceProvider 中获取服务并使用 var userService = serviceProvider.GetService<IUserService>(); userService?.DoSomething(); var orderService = serviceProvider.GetService<IOrderService>(); orderService?.ProcessOrder(); Console.ReadKey(); } }
代码说明
- ServiceTypeAttribute: 这是一个自定义属性,用于显式指定服务类的生命周期(Singleton、Scoped 或 Transient)。
- RegisterServices 扩展方法:
- 它扩展了
IServiceCollection
,提供了一个便捷的方法来批量注册服务。 servicePrefix
参数指定服务类的前缀(例如,"Service"
)。interfacePrefix
参数指定接口的前缀(例如,"I"
)。- 它使用反射来查找程序集中所有符合命名约定的类(例如,以 "Service" 开头的类)。
- 它假定接口的命名约定是接口前缀 + 服务类名(去掉服务前缀)。 例如,如果服务类是
UserService
,则对应的接口应该是IUserService
。 - 它使用
GetCustomAttribute
方法获取服务类上的ServiceTypeAttribute
属性,并根据属性中指定的生命周期注册服务。 如果未找到此属性,则默认使用Scoped
生命周期。 - 如果找不到与服务类对应的接口,它会抛出一个异常,指示缺少必需的接口。
- 它扩展了
- 示例:
IUserService
和UserService
演示了如何定义一个服务及其接口。IOrderService
和OrderService
演示了另一个服务及其接口,并使用[ServiceType]
特性标记了生命周期- 在
Main
方法中,我们创建了一个ServiceCollection
,调用RegisterServices
方法注册服务,然后从ServiceProvider
中解析并使用这些服务。
使用说明
- 定义您的服务接口和服务类,并确保它们遵循命名约定(例如,
IUserService
和UserService
)。 - 使用
ServiceTypeAttribute
属性标记您的服务类,以指定其生命周期。 如果省略此属性,则默认使用Scoped
生命周期。 - 在您的应用程序的启动配置中(例如,
Main
方法或ConfigureServices
方法中),获取IServiceCollection
的实例,并调用RegisterServices
扩展方法,传递服务类前缀和接口前缀。 - 像往常一样使用依赖注入来获取服务实例。
这个方法提供了一种灵活且类型安全的方式来批量注册服务,并允许您通过属性显式控制每个服务的生命周期。