### 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 looking into this issue. It depends how important the trace info being written inside the controller tracer's dispose method is. I mean if it's removed, it will break this trace info for the current implementations (the TraceWritters which don't use dependency scope are not affected for this issue). I prefer the first option but I'm really not sure in terms of performance, consistency, its side effects, etc.
- 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 looking into this issue. It depends how important the trace info being written inside the controller tracer's dispose method is. I mean if it's removed, it will break this trace info for the current implementations (the TraceWritters which don't use dependency scope are not affected for this issue). I prefer the first option but I'm really not sure in terms of performance, consistency, its side effects, etc.