### Facts
- I am using AutoFac as IoC container
- I enabled tracing with my custom tracer.
- During the trace operation, I try to get the `DependencyScope` through request.Properties collection.
### Result
At the end of the request (when the response is ready to go out the door), `System.Net.Http.HttpRequestMessageExtensions.DisposeRequestResources` is called to dispose the list of disposables registered inside the `request.Properties["MS_DisposableRequestResources"]`. When tracing is enabled and I am using AutoFac, there are two registered disposables (in the same order as below):
- `Autofac.Integration.WebApi.AutofacWebApiDependencyScope`
- `System.Web.Http.Tracing.Tracers.HttpControllerTracer`
As `DisposeRequestResources` method goes through the list in order, `AutofacWebApiDependencyScope` is disposed before the `HttpControllerTracer`. The funny thing is that `HttpControllerTracer.Dispose` calls the registered tracer to write the End trace and as my trace needs the `DependencyScope`, it blows with the below error since the `DependencyScope` is now disposed:
> {"Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed."}
>
> at Autofac.Core.Lifetime.LifetimeScope.CheckNotDisposed()
> at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
> at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
> at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable`1 parameters)
> at Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType, IEnumerable`1 parameters)
> at Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType)
> at Autofac.Integration.WebApi.AutofacWebApiDependencyScope.GetService(Type serviceType)
> at MyProject.HttpRequestMessageExtensions.GetService[TService](HttpRequestMessage request) in e:\Dropbox\Apps\Clients\Serdar\MyProject\src\MyProject.API\Http\HttpRequestMessageExtensions.cs:line 64
> at MyProject.Http.HttpRequestMessageExtensions.GetLoggingService(HttpRequestMessage request) in e:\Dropbox\Apps\Clients\Serdar\MyProject\src\MyProject.API\Http\HttpRequestMessageExtensions.cs:line 33
> at MyProject.API.Tracing.MyProjectTracer.Log(TraceRecord traceRecord) in e:\Dropbox\Apps\Clients\Serdar\MyProject\src\MyProject.API\Tracing\MyProjectTracer.cs:line 23
> at MyProject.API.Tracing.MyProjectTracer.Trace(HttpRequestMessage request, String category, TraceLevel level, Action`1 traceAction) in e:\Dropbox\Apps\Clients\Serdar\MyProject\src\MyProject.API\Tracing\MyProjectTracer.cs:line 17
> at System.Web.Http.Tracing.ITraceWriterExtensions.TraceBeginEnd(ITraceWriter traceWriter, HttpRequestMessage request, String category, TraceLevel level, String operatorName, String operationName, Action`1 beginTrace, Action execute, Action`1 endTrace, Action`1 errorTrace)
> at System.Web.Http.Tracing.Tracers.HttpControllerTracer.System.IDisposable.Dispose()
> at System.Net.Http.HttpRequestMessageExtensions.DisposeRequestResources(HttpRequestMessage request)
## Suggestion
`System.Net.Http.HttpRequestMessageExtensions.DisposeRequestResources` **should** have a deep knowledge about the `DependencyScope` and **should** dispose it lastly.
### Workaround
There are several workarounds for that (replacing the HttpControllerTracer, etc.) but the one I applied is very dirty. Adding a message handler as the first message handler (so that it runs last (just in case)) and reordering the disposables on the way out:
public class DisposableRequestResourcesReorderHandler : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
return base.SendAsync(request, cancellationToken).Finally(() => {
List<IDisposable> disposableResources = request.Properties[HttpPropertyKeys.DisposableRequestResourcesKey] as List<IDisposable>;
if (disposableResources != null && disposableResources.Count > 1) {
// 1-) Get the first one (which I know is AutofacWebApiDependencyScope).
// 2-) Remove it from the list.
// 3-) Push it at the end of the list.
IDisposable dependencyScope = disposableResources[0];
disposableResources.RemoveAt(0);
disposableResources.Add(dependencyScope);
}
}, runSynchronously: true);
}
}
Comments: Hi HongmeiG, Thanks for the response. I don't think you need to take a dependency on IoC container for this at all. The problem is related to fact that the Web API framework registers the IDependencyScope itself to be disposed by the host. If you have a look at the GetDependencyScope extension method, you will see the below code: public static IDependencyScope GetDependencyScope(this HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } IDependencyScope result; if (!request.Properties.TryGetValue<IDependencyScope>(HttpPropertyKeys.DependencyScope, out result)) { IDependencyResolver dependencyResolver = request.GetConfiguration().DependencyResolver; result = dependencyResolver.BeginScope(); if (result == null) { throw Error.InvalidOperation(SRResources.DependencyResolver_BeginScopeReturnsNull, dependencyResolver.GetType().Name); } request.Properties[HttpPropertyKeys.DependencyScope] = result; request.RegisterForDispose(result); } return result; } Then, the problem I explained inside [this post](http://www.tugberkugurlu.com/archive/asp-net-web-api-tracing-and-idependencyscope-dispose-issue) happens. This is a bug for sure IMO. The question is: what would be the best way to solve this.
- I am using AutoFac as IoC container
- I enabled tracing with my custom tracer.
- During the trace operation, I try to get the `DependencyScope` through request.Properties collection.
### Result
At the end of the request (when the response is ready to go out the door), `System.Net.Http.HttpRequestMessageExtensions.DisposeRequestResources` is called to dispose the list of disposables registered inside the `request.Properties["MS_DisposableRequestResources"]`. When tracing is enabled and I am using AutoFac, there are two registered disposables (in the same order as below):
- `Autofac.Integration.WebApi.AutofacWebApiDependencyScope`
- `System.Web.Http.Tracing.Tracers.HttpControllerTracer`
As `DisposeRequestResources` method goes through the list in order, `AutofacWebApiDependencyScope` is disposed before the `HttpControllerTracer`. The funny thing is that `HttpControllerTracer.Dispose` calls the registered tracer to write the End trace and as my trace needs the `DependencyScope`, it blows with the below error since the `DependencyScope` is now disposed:
> {"Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed."}
>
> at Autofac.Core.Lifetime.LifetimeScope.CheckNotDisposed()
> at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
> at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
> at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable`1 parameters)
> at Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType, IEnumerable`1 parameters)
> at Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType)
> at Autofac.Integration.WebApi.AutofacWebApiDependencyScope.GetService(Type serviceType)
> at MyProject.HttpRequestMessageExtensions.GetService[TService](HttpRequestMessage request) in e:\Dropbox\Apps\Clients\Serdar\MyProject\src\MyProject.API\Http\HttpRequestMessageExtensions.cs:line 64
> at MyProject.Http.HttpRequestMessageExtensions.GetLoggingService(HttpRequestMessage request) in e:\Dropbox\Apps\Clients\Serdar\MyProject\src\MyProject.API\Http\HttpRequestMessageExtensions.cs:line 33
> at MyProject.API.Tracing.MyProjectTracer.Log(TraceRecord traceRecord) in e:\Dropbox\Apps\Clients\Serdar\MyProject\src\MyProject.API\Tracing\MyProjectTracer.cs:line 23
> at MyProject.API.Tracing.MyProjectTracer.Trace(HttpRequestMessage request, String category, TraceLevel level, Action`1 traceAction) in e:\Dropbox\Apps\Clients\Serdar\MyProject\src\MyProject.API\Tracing\MyProjectTracer.cs:line 17
> at System.Web.Http.Tracing.ITraceWriterExtensions.TraceBeginEnd(ITraceWriter traceWriter, HttpRequestMessage request, String category, TraceLevel level, String operatorName, String operationName, Action`1 beginTrace, Action execute, Action`1 endTrace, Action`1 errorTrace)
> at System.Web.Http.Tracing.Tracers.HttpControllerTracer.System.IDisposable.Dispose()
> at System.Net.Http.HttpRequestMessageExtensions.DisposeRequestResources(HttpRequestMessage request)
## Suggestion
`System.Net.Http.HttpRequestMessageExtensions.DisposeRequestResources` **should** have a deep knowledge about the `DependencyScope` and **should** dispose it lastly.
### Workaround
There are several workarounds for that (replacing the HttpControllerTracer, etc.) but the one I applied is very dirty. Adding a message handler as the first message handler (so that it runs last (just in case)) and reordering the disposables on the way out:
public class DisposableRequestResourcesReorderHandler : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
return base.SendAsync(request, cancellationToken).Finally(() => {
List<IDisposable> disposableResources = request.Properties[HttpPropertyKeys.DisposableRequestResourcesKey] as List<IDisposable>;
if (disposableResources != null && disposableResources.Count > 1) {
// 1-) Get the first one (which I know is AutofacWebApiDependencyScope).
// 2-) Remove it from the list.
// 3-) Push it at the end of the list.
IDisposable dependencyScope = disposableResources[0];
disposableResources.RemoveAt(0);
disposableResources.Add(dependencyScope);
}
}, runSynchronously: true);
}
}
Comments: Hi HongmeiG, Thanks for the response. I don't think you need to take a dependency on IoC container for this at all. The problem is related to fact that the Web API framework registers the IDependencyScope itself to be disposed by the host. If you have a look at the GetDependencyScope extension method, you will see the below code: public static IDependencyScope GetDependencyScope(this HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } IDependencyScope result; if (!request.Properties.TryGetValue<IDependencyScope>(HttpPropertyKeys.DependencyScope, out result)) { IDependencyResolver dependencyResolver = request.GetConfiguration().DependencyResolver; result = dependencyResolver.BeginScope(); if (result == null) { throw Error.InvalidOperation(SRResources.DependencyResolver_BeginScopeReturnsNull, dependencyResolver.GetType().Name); } request.Properties[HttpPropertyKeys.DependencyScope] = result; request.RegisterForDispose(result); } return result; } Then, the problem I explained inside [this post](http://www.tugberkugurlu.com/archive/asp-net-web-api-tracing-and-idependencyscope-dispose-issue) happens. This is a bug for sure IMO. The question is: what would be the best way to solve this.