Blazor交互式服务器跨服务DBContext修改的架构难题
背景与架构设计目标
我试用Blazor交互式服务器已有一段时间,整体体验不错,目前正处于应用核心架构的构建阶段。我的设计思路是为每个实体提供独立的业务服务(如CarService、MakeService等),部分服务会执行复杂业务逻辑后完成实体的增删改操作,这么做是为了实现关注点分离并遵循DRY原则,避免业务逻辑在多个服务中重复,同时不让服务之间知晓彼此的业务细节。
我期望这套架构满足以下特性:
- 构建UI时,用户可通过各实体服务对不同类型实体执行多项修改,视图层仅需调用DBContext的单个
SaveChangesAsync方法即可完成所有修改的持久化,比如点击页面保存按钮时执行该操作。 - 支持在一个服务中调用其他实体服务完成业务操作,最终通过DBContext的单个
SaveChangesAsync提交所有修改,或者将提交操作延迟到视图层执行。
参考服务定义
public abstract class OrderService<TEntity> : ILXEntityService<TEntity> where TEntity : class { private LXDBContext _dbContext; private AnalysisService _analysisService; public LXModelService(LXDBContext dbContext, AnalysisService analysisService) { this._dbContext = dbContext; // 依赖注入 this._analysisService = analysisService; } // ... public async Task Deactivate(Order order) { // 示例简化,未包含事务处理 foreach (Analysis analysis in order.analyses) { await _analysisService.Deactivate(analysis); // 调用其他服务 } order.activefl = false; } // ... }
期望的Blazor组件操作逻辑
@inject LXDBContext dbContext @inject AnalysisService analysisService <button @onclick="OnDeactivate()">Deactivate</button> <button @onclick="OnSave()">Save</button> @code { public async Task OnDeactivate() { await analysisService.Deactivate(lorder); // 执行修改操作 } public async Task OnSave() { await dbContext.SaveChangesAsync(); // 统一保存所有修改 } }
核心冲突问题
使用Scoped生命周期的DBContext时,上述架构可以正常工作,但Scoped DBContext本身会引发诸多并发问题,最常见的错误提示为:
a second operation was started on this context instance before a previous operation completed. this is usually caused by different threads concurrently using the same instance of DBContext
在Blazor场景下,多个组件/页面共享同一个Scoped DBContext实例时,这类问题几乎无法避免。
另一种方案是使用Transient生命周期或按操作实例化DBContext(这也是微软官方推荐的Blazor中DBContext的使用方式),但这种方式会丧失EF Core的诸多核心特性:延迟加载、变更跟踪、一级缓存、工作单元模式等,此时EF Core仅相当于一个普通ORM框架。
对我当前构建的应用和实体规模而言,变更跟踪功能是必不可少的——我无法接受EF Core在保存时覆盖所有属性(包括未修改的属性)。
由于刚接触Blazor,我相信有Blazor和EF Core使用经验的开发者一定有对应的解决方案,但目前网上搜索未找到有效线索。
内容的提问来源于stack exchange,提问作者LostSoul

