.NET Framework Web API中Autofac InstancePerRequest同请求返回新实例问题
.NET Framework Web API 下 Autofac InstancePerRequest 不生效的修复方案
核心根因
- Autofac 集成 ASP.NET Web API 的 InstancePerRequest 生命周期,生效的前提是所有解析动作都基于当前 HttpRequestMessage 关联的请求级 LifetimeScope。
- 你代码里手动调用
DependencyResolver.BeginScope()的操作,创建的是和当前HTTP请求完全无绑定的独立顶级scope,每次调用都会生成全新scope,自然每次解析注册为InstancePerRequest的服务时,都会创建新实例。 - 全局
AutofacWebApiDependencyResolver对应的是根容器派生的Resolver,从它开出来的scope都是独立的顶级scope,和Web API框架自动为每个请求创建的请求scope没有任何关联,根本拿不到请求范围内的单例。 - 你在过滤器中把后续请求处理逻辑包在手动创建的scope内,调用
next()进入控制器阶段时,框架会使用自身绑定的请求scope解析控制器依赖,这个scope和你手动创建的scope没有父子关系,两个scope下的InstancePerRequest服务实例完全隔离,自然拿到的不是同一个对象。
代码修正点
1. 自定义过滤器修正
不要手动从全局Resolver开scope,直接从框架传入的HttpActionContext.Request对象上获取当前请求已经绑定好的scope:
public class MyCustomFilter : IAutofacContinuationActionFilter { public async Task<HttpResponseMessage> ExecuteActionFilterAsync( HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> next) { // 直接取当前请求绑定的scope,无需手动创建新scope var requestScope = actionContext.Request.GetDependencyScope(); var hs = (IHomeService)requestScope.GetService(typeof(IHomeService)); hs.Counter++; // 同一scope内重复解析,拿到的是同一个实例 var sameHs = (IHomeService)requestScope.GetService(typeof(IHomeService)); sameHs.Counter++; return await next(); } }
2. 控制器代码修正
不要从全局Resolver取scope,直接用ApiController基类自带的Request属性获取当前请求绑定的scope:
[Route("home")] public class HomeController : ApiController { private readonly IHomeService _injectedHomeService; // 构造函数注入的服务,本身就是框架从请求scope解析的实例 public HomeController(IHomeService homeService) { _injectedHomeService = homeService; } [HttpGet] [Route("")] public string Index() { var requestScope = Request.GetDependencyScope(); var resolvedHomeService = (IHomeService)requestScope.GetService(typeof(IHomeService)); resolvedHomeService.Counter++; // 此时 _injectedHomeService、resolvedHomeService、过滤器中解析的IHomeService 为同一个实例 return "Home"; } }
3. 独立类链路的通用获取方案
针对无法使用构造函数注入的自动生成代码链路,可封装静态帮助类,从当前请求上下文获取统一的请求scope:
public static class RequestScopeHelper { public static T GetRequestService<T>() { var currentContext = HttpContext.Current; // Web API框架会自动把当前请求的HttpRequestMessage存在HttpContext的Items集合中 if (currentContext?.Items[typeof(HttpRequestMessage).FullName] is not HttpRequestMessage request) { throw new InvalidOperationException("当前代码不在Web API请求处理管道内"); } var scope = request.GetDependencyScope(); return (T)scope.GetService(typeof(T)); } }
注意事项
- 所有需要获取请求级单例的解析动作,都必须从当前
HttpRequestMessage绑定的scope出发,不能绕开这个scope手动创建新的独立scope。 - 该实现效果和.NET Core 中注册Scoped生命周期服务的行为完全一致:同一请求内,无论在哪一层代码解析,拿到的都是同一个实例;请求结束后,scope会被自动释放,服务实例也会被回收。
内容的提问来源于stack exchange,提问作者g3v
相关产品推荐
相关产品推荐

