### 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 Tugberk, If your tracer has dependency on autofacdespendencyscope, you need to dispose the autofacdependancyscope in the Dispose() method of your tracer, and add your tracer to request's dispose list. Web API runtime should not have dependency on autofac, and the reordering you suggested is not general enough to solve all the problem. For example, if your tracer depends on something else, we won't be able to reorder it correctly for you.
- 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 Tugberk, If your tracer has dependency on autofacdespendencyscope, you need to dispose the autofacdependancyscope in the Dispose() method of your tracer, and add your tracer to request's dispose list. Web API runtime should not have dependency on autofac, and the reordering you suggested is not general enough to solve all the problem. For example, if your tracer depends on something else, we won't be able to reorder it correctly for you.