.NET Core DI作用域服务值传递为空:控制器到仓库取值异常
.NET Core 依赖注入(DI)作用域服务值传递问题
问题描述
登录时已成功为ScopService的UserName属性赋值,但跳转到HomeController后,注入的_scopService的UserName始终为null,无法将值传递到HomeRepo中。已将ScopService注册为作用域服务,请教如何正确将值传递到仓库?
代码示例
// ConfigureServices 中的服务注册 ConfigureServices(IServiceCollection services) { services.AddScoped<ScopService>(); } // LogInController public class LogInController { private readonly ScopService _userService; public LogInController(ScopService userService) { _userService = userService; } public ActionResult LogIn() { // 假设 Userdt 是查询到的数据表 _userService.UserName = Convert.ToString(Userdt.Rows[0]["UserName"]); // 值设置正确 return RedirectToAction("Index", "Home"); } } // HomeController public class HomeController { private readonly HomeRepo _repo; public ScopService _userService { get; } public HomeController(ScopService ser) { _userService = ser; // 此处 _userService.UserName 为 null this.repo = new HomeRepo(_userService); } } // HomeRepo public class HomeRepo : _baseRepo { public ScopService _service { get; } public HomeRepo(ScopService service) : base(service) { _service = service; // 此处 _service.UserName 为 null } public void Find() { string username = _service.UserName; } } // _baseRepo public class _baseRepo { protected readonly ScopService _userService; public _baseRepo(ScopService userService) { _userService = userService; } }
注:原问题提及“.NET Core单例服务控制器出现空服务”,但实际注册的是作用域服务。
问题原因与解决方案
核心原因
HTTP请求是无状态的,每个HTTP请求对应一个独立的DI作用域。登录请求是一个作用域,跳转后的Home请求是另一个全新的作用域,两个请求中的ScopService是完全独立的实例,因此登录时设置的值无法跨请求保留。
正确解决方案
方案1:使用Session存储用户信息(简单实用)
- 启用Session支持:
在ConfigureServices中添加Session配置:
services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); // 设置Session超时时间 options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); // 注册HttpContextAccessor用于访问Session services.AddHttpContextAccessor();
在Configure管道中启用Session(注意顺序,需放在路由之后、端点之前):
app.UseRouting(); app.UseSession(); // 启用Session app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); });
- 登录时将用户信息存入Session:
public ActionResult LogIn() { string userName = Convert.ToString(Userdt.Rows[0]["UserName"]); // 将用户名存入Session HttpContext.Session.SetString("UserName", userName); return RedirectToAction("Index", "Home"); }
- 在HomeController和HomeRepo中读取Session:
修改HomeController和HomeRepo,通过IHttpContextAccessor获取Session中的用户名:
// HomeController public class HomeController { private readonly HomeRepo _repo; public HomeController(IHttpContextAccessor httpContextAccessor) { string userName = httpContextAccessor.HttpContext.Session.GetString("UserName"); _repo = new HomeRepo(userName); } } // HomeRepo 修改为直接接收用户名 public class HomeRepo : _baseRepo { private readonly string _userName; public HomeRepo(string userName) : base(userName) { _userName = userName; } public void Find() { string username = _userName; } } // 修改_baseRepo public class _baseRepo { protected readonly string _userName; public _baseRepo(string userName) { _userName = userName; } }
方案2:使用ASP.NET Core认证系统(生产环境首选)
如果是正规的用户登录场景,推荐使用ASP.NET Core Identity或JWT认证,认证成功后用户信息会自动绑定到User对象,无需手动传递:
- 配置认证系统后,登录时调用认证API完成登录(例如
SignInManager.PasswordSignInAsync)。 - 在任意控制器或服务中通过
ClaimsPrincipal获取用户信息:
// 在HomeController中直接获取 public ActionResult Index() { string userName = User.Identity.Name; return View(); } // 在HomeRepo中通过IHttpContextAccessor获取 public class HomeRepo { private readonly IHttpContextAccessor _httpContextAccessor; public HomeRepo(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public void Find() { string userName = _httpContextAccessor.HttpContext.User.Identity.Name; } }
方案3:改造ScopService为基于Session的服务
如果一定要保留ScopService的结构,可以将其改造为依赖Session的服务,确保跨请求能读取到同一个值:
public class ScopService { private readonly IHttpContextAccessor _httpContextAccessor; public ScopService(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public string UserName { get => _httpContextAccessor.HttpContext.Session.GetString("UserName"); set => _httpContextAccessor.HttpContext.Session.SetString("UserName", value); } }
注册方式不变,但此时ScopService的UserName会从Session中读写,跨请求也能获取到正确值。
内容的提问来源于stack exchange,提问作者Umer Ullah
相关产品推荐
相关产品推荐

