HttpClient DelegatingHandler unexpected life cycle
up vote
1
down vote
favorite
In ASP.NET Core 2.1 app I am making REST request using HttpClient. I am using DelegatingHandlers to define some common behavior. The registration i here:
private static void AddRestServiceDataClient<TTypedHttpClient, TTypedHttpClientImpl>(this IServiceCollection services)
where TTypedHttpClient : class
where TTypedHttpClientImpl : RestServiceDataClient, TTypedHttpClient
var httpClientBuilder = services
.AddHttpClient<TTypedHttpClient, TTypedHttpClientImpl>()
.AddHttpMessageHandler<CachingHttpDelegatingHandler>()
.AddHttpMessageHandler<ExceptionHttpDelegatingHandler>()
.AddHttpMessageHandler<LoggingHttpDelegatingHandler>();
...
// EDIT: You should always register DelegatingHandlers as TRANSIENT (read answer for more).
services.AddScoped<ExceptionHttpDelegatingHandler>();
services.AddScoped<CachingHttpDelegatingHandler>();
services.AddScoped<LoggingHttpDelegatingHandler>();
I register DelegatingHandlers as Scoped, but in two different scopes (requests) I get the same DelegatingHandler. I mean that the constructor of DelegationgHandler is being called only once and the same instance is used across more requests (like singleton). Everything else is as expected - life cycle of other services, TypedHttpClients and HttpClient is ok.
I tested everything by breakpoint in constructor and I have testing Guid in every instance so I can distinguish instances.
When I register DelegatingHandlers as Transient it makes no difference.
TL;DR DelegatingHandlers are resolved like singleton even though they are registered as scoped. And this causes me mishmash in service lifestyles.
.net asp.net-core dependency-injection .net-core dotnet-httpclient
add a comment |
up vote
1
down vote
favorite
In ASP.NET Core 2.1 app I am making REST request using HttpClient. I am using DelegatingHandlers to define some common behavior. The registration i here:
private static void AddRestServiceDataClient<TTypedHttpClient, TTypedHttpClientImpl>(this IServiceCollection services)
where TTypedHttpClient : class
where TTypedHttpClientImpl : RestServiceDataClient, TTypedHttpClient
var httpClientBuilder = services
.AddHttpClient<TTypedHttpClient, TTypedHttpClientImpl>()
.AddHttpMessageHandler<CachingHttpDelegatingHandler>()
.AddHttpMessageHandler<ExceptionHttpDelegatingHandler>()
.AddHttpMessageHandler<LoggingHttpDelegatingHandler>();
...
// EDIT: You should always register DelegatingHandlers as TRANSIENT (read answer for more).
services.AddScoped<ExceptionHttpDelegatingHandler>();
services.AddScoped<CachingHttpDelegatingHandler>();
services.AddScoped<LoggingHttpDelegatingHandler>();
I register DelegatingHandlers as Scoped, but in two different scopes (requests) I get the same DelegatingHandler. I mean that the constructor of DelegationgHandler is being called only once and the same instance is used across more requests (like singleton). Everything else is as expected - life cycle of other services, TypedHttpClients and HttpClient is ok.
I tested everything by breakpoint in constructor and I have testing Guid in every instance so I can distinguish instances.
When I register DelegatingHandlers as Transient it makes no difference.
TL;DR DelegatingHandlers are resolved like singleton even though they are registered as scoped. And this causes me mishmash in service lifestyles.
.net asp.net-core dependency-injection .net-core dotnet-httpclient
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
In ASP.NET Core 2.1 app I am making REST request using HttpClient. I am using DelegatingHandlers to define some common behavior. The registration i here:
private static void AddRestServiceDataClient<TTypedHttpClient, TTypedHttpClientImpl>(this IServiceCollection services)
where TTypedHttpClient : class
where TTypedHttpClientImpl : RestServiceDataClient, TTypedHttpClient
var httpClientBuilder = services
.AddHttpClient<TTypedHttpClient, TTypedHttpClientImpl>()
.AddHttpMessageHandler<CachingHttpDelegatingHandler>()
.AddHttpMessageHandler<ExceptionHttpDelegatingHandler>()
.AddHttpMessageHandler<LoggingHttpDelegatingHandler>();
...
// EDIT: You should always register DelegatingHandlers as TRANSIENT (read answer for more).
services.AddScoped<ExceptionHttpDelegatingHandler>();
services.AddScoped<CachingHttpDelegatingHandler>();
services.AddScoped<LoggingHttpDelegatingHandler>();
I register DelegatingHandlers as Scoped, but in two different scopes (requests) I get the same DelegatingHandler. I mean that the constructor of DelegationgHandler is being called only once and the same instance is used across more requests (like singleton). Everything else is as expected - life cycle of other services, TypedHttpClients and HttpClient is ok.
I tested everything by breakpoint in constructor and I have testing Guid in every instance so I can distinguish instances.
When I register DelegatingHandlers as Transient it makes no difference.
TL;DR DelegatingHandlers are resolved like singleton even though they are registered as scoped. And this causes me mishmash in service lifestyles.
.net asp.net-core dependency-injection .net-core dotnet-httpclient
In ASP.NET Core 2.1 app I am making REST request using HttpClient. I am using DelegatingHandlers to define some common behavior. The registration i here:
private static void AddRestServiceDataClient<TTypedHttpClient, TTypedHttpClientImpl>(this IServiceCollection services)
where TTypedHttpClient : class
where TTypedHttpClientImpl : RestServiceDataClient, TTypedHttpClient
var httpClientBuilder = services
.AddHttpClient<TTypedHttpClient, TTypedHttpClientImpl>()
.AddHttpMessageHandler<CachingHttpDelegatingHandler>()
.AddHttpMessageHandler<ExceptionHttpDelegatingHandler>()
.AddHttpMessageHandler<LoggingHttpDelegatingHandler>();
...
// EDIT: You should always register DelegatingHandlers as TRANSIENT (read answer for more).
services.AddScoped<ExceptionHttpDelegatingHandler>();
services.AddScoped<CachingHttpDelegatingHandler>();
services.AddScoped<LoggingHttpDelegatingHandler>();
I register DelegatingHandlers as Scoped, but in two different scopes (requests) I get the same DelegatingHandler. I mean that the constructor of DelegationgHandler is being called only once and the same instance is used across more requests (like singleton). Everything else is as expected - life cycle of other services, TypedHttpClients and HttpClient is ok.
I tested everything by breakpoint in constructor and I have testing Guid in every instance so I can distinguish instances.
When I register DelegatingHandlers as Transient it makes no difference.
TL;DR DelegatingHandlers are resolved like singleton even though they are registered as scoped. And this causes me mishmash in service lifestyles.
.net asp.net-core dependency-injection .net-core dotnet-httpclient
.net asp.net-core dependency-injection .net-core dotnet-httpclient
edited Nov 9 at 13:53
asked Nov 9 at 9:51
Martin Volek
4041417
4041417
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
up vote
2
down vote
accepted
After some investigation I found out that even though DelegatingHandlers are resolved by dependecy injection, the lifecycle of DelegatingHandlers is a bit unexpected and requires deeper knowledge of HttpClientFactory in .NET Core 2.1.
HttpClientFactory creates new HttpClient every time, but shares HttpMessageHandler across multiple HttpClients. More information about this you can find in Steve Gordon's article.
Because actual instances of DelegatingHandlers are held inside HttpMessageHandler (recursively in InnerHandler property) and HttpMessageHandler is shared, then DelegatingHandlers are shared the same way and have same lifecycle as the shared HttpMessageHandler.
Service provider is here used only for creating new DelegatingHandlers when HttpClientFactory "decides to" - thus every DelegatingHandler must be registered as transient! Otherwise you would get non-deterministic behavior. HttpClientFactory would try to reuse already used DelegatingHandler.
Workaround
If you need to resolve dependencies in DelegatingHandler you can resolve IHttpContextAccessor in constructor and then resolve dependencies by ServiceProvider in httpContextAccessor.HttpContext.RequestServices.
This approach is not exactly "architecturally clean" but it is the only workaround I have found.
Example:
internal class MyDelegatingHandler : DelegatingHandler
private readonly IHttpContextAccessor httpContextAccessor;
protected MyDelegatingHandler(IHttpContextAccessor httpContextAccessor)
this.httpContextAccessor = httpContextAccessor;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
var serviceProvider = this.httpContextAccessor.HttpContext.RequestServices;
var myService = serviceProvider.GetRequiredService<IMyService>();
...
add a comment |
up vote
-1
down vote
The official documentation states:
When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Don't inject via constructor injection because it forces the service to behave like a singleton. For more information, see ASP.NET Core Middleware.
I think that it's exactly your case
MyDelegatingHandlers have nothing to do with ASP.NET Core Middleware. TheDelegatingHandlers are "wrappers" aroundHttpClientthat I use for making REST requests on the server.
– Martin Volek
Nov 9 at 12:34
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
After some investigation I found out that even though DelegatingHandlers are resolved by dependecy injection, the lifecycle of DelegatingHandlers is a bit unexpected and requires deeper knowledge of HttpClientFactory in .NET Core 2.1.
HttpClientFactory creates new HttpClient every time, but shares HttpMessageHandler across multiple HttpClients. More information about this you can find in Steve Gordon's article.
Because actual instances of DelegatingHandlers are held inside HttpMessageHandler (recursively in InnerHandler property) and HttpMessageHandler is shared, then DelegatingHandlers are shared the same way and have same lifecycle as the shared HttpMessageHandler.
Service provider is here used only for creating new DelegatingHandlers when HttpClientFactory "decides to" - thus every DelegatingHandler must be registered as transient! Otherwise you would get non-deterministic behavior. HttpClientFactory would try to reuse already used DelegatingHandler.
Workaround
If you need to resolve dependencies in DelegatingHandler you can resolve IHttpContextAccessor in constructor and then resolve dependencies by ServiceProvider in httpContextAccessor.HttpContext.RequestServices.
This approach is not exactly "architecturally clean" but it is the only workaround I have found.
Example:
internal class MyDelegatingHandler : DelegatingHandler
private readonly IHttpContextAccessor httpContextAccessor;
protected MyDelegatingHandler(IHttpContextAccessor httpContextAccessor)
this.httpContextAccessor = httpContextAccessor;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
var serviceProvider = this.httpContextAccessor.HttpContext.RequestServices;
var myService = serviceProvider.GetRequiredService<IMyService>();
...
add a comment |
up vote
2
down vote
accepted
After some investigation I found out that even though DelegatingHandlers are resolved by dependecy injection, the lifecycle of DelegatingHandlers is a bit unexpected and requires deeper knowledge of HttpClientFactory in .NET Core 2.1.
HttpClientFactory creates new HttpClient every time, but shares HttpMessageHandler across multiple HttpClients. More information about this you can find in Steve Gordon's article.
Because actual instances of DelegatingHandlers are held inside HttpMessageHandler (recursively in InnerHandler property) and HttpMessageHandler is shared, then DelegatingHandlers are shared the same way and have same lifecycle as the shared HttpMessageHandler.
Service provider is here used only for creating new DelegatingHandlers when HttpClientFactory "decides to" - thus every DelegatingHandler must be registered as transient! Otherwise you would get non-deterministic behavior. HttpClientFactory would try to reuse already used DelegatingHandler.
Workaround
If you need to resolve dependencies in DelegatingHandler you can resolve IHttpContextAccessor in constructor and then resolve dependencies by ServiceProvider in httpContextAccessor.HttpContext.RequestServices.
This approach is not exactly "architecturally clean" but it is the only workaround I have found.
Example:
internal class MyDelegatingHandler : DelegatingHandler
private readonly IHttpContextAccessor httpContextAccessor;
protected MyDelegatingHandler(IHttpContextAccessor httpContextAccessor)
this.httpContextAccessor = httpContextAccessor;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
var serviceProvider = this.httpContextAccessor.HttpContext.RequestServices;
var myService = serviceProvider.GetRequiredService<IMyService>();
...
add a comment |
up vote
2
down vote
accepted
up vote
2
down vote
accepted
After some investigation I found out that even though DelegatingHandlers are resolved by dependecy injection, the lifecycle of DelegatingHandlers is a bit unexpected and requires deeper knowledge of HttpClientFactory in .NET Core 2.1.
HttpClientFactory creates new HttpClient every time, but shares HttpMessageHandler across multiple HttpClients. More information about this you can find in Steve Gordon's article.
Because actual instances of DelegatingHandlers are held inside HttpMessageHandler (recursively in InnerHandler property) and HttpMessageHandler is shared, then DelegatingHandlers are shared the same way and have same lifecycle as the shared HttpMessageHandler.
Service provider is here used only for creating new DelegatingHandlers when HttpClientFactory "decides to" - thus every DelegatingHandler must be registered as transient! Otherwise you would get non-deterministic behavior. HttpClientFactory would try to reuse already used DelegatingHandler.
Workaround
If you need to resolve dependencies in DelegatingHandler you can resolve IHttpContextAccessor in constructor and then resolve dependencies by ServiceProvider in httpContextAccessor.HttpContext.RequestServices.
This approach is not exactly "architecturally clean" but it is the only workaround I have found.
Example:
internal class MyDelegatingHandler : DelegatingHandler
private readonly IHttpContextAccessor httpContextAccessor;
protected MyDelegatingHandler(IHttpContextAccessor httpContextAccessor)
this.httpContextAccessor = httpContextAccessor;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
var serviceProvider = this.httpContextAccessor.HttpContext.RequestServices;
var myService = serviceProvider.GetRequiredService<IMyService>();
...
After some investigation I found out that even though DelegatingHandlers are resolved by dependecy injection, the lifecycle of DelegatingHandlers is a bit unexpected and requires deeper knowledge of HttpClientFactory in .NET Core 2.1.
HttpClientFactory creates new HttpClient every time, but shares HttpMessageHandler across multiple HttpClients. More information about this you can find in Steve Gordon's article.
Because actual instances of DelegatingHandlers are held inside HttpMessageHandler (recursively in InnerHandler property) and HttpMessageHandler is shared, then DelegatingHandlers are shared the same way and have same lifecycle as the shared HttpMessageHandler.
Service provider is here used only for creating new DelegatingHandlers when HttpClientFactory "decides to" - thus every DelegatingHandler must be registered as transient! Otherwise you would get non-deterministic behavior. HttpClientFactory would try to reuse already used DelegatingHandler.
Workaround
If you need to resolve dependencies in DelegatingHandler you can resolve IHttpContextAccessor in constructor and then resolve dependencies by ServiceProvider in httpContextAccessor.HttpContext.RequestServices.
This approach is not exactly "architecturally clean" but it is the only workaround I have found.
Example:
internal class MyDelegatingHandler : DelegatingHandler
private readonly IHttpContextAccessor httpContextAccessor;
protected MyDelegatingHandler(IHttpContextAccessor httpContextAccessor)
this.httpContextAccessor = httpContextAccessor;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
var serviceProvider = this.httpContextAccessor.HttpContext.RequestServices;
var myService = serviceProvider.GetRequiredService<IMyService>();
...
edited Nov 12 at 7:01
answered Nov 9 at 13:51
Martin Volek
4041417
4041417
add a comment |
add a comment |
up vote
-1
down vote
The official documentation states:
When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Don't inject via constructor injection because it forces the service to behave like a singleton. For more information, see ASP.NET Core Middleware.
I think that it's exactly your case
MyDelegatingHandlers have nothing to do with ASP.NET Core Middleware. TheDelegatingHandlers are "wrappers" aroundHttpClientthat I use for making REST requests on the server.
– Martin Volek
Nov 9 at 12:34
add a comment |
up vote
-1
down vote
The official documentation states:
When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Don't inject via constructor injection because it forces the service to behave like a singleton. For more information, see ASP.NET Core Middleware.
I think that it's exactly your case
MyDelegatingHandlers have nothing to do with ASP.NET Core Middleware. TheDelegatingHandlers are "wrappers" aroundHttpClientthat I use for making REST requests on the server.
– Martin Volek
Nov 9 at 12:34
add a comment |
up vote
-1
down vote
up vote
-1
down vote
The official documentation states:
When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Don't inject via constructor injection because it forces the service to behave like a singleton. For more information, see ASP.NET Core Middleware.
I think that it's exactly your case
The official documentation states:
When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Don't inject via constructor injection because it forces the service to behave like a singleton. For more information, see ASP.NET Core Middleware.
I think that it's exactly your case
answered Nov 9 at 12:21
Leonardo
4,25543387
4,25543387
MyDelegatingHandlers have nothing to do with ASP.NET Core Middleware. TheDelegatingHandlers are "wrappers" aroundHttpClientthat I use for making REST requests on the server.
– Martin Volek
Nov 9 at 12:34
add a comment |
MyDelegatingHandlers have nothing to do with ASP.NET Core Middleware. TheDelegatingHandlers are "wrappers" aroundHttpClientthat I use for making REST requests on the server.
– Martin Volek
Nov 9 at 12:34
My
DelegatingHandlers have nothing to do with ASP.NET Core Middleware. The DelegatingHandlers are "wrappers" around HttpClient that I use for making REST requests on the server.– Martin Volek
Nov 9 at 12:34
My
DelegatingHandlers have nothing to do with ASP.NET Core Middleware. The DelegatingHandlers are "wrappers" around HttpClient that I use for making REST requests on the server.– Martin Volek
Nov 9 at 12:34
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53223411%2fhttpclient-delegatinghandler-unexpected-life-cycle%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown