ASP.NET MVC&OWIN中InstancePerRequest服务构造注入的Dispose问题
问题描述
我在ASP.NET MVC & OWIN中通过Autofac注册了InstancePerRequest生命周期的FooRequest服务:
builder.RegisterType<FooRequest>().AsSelf().InstancePerRequest();
之后在两处解析FooRequest:
- 在
global.asax的Application_BeginRequest()方法中:
protected void Application_BeginRequest(object sender, EventArgs e) { var fooRequest = DependencyResolver.Current.GetService<FooRequest>(); }
- 在标记为
InstancePerLifetimeScope的FooService构造函数中注入:
public class FooService { public FooService(FooRequest fooRequest) { ... } }
遇到的问题:
- 这两处解析得到的
FooRequest实例不相同 - 构造注入到
FooService中的FooRequest实例,在请求结束时不会调用Dispose[Async]方法 - 补充:在
FooService构造函数外使用DependencyResolver.Current.GetService<FooRequest>()能获取到正确的同实例FooRequest
补充配置信息
OWIN & 容器配置
[assembly: OwinStartup(typeof(Project.Web.Startup))] namespace Project.Web { public partial class Startup { public void Configuration(IAppBuilder app) { var builder = new ContainerBuilder(); // 注册控制器以支持构造函数注入 builder.RegisterControllers(typeof(MvcApplication).Assembly); builder.RegisterApiControllers(typeof(MvcApplication).Assembly); // 注册依赖项 builder.RegisterModule(new ProjectWebModule()); builder.Register(c => new IdentityFactoryOptions<ApplicationUserManager> { DataProtectionProvider = app.GetDataProtectionProvider() }); // 注册过滤器以支持属性注入 builder.RegisterFilterProvider(); // 构建容器 var container = builder.Build(); // 替换MVC依赖解析器为Autofac DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); // 替换WebAPI依赖解析器为Autofac GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container); // 向OWIN注册Autofac中间件 app.UseAutofacMiddleware(container); app.UseAutofacMvc(); app.Use((context, next) => { var httpContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName); httpContext.SetSessionStateBehavior(SessionStateBehavior.Required); return next(); }); // 标准MVC配置 RouteConfig.RegisterRoutes(RouteTable.Routes); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); // 始终在RouteConfig.RegisterRoutes之后执行! BundleConfig.RegisterBundles(BundleTable.Bundles); // 在RegisterGlobalFilters之后配置认证 ConfigureAuth(app); } } }
ProjectWebModule
namespace Project.Web.Autofac { public class ProjectWebModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType<FooRequest>().AsSelf().InstancePerRequest(); builder.RegisterType<FooService>().AsSelf().InstancePerLifetimeScope(); builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).As<IAuthenticationManager>(); builder.Register(c => // 当HttpContext不可用时注册FakeHttpContext HttpContext.Current != null ? new HttpContextWrapper(HttpContext.Current) as HttpContextBase : new FakeHttpContext("~/") as HttpContextBase) .As<HttpContextBase>() .InstancePerLifetimeScope() .OnActivated(e => { Toolbox.HttpContext = e.Instance; }); builder.Register(c => c.Resolve<HttpContextBase>().Request) .As<HttpRequestBase>() .InstancePerLifetimeScope(); builder.Register(c => c.Resolve<HttpContextBase>().Response) .As<HttpResponseBase>() .InstancePerLifetimeScope(); builder.Register(c => c.Resolve<HttpContextBase>().Server) .As<HttpServerUtilityBase>() .InstancePerLifetimeScope(); builder.Register(c => c.Resolve<HttpContextBase>().Session) .As<HttpSessionStateBase>() .InstancePerLifetimeScope(); base.Load(builder); } } }
问题原因与解决方案
核心原因
问题本质是请求生命周期范围的创建时机不匹配,以及FooService的生命周期范围与请求范围脱节:
Application_BeginRequest中解析时,Autofac会自动创建绑定到当前HttpContext的请求级生命周期范围,这个范围会在请求结束时自动释放,因此能拿到正确的FooRequest实例。FooService注册为InstancePerLifetimeScope,如果它在Autofac OWIN中间件创建请求范围前被解析,或者从根容器直接解析,会被绑定到根容器的子范围(而非请求范围)。此时注入的FooRequest属于这个子范围,既和请求范围的实例不同,也不会在请求结束时被自动释放。- 构造函数外调用
DependencyResolver.Current.GetService<FooRequest>()能拿到正确实例,是因为此时请求范围已存在,解析操作会使用当前请求范围。
解决方案
方案1:将FooService改为InstancePerRequest(推荐)
如果FooService本身是请求级服务,直接修改注册方式,让它绑定到请求范围,注入的FooRequest会自动归属请求范围并在请求结束时释放:
// 在ProjectWebModule中修改 builder.RegisterType<FooService>().AsSelf().InstancePerRequest();
方案2:确保FooService在请求范围内解析
若必须保留InstancePerLifetimeScope,要保证FooService的解析操作始终在请求范围内进行:
- 避免从根容器直接解析
FooService,统一通过DependencyResolver.Current(MVC场景)或Request.GetOwinContext().GetAutofacLifetimeScope()(OWIN场景)获取当前请求范围,再从该范围解析服务。 - 检查
FooService的触发时机,禁止在Application_Start等请求前阶段,或自定义非请求范围中解析。
方案3:调整OWIN中间件顺序(关键配置修正)
你的OWIN中间件顺序错误,AutofacMvc中间件应在所有MVC相关配置前执行,确保请求范围在MVC流程启动前创建:
修改Startup.Configuration中的顺序:
public void Configuration(IAppBuilder app) { var builder = new ContainerBuilder(); // ... 注册控制器、依赖项、过滤器等代码保持不变 ... var container = builder.Build(); // 替换依赖解析器代码保持不变 ... // 先注册Autofac中间件 app.UseAutofacMiddleware(container); app.UseAutofacMvc(); // 再设置Session行为 app.Use((context, next) => { var httpContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName); httpContext.SetSessionStateBehavior(SessionStateBehavior.Required); return next(); }); // 最后执行MVC标准配置 RouteConfig.RegisterRoutes(RouteTable.Routes); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); BundleConfig.RegisterBundles(BundleTable.Bundles); ConfigureAuth(app); }
方案4:替换HttpContextBase的注册方式(可选)
将HttpContextBase的生命周期改为InstancePerRequest,确保它始终绑定到请求范围,避免意外的子范围问题:
builder.Register(c => HttpContext.Current != null ? new HttpContextWrapper(HttpContext.Current) as HttpContextBase : new FakeHttpContext("~/") as HttpContextBase) .As<HttpContextBase>() .InstancePerRequest() .OnActivated(e => { Toolbox.HttpContext = e.Instance; });
内容的提问来源于stack exchange,提问作者Darth Sonic
相关产品推荐
相关产品推荐

