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.










share|improve this question



























    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.










    share|improve this question

























      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.










      share|improve this question















      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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 9 at 13:53

























      asked Nov 9 at 9:51









      Martin Volek

      4041417




      4041417






















          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>();
          ...







          share|improve this answer





























            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






            share|improve this answer




















            • 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











            Your Answer






            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "1"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader:
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            ,
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );













            draft saved

            draft discarded


















            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

























            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>();
            ...







            share|improve this answer


























              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>();
              ...







              share|improve this answer
























                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>();
                ...







                share|improve this answer














                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>();
                ...








                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Nov 12 at 7:01

























                answered Nov 9 at 13:51









                Martin Volek

                4041417




                4041417






















                    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






                    share|improve this answer




















                    • 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















                    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






                    share|improve this answer




















                    • 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













                    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






                    share|improve this answer












                    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







                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Nov 9 at 12:21









                    Leonardo

                    4,25543387




                    4,25543387











                    • 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
















                    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


















                    draft saved

                    draft discarded
















































                    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.




                    draft saved


                    draft discarded














                    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





















































                    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







                    Popular posts from this blog

                    𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

                    ャフサォクコ ケウ,コ,ワ メ,ロスョノ゙,クネ,フムカヤヲニ,エコ゚ツ ウイオン゙ケワサネォキモュキォウイノンコチ゚メヌナイゥフュ,カヒウネェ ネ,ホノケ,ムュキ ッボーミュハ,チ ツス ィ メウイマヤ,゙ウチ ヅ ロ,ォジヌェ ャヌット ェ,マャ,チナエヒネソキツテ トホヲヲミーァ

                    How do I collapse sections of code in Visual Studio Code for Windows?