非控制器类中使用依赖注入实现邮箱激活功能遇阻求助
邮箱激活功能的依赖注入问题分析与修复
当前实现的核心问题
你现在通过Startup.ActivationService静态字段手动获取服务的方式,违反了依赖注入的设计原则——不仅硬编码了服务获取逻辑,还会因为手动调用BuildServiceProvider()创建额外的服务提供者,导致Scoped生命周期的服务(比如你的IActivationService)无法和当前请求上下文保持一致,容易出现数据不一致或资源泄漏的问题。
修正方案:让DI容器接管服务注入
我们需要重构代码,让所有依赖都通过构造函数注入,而非手动从Startup获取。
1. 重构EmailActivaitonKey类
去掉对Startup的硬依赖,改用构造函数注入IActivationService:
public class EmailActivaitonKey { private readonly IActivationService _activationService; // 构造函数注入依赖,让DI容器自动传递服务实例 public EmailActivaitonKey(IActivationService activationService) { _activationService = activationService ?? throw new ArgumentNullException(nameof(activationService)); } public string ActivationKey(string email) { string guid = Guid.NewGuid().ToString(); // 优化:用存在性判断代替实体查询,减少数据库负载 while (_activationService.GetByFilter(i => i.ActivationKey.Contains(guid)) != null) { guid = Guid.NewGuid().ToString(); } string key = $"{email}:OSK:{DateTime.Now}:OSK:{guid}"; EmailValid emailValid = new EmailValid { Email = email, Time = DateTime.Today, ActivationKey = key }; _activationService.Insert(emailValid); return new Helpers.AESEncryption().EncryptText(key); } }
2. 调整Startup的服务注册逻辑
删除手动创建静态服务实例的代码,只保留标准的DI注册,并把EmailActivaitonKey也纳入容器管理:
// 保留Scoped服务注册 services.AddScoped<IActivationService, ActivationService>(); // 将EmailActivaitonKey注册到DI容器,让容器管理它的依赖 services.AddScoped<EmailActivaitonKey>();
3. 在调用类中注入EmailActivaitonKey
不能再直接new EmailActivaitonKey(),要通过构造函数注入该类:
示例:在服务类中调用
public class EmailSenderService { private readonly EmailActivaitonKey _emailActivationKey; public EmailSenderService(EmailActivaitonKey emailActivationKey) { _emailActivationKey = emailActivationKey; } public void SendActivationEmail(string email) { string encryptedKey = _emailActivationKey.ActivationKey(email); MailMessage mailMessage = new MailMessage { From = new MailAddress("***@***.***"), // 把HTML内容放到Body,而非Subject Body = $"Crypto Box Activation <a href='/Email/Activation?key={encryptedKey}'><h1>Click For Activation</h1></a>", Subject = "Crypto Box Activation", IsBodyHtml = true, // 必须开启,否则HTML会被当作纯文本 To = { email } }; // 这里添加发送邮件的逻辑 } }
示例:在控制器中调用
public class EmailController : Controller { private readonly EmailActivaitonKey _emailActivationKey; public EmailController(EmailActivaitonKey emailActivationKey) { _emailActivationKey = emailActivationKey; } public IActionResult SendActivation(string email) { string encryptedKey = _emailActivationKey.ActivationKey(email); // 执行发送邮件逻辑... return Ok("激活邮件已发送"); } }
额外优化建议
- 移除冗余循环:
Guid.NewGuid()重复的概率极低,几乎可以忽略,建议去掉while循环,减少不必要的数据库查询。 - 仓储层扩展:给仓储添加
Any()方法,用于判断实体是否存在,比GetByFilter()更高效(只返回布尔值,无需加载实体)。 - 密钥时效性:可以给
EmailValid添加过期时间字段,避免无效的激活密钥长期存在数据库中。
内容的提问来源于stack exchange,提问作者OMANSAK
相关产品推荐
相关产品推荐

