如何在.NET Framework4.7 WebApi中用Structuremap正确配置EF Core DBContext请求单例?
嘿,刚好我之前在.NET Framework 4.7的WebApi项目里折腾过StructureMap和EF Core,来帮你理清这些问题~
1. 先说说你提到的“正确”配置指什么?
你说的“正确”在这个场景下,其实就是指每个HTTP请求内DbContext只存在一个实例——也就是我们常说的Scoped生命周期。
为啥要这么做?因为EF Core的DbContext是按工作单元模式设计的:
- 同一个请求里复用实例,能保证实体状态跟踪的一致性,还能避免重复创建数据库连接带来的性能损耗;
- 要是用默认的Transient(每次请求都new新实例),会频繁创建销毁DbContext,性能拉胯;
- 要是搞成应用级单例,那绝对是大坑——DbContext根本不是线程安全的,多请求并发时会出现实体状态混乱、查询结果异常甚至数据库连接泄漏的问题。
2. 你贴的代码里DbContext会变成应用单例吗?
答案是会的!你现在的注册写法:
For<DistributedTaskDbContext>().Use(() => new DistributedTaskDbContextFactory().CreateDbContext(null));
在StructureMap里,这种没有显式指定生命周期的注册,默认就是Singleton(应用级单例)。也就是说整个应用运行期间只会创建一次DbContext实例,这完全不符合EF Core的使用规范,一定要改!
3. .NET Framework 4.7 WebApi里的HttpContextScoped去哪了?
其实它没消失,在适配.NET Framework WebApi的StructureMap版本里,HttpContextScoped()就是专门用来绑定到HTTP请求生命周期的方法,只是你没在代码里显式用上而已。
给你改好的正确配置:
针对你的项目,需要把DbContext(以及依赖它的仓库)都注册为请求级别的实例:
public class DistributedTaskRegistry : Registry { public DistributedTaskRegistry() { // 注册DbContext为请求级单例 For<DistributedTaskDbContext>() .Use(() => new DistributedTaskDbContextFactory().CreateDbContext(null)) .HttpContextScoped(); // 仓库也跟着用请求级生命周期,避免依赖的DbContext生命周期不一致 For<IDistributedTaskRepository>().Use<DistributedTaskRepository>() .HttpContextScoped(); } }
另外要确保你的WebApi项目已经正确集成了StructureMap:
- 安装
StructureMap.WebApi2NuGet包; - 在
WebApiConfig.cs里设置依赖解析器:
var container = new Container(new DistributedTaskRegistry()); GlobalConfiguration.Configuration.DependencyResolver = new StructureMapDependencyResolver(container);
这样StructureMap才能识别当前的HttpContext,正确管理请求级别的实例生命周期。
额外小提醒
如果你用的是较新的StructureMap版本,可能HttpContextScoped()被重命名为Scoped(),但在.NET Framework WebApi环境下,HttpContextScoped()的语义更准确,它会严格绑定到当前HTTP请求的生命周期。
内容的提问来源于stack exchange,提问作者jenson-button-event

