.NET Core 2.0 Web API:无需在BLL实例化DbContext调用DAL的优雅方案
优雅解决DAL依赖注入问题的方案
这问题其实很好解决,ASP.NET Core自带的依赖注入(DI)容器就是专门用来处理这种场景的——让容器帮你管理所有依赖的实例化和传递,不用手动在控制器里处理一堆底层对象。下面是具体的优化步骤:
1. 给DAL类定义抽象接口
首先,我们要给ApplicationUserStore定义一个对应的接口,遵循依赖倒置原则,让上层(控制器)依赖抽象而非具体实现:
// DAL程序集中添加接口 public interface IApplicationUserStore { Task<ApplicationUser> Get(string id); Task<IdentityResult> Create(ApplicationUser user); // 后续保存密码哈希需要用到的更新方法 Task<IdentityResult> Update(ApplicationUser user); }
然后让你的ApplicationUserStore实现这个接口:
public class ApplicationUserStore : IApplicationUserStore { private readonly ApplicationDbContext _context; private readonly UserManager<ApplicationUser> _userManager; // 构造函数保留依赖注入,让DI容器自动传入所需对象 public ApplicationUserStore(ApplicationDbContext ctx, UserManager<ApplicationUser> userManager) { _context = ctx; _userManager = userManager; } public async Task<ApplicationUser> Get(string id) { return await _context.Users.Where(u => u.Id == id).SingleOrDefaultAsync(); } public async Task<IdentityResult> Create(ApplicationUser user) { return await _userManager.CreateAsync(user); } // 实现更新方法,用于保存密码哈希 public async Task<IdentityResult> Update(ApplicationUser user) { return await _userManager.UpdateAsync(user); } }
2. 将DAL服务注册到DI容器
在项目启动文件(.NET 6+是Program.cs,旧版本是Startup.cs)中,把IApplicationUserStore和它的实现类注册到DI容器:
// Program.cs 示例 var builder = WebApplication.CreateBuilder(args); // 先确保DbContext和Identity已注册(Identity模板项目通常默认已配置) builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>(); // 注册DAL服务,使用Scoped生命周期(与DbContext保持一致) builder.Services.AddScoped<IApplicationUserStore, ApplicationUserStore>();
3. 简化控制器的依赖注入
现在你的控制器只需要注入IApplicationUserStore和业务需要的IPasswordHasher,完全不用管DAL内部依赖的DbContext和UserManager——这些都会由DI容器自动处理:
public class UserController : Controller { private readonly IApplicationUserStore _userStore; private readonly IPasswordHasher<ApplicationUser> _passwordHasher; // 只注入需要的抽象依赖,代码瞬间清爽 public UserController(IApplicationUserStore userStore, IPasswordHasher<ApplicationUser> passwordHasher) { _userStore = userStore; _passwordHasher = passwordHasher; } [AllowAnonymous] [HttpPost("create")] public async Task<IActionResult> Create([FromBody] CreateApplicationUserViewModel model) { var user = new ApplicationUser() { Email = model.Email, UserName = model.Email, FirstName = model.FirstName, LastName = model.LastName }; var result = await _userStore.Create(user); if (result.Succeeded) { try { // 生成密码哈希后,调用Update方法保存到数据库 _passwordHasher.HashPassword(user, model.ConfirmPassword); await _userStore.Update(user); return Ok(); } catch (Exception ex) { return BadRequest(ex.Message); } } foreach (var error in result.Errors) { ModelState.AddModelError("error", error.Description); } return BadRequest(result.Errors); } }
为什么这样做更优雅?
- 解耦性更强:控制器只聚焦业务逻辑,无需知晓DAL层的底层依赖细节,后续修改DAL依赖时,控制器完全不用改动。
- 符合SOLID原则:依赖抽象而非具体实现,代码更易维护和测试(比如单元测试时可轻松替换
IApplicationUserStore的模拟实现)。 - 自动处理依赖链:DI容器会自动创建
ApplicationUserStore所需的DbContext和UserManager实例,避免手动new带来的重复代码和潜在错误。
补充个小细节:你原来的代码调用HashPassword后没有保存用户,会导致密码哈希无法写入数据库,所以上面的代码新增了Update方法来解决这个问题。
内容的提问来源于stack exchange,提问作者Rick Nijhuis
相关产品推荐
相关产品推荐

