Blazor中能否像ASP.NET Core MVC一样通过服务操作DbContext?
问题解答
- 这不是强制要求,但极不推荐你直接沿用ASP.NET Core MVC中的直接注入DbContext的用法,否则大概率会遇到并发异常、数据过期、内存膨胀等难以排查的问题。
为什么MVC里的用法在Blazor Server不适用
ASP.NET Core MVC的Scoped服务生命周期和单个HTTP请求绑定,请求结束就自动释放Scoped的DbContext,不会有长期持有上下文的问题。但Blazor Server的Scoped生命周期是和用户的SignalR长连接绑定的,只要用户不关闭页面,连接就会一直存在,你注入的Scoped DbContext会被持续持有数分钟甚至数小时,会触发以下问题:
- DbContext本身不是线程安全的,Blazor中用户多次点击、异步事件并发触发时,多个操作同时访问同一个DbContext会直接抛出并发异常
- 长期存活的DbContext会持续缓存跟踪过的实体,内存占用越来越高,同时读取到的是缓存的过期数据,不会和数据库最新状态同步
- 容易意外产生长事务,导致数据库锁表、性能下降
改造方案(无需大幅重构原有业务代码)
你不需要完全推翻已写好的GameService逻辑,只要调整依赖注入的对象即可,同时兼容MVC和Blazor场景:
第一步 修改服务注册
// 替换原来的AddDbContextPool,改用工厂注册 services.AddDbContextFactory<AppDbContext>(option => { option .UseMySql( Configuration.GetConnectionString("SqliteConstr"), // 注意此处连接字符串名如果是MySQL建议修改,避免歧义 new MySqlServerVersion(new Version(5, 5, 62)), MysqlOpt => MysqlOpt.EnableRetryOnFailure() ); }); // GameService的注册保持不变 services.AddScoped<GameService>();
第二步 改造GameService实现
public class GameService { private readonly IDbContextFactory<AppDbContext> _contextFactory; public GameService(IDbContextFactory<AppDbContext> contextFactory) { _contextFactory = contextFactory; } public async Task<Game[]> GetGamesAsync() { // 每个方法内部创建独立的DbContext,using块结束自动释放 using var DbContext = _contextFactory.CreateDbContext(); return await DbContext.Games.ToArrayAsync(); } // 其他增删改方法同理,每个操作单独创建上下文即可 }
改造后你原来调用GameService的上层代码完全不需要改动,既可以在MVC中使用,也可以在Blazor组件中直接注入调用,避免了直接操作DbFactory的重复代码。
内容的提问来源于stack exchange,提问作者SmRiley
相关产品推荐
相关产品推荐

