dotNet 整合 Microsoft.Extensions.DependencyInjection 利用反射实现依赖批量注入
前言
框架
.net 8.0
Microsoft.Extensions.DependencyInjection 8.0
创建三个类库项目
ConfigServices
LogServices
MailServices
创建一个控制台项目
ConsoleAppMail
ConfigServices
namespace ConfigServices.IService
{
public interface IConfigService
{
public string GetValue(string key);
}
}
namespace ConfigServices.IService
{
public class EnvVarConfigServiceImpl : IConfigService
{
public string GetValue(string key)
{
return Environment.GetEnvironmentVariable(key);
}
}
}
LogServices
namespace LogServices.IService
{
public interface ILogProvider
{
public void LogInfo(string message);
public void LogError(string message);
}
}
namespace LogServices.IService
{
public class LogProviderImpl : ILogProvider
{
public void LogInfo(string message)
{
Console.WriteLine($"INFO:{message}");
}
public void LogError(string message)
{
Console.WriteLine($"ERROR:{message}");
}
}
}
MailServices
public interface IMailService
{
public void Send(string title, string to, string body);
}
在邮件服务里面需要引用 LogProvider 和 ConfigService 在这里需要依赖项里面添加项目引用,在 Microsoft.Extensions.DependencyInjection 里面默认是采用构造函数注入,只需要在构造函数中编写就可以了,在创建对象的时候 DependencyInjection 框架会帮助我们实现依赖注入(DI)。
namespace MailServices.IService
{
public class MailServiceImpl : IMailService
{
private readonly ILogProvider log;
private readonly IConfigService config;
public MailServiceImpl(ILogProvider log, IConfigService config)
{
this.log = log;
this.config = config;
}
public void Send(string title, string to, string body)
{
log.LogInfo("准备发送邮件");
string smtpServer = this.config.GetValue("smtpServer");
string username = this.config.GetValue("username");
string password = this.config.GetValue("password");
Console.WriteLine($"邮件服务器地址{smtpServer},{username},{password}");
Console.WriteLine("发邮件!");
log.LogInfo("邮件发送完成");
}
}
}
ConsoleAppMail
在控制台项目相当于我们的启动项, 需要告诉 DependencyInjection 框架那些类需要注入到容器中。
单个注入
单个注入比较麻烦,假如有100个service 就需要在Main 函数里面写100个,所以就有了通过反射的方式来批量注入。
using ConfigServices.IService;
using LogServices.IService;
using MailServices.IService;
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleAppMail
{
internal class Program
{
static void Main(string[] args)
{
ServiceCollection services = new ServiceCollection();
services.AddScoped<ILogProvider,LogProviderImpl>();
services.AddScoped<IConfigService,EnvVarConfigServiceImpl>();
services.AddScoped<IMailService,MailServiceImpl>();
using (var sp = services.BuildServiceProvider())
{
var mailService = sp.GetRequiredService<IMailService>();
mailService.Send("hello", "hshd@163.com", "你好 朋友");
}
Console.ReadLine();
}
}
}
批量注入
通过反射可以批量读取程序集信息,将对应的接口和对应的实现类注入到IOC 容器中。
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
namespace ConsoleAppMail.util
{
public static class ServiceCollectionExtensions
{
/// <summary>
/// 批量注册指定程序集中的所有实现了接口的类到 ServiceCollection
/// </summary>
/// <param name="services">ServiceCollection 实例</param>
/// <param name="assembly">要扫描的程序集</param>
public static IServiceCollection RegisterServicesFromAssembly(this IServiceCollection services, Assembly assembly)
{
var types = assembly.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Any()); // 筛选出实现接口的类
foreach (var type in types)
{
var serviceInterface = type.GetInterfaces().FirstOrDefault();
if (serviceInterface != null)
{
services.AddScoped(serviceInterface, type); // 注册为 Scoped 生命周期
}
}
return services;
}
}
}
static void Main(string[] args)
{
var assembly1 = Assembly.Load("ConfigServices");
var assembly2 = Assembly.Load("LogServices");
var assembly3 = Assembly.Load("MailServices");
ServiceCollection services = new ServiceCollection();
// 批量注册当前程序集中的服务
services.RegisterServicesFromAssembly(assembly1);
services.RegisterServicesFromAssembly(assembly2);
services.RegisterServicesFromAssembly(assembly3);
using (var sp = services.BuildServiceProvider())
{
var mailService = sp.GetRequiredService<IMailService>();
mailService.Send("hello", "hshd@163.com", "你好 朋友");
}
Console.ReadLine();
}
通过该例子可以了解一哈 DependencyInjection 框架的基本使用方式,使用前需要了解一下依赖注入(DI)和IOC 容器的知识。