控制器注册为Transient却实现IDisposable,Autofac注册配置问题咨询
Let's break down the critical problems in your setup and walk through actionable fixes to get everything working reliably:
1. Transient Controller with IDisposable: The Cleanup Problem
When you register a controller as Transient and implement IDisposable, Autofac won't automatically track and dispose of these instances by default. Transient objects are meant to be short-lived and owned by the caller, but in web apps (like ASP.NET), controllers are tied directly to the HTTP request lifecycle—so a Transient controller can lead to unmanaged resource leaks if their Dispose method isn't called properly.
Fix: Switch Controllers to Scoped Lifecycle
Controllers should almost always use a Scoped lifecycle in web applications, since they're inherently tied to a single HTTP request. This way, Autofac will automatically track the controller instance and call Dispose when the request ends.
If you're using Autofac with ASP.NET Core, first ensure controllers are registered as services:
services.AddControllers().AddControllersAsServices();
Then register your controller in Autofac with the scoped lifestyle:
container.RegisterType<YourController>().AsSelf().InstancePerLifetimeScope();
This aligns the controller's lifecycle with your configured AsyncScopedLifestyle, ensuring proper cleanup of disposable resources.
2. Singleton SqlConnection: Thread-Safety & Lifecycle Mismatch
Your RegisterInstance<IDbConnection>(new SqlConnection(...)) registers the connection as a Singleton (the default behavior for RegisterInstance). This is a dangerous anti-pattern because:
SqlConnectionis not thread-safe—multiple concurrent requests (using your Scoped services) will try to use the same connection, leading to race conditions, broken queries, or connection state corruption.- Scoped services (like
AdvertRepositoryandAdvertService) should never depend on Singleton instances that hold state tied to a single request.
Fix: Register IDbConnection as Scoped
ADO.NET has built-in connection pooling, so creating a new SqlConnection per request is efficient and safe. Register it with a Scoped lifecycle to match your services:
container.Register(c => new SqlConnection(connectionString)) .As<IDbConnection>() .InstancePerLifetimeScope();
Now each request gets its own connection instance, which will be disposed when the request ends (since SqlConnection implements IDisposable), and the underlying connection will be returned to the pool for reuse.
3. Verify Async Scoped Lifestyle Configuration
You've set container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();—make sure this aligns with how you're creating scopes in your app:
- In ASP.NET Core, Autofac integrates with the request scope automatically, so this should work out of the box.
- If you're manually creating scopes in async code (e.g., background tasks), always use the async-scoped tag:
using (var scope = container.BeginLifetimeScope(AsyncScopedLifestyle.Tag)) { var service = scope.Resolve<IAdvertService>(); await service.DoWorkAsync(); }
Final Recommendations
- Always use Scoped lifecycle for web controllers: It's the most natural fit for request-bound objects and ensures proper disposal of
IDisposableimplementations. - Never register
SqlConnectionas a Singleton: Leverage ADO.NET's connection pooling by creating a new instance per request. - Avoid mixing lifecycle scopes incorrectly: Scoped services should only depend on other Scoped or Transient services—never Singletons that hold request-specific state.
内容的提问来源于stack exchange,提问作者user9703756

