<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bojan Nikolić</title>
    <description>The latest articles on DEV Community by Bojan Nikolić (@nikolicbojan).</description>
    <link>https://dev.to/nikolicbojan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F213741%2Fc25ea7a6-d173-4b9d-8c9d-0039597cc1e5.jpg</url>
      <title>DEV Community: Bojan Nikolić</title>
      <link>https://dev.to/nikolicbojan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikolicbojan"/>
    <language>en</language>
    <item>
      <title>Your "Clean Architecture" is still layered!</title>
      <dc:creator>Bojan Nikolić</dc:creator>
      <pubDate>Fri, 04 Dec 2020 13:52:09 +0000</pubDate>
      <link>https://dev.to/nikolicbojan/your-clean-architecture-is-still-layered-3god</link>
      <guid>https://dev.to/nikolicbojan/your-clean-architecture-is-still-layered-3god</guid>
      <description>&lt;p&gt;Often developers, when talking about architecture, actually think about their project structure. Clean, Ports &amp;amp; Adapters, Onion... all of these are referring on how to organize your project. You either "inherit" it from your team or try some that makes more sense to you on new project or when refactoring. Thing is that project organization is important when you have a monolith or some coarse grained services that contain several functionalities that are somewhat related, but you want to keep them separate on code level and not to jump into making a (micro)service for each.&lt;/p&gt;

&lt;p&gt;You usually end up with a solution that has several projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API - just for controllers and to expose business logic&lt;/li&gt;
&lt;li&gt;Domain/Core - with business logic&lt;/li&gt;
&lt;li&gt;Infrastructure - interface implementation&lt;/li&gt;
&lt;li&gt;Persistence? - some folks like to keep DB stuff separated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So what is wrong with this structure? Listen to this talk by Simon Brown and continue reading to grab some code samples.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/5OjqD-ow8GE"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;
&lt;h1&gt;
  
  
  Your code doesn't match your diagram
&lt;/h1&gt;

&lt;p&gt;Let's start by having an API that has two functionalities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Weather Forecast&lt;/li&gt;
&lt;li&gt;Calculator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How would you draw a component diagram? Something like this, right?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2co4nbwuw8iq1fn4c6vo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2co4nbwuw8iq1fn4c6vo.png" alt="Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How would you structure your code? I guess something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API (project)&lt;/li&gt;
&lt;li&gt;Domain (project)

&lt;ul&gt;
&lt;li&gt;WeatherForecast (folder)&lt;/li&gt;
&lt;li&gt;Calculator (folder)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Infrastructure

&lt;ul&gt;
&lt;li&gt;WeatherForecast (folder)&lt;/li&gt;
&lt;li&gt;Calculator (folder)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is an obvious mismatch, right? You might say - that doesn't matter, we have a nice folder structure so we keep things separated and well organized.&lt;/p&gt;

&lt;p&gt;OK, but what is stopping something from &lt;code&gt;WeatherForecast&lt;/code&gt; folder to call something else from &lt;code&gt;Calculator&lt;/code&gt; folder? Internal conventions and rules? Pull requests? Yes, all of that might help, but it would be more obvious if in that pull request you would see that unwanted relation trying to creeping in.&lt;/p&gt;
&lt;h1&gt;
  
  
  Then how to structure?
&lt;/h1&gt;

&lt;p&gt;A suggestion is that it matches you component diagram, like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API (project)&lt;/li&gt;
&lt;li&gt;WeatherForecast (project)

&lt;ul&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Calculator (project)

&lt;ul&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I deliberately put three dots, since it is up to developer of the specific functionality how to organize code inside of it. Should you have Domain/Infrastructure/whatever folder structure in each project? Or you will have some convention that if functionality is simple and you have just few classes, you do not even want to have any sub-folder? Whatever your teams' convention is, that is fine.&lt;/p&gt;

&lt;p&gt;Some obvious advantages are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solution structure matches diagram, so it is obvious where the code is.&lt;/li&gt;
&lt;li&gt;All code related to one functionality is in one project, making it much harder to make some dangerous dependencies between different functionalities.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Encapsulate your component
&lt;/h1&gt;

&lt;p&gt;We all know what is encapsulation, but we often think of it just in the terms of class encapsulation. Simon Brown suggested we do the encapsulation on component level too. If you think of your REST API, it is encapsulated as a consumer can only call exposed methods in a way you specified. Why not do the same for your component?&lt;/p&gt;

&lt;p&gt;You might say you already do that by having interfaces that are then injected, so a caller can't access the implementation, but that is more often not true. Usually we have that API/Core/Implementation project structure and all our classes are &lt;code&gt;public&lt;/code&gt;. We need them to be &lt;code&gt;public&lt;/code&gt; as DI setup is usually in API project and they need to be visible.&lt;/p&gt;

&lt;p&gt;With the new project-per-component structure, we do not have that problem any more. Implementation classes can be &lt;code&gt;private&lt;/code&gt; or &lt;code&gt;internal&lt;/code&gt; and therefore hidden from other components. Yes, you would have your DI configuration inside the component!&lt;/p&gt;
&lt;h1&gt;
  
  
  Sample solution
&lt;/h1&gt;

&lt;p&gt;Grab a look at the sample solution on GutHub &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/nikolic-bojan" rel="noopener noreferrer"&gt;
        nikolic-bojan
      &lt;/a&gt; / &lt;a href="https://github.com/nikolic-bojan/modularity" rel="noopener noreferrer"&gt;
        modularity
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Sample solution showing how to organize code in Components/Modules, so we completely avoid layered approach.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Your "Clean Architecture" is still layered!&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Often developers, when talking about architecture, actually think about their project structure. Hexagonal, Clean, Ports &amp;amp; Adapters, Onion... all of these are referring on how to organize your project. You either "inherit" it from your team or try some that makes more sense to you on new project or when refactoring. Thing is that project organization is important when you have a monolith or some coarse grained services that contain several functionalities that are somewhat related, but you want to keep them separate on code level and not to jump into making a (micro)service for each.&lt;/p&gt;
&lt;p&gt;You usually end up with a solution that has several projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API - just for controllers and to expose business logic&lt;/li&gt;
&lt;li&gt;Domain/Core - with business logic&lt;/li&gt;
&lt;li&gt;Infrastructure - interface implementation&lt;/li&gt;
&lt;li&gt;Persistence? - some folks like to keep DB stuff separated&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So what is wrong with this structure? Listen…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/nikolic-bojan/modularity" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Structure of my project is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API (project)&lt;/li&gt;
&lt;li&gt;Components (solution folder to group components)

&lt;ul&gt;
&lt;li&gt;WeatherForecast (project)&lt;/li&gt;
&lt;li&gt;Calculator (project)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Things that are exposed from component are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interfaces&lt;/li&gt;
&lt;li&gt;DTOs needed for interfaces&lt;/li&gt;
&lt;li&gt;Dependency Injection configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In API's &lt;code&gt;Startup.cs&lt;/code&gt; you just add calls to extension methods in order to configure DI like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddComponentWeatherForecast&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddComponentCalculator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a sample of one extension method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ServiceCollectionExtensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="nf"&gt;AddComponentWeatherForecast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IWeatherForecastService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WeatherForecastService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Final words
&lt;/h1&gt;

&lt;p&gt;I hope you like these encapsulated components. They are really making hard for team members to make "shortcuts". If components need to talk to each other, that is totally fine - only thing you can access on another component are interfaces and DTOs. If you want to make a full-blown service out of your component, that would be super-easy as everything is in one place.&lt;/p&gt;

&lt;p&gt;Would like to hear your comments and questions.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Handle errors with your convention in ASP.NET Core</title>
      <dc:creator>Bojan Nikolić</dc:creator>
      <pubDate>Sat, 15 Aug 2020 22:44:58 +0000</pubDate>
      <link>https://dev.to/nikolicbojan/handle-errors-with-your-convention-in-asp-net-core-6c1</link>
      <guid>https://dev.to/nikolicbojan/handle-errors-with-your-convention-in-asp-net-core-6c1</guid>
      <description>&lt;p&gt;This post is a natural continuation of some of my previous ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/nikolicbojan/make-soap-requests-using-ihttpclientfactory-in-net-core-46hk"&gt;Make SOAP requests using IHttpClientFactory in .NET Core&lt;/a&gt; and&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/nikolicbojan/log-httpclient-request-and-response-based-on-custom-conditions-in-net-core-412f"&gt;Log HttpClient request and response based on custom conditions in .NET Core&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All work is done in the same GitHub repository &lt;a href="https://github.com/nikolic-bojan/soap-client"&gt;https://github.com/nikolic-bojan/soap-client&lt;/a&gt; so you can see the code.&lt;/p&gt;

&lt;p&gt;Here, we are dealing with handling errors in API project and also with logging some additional information that are valuable both for error analysis and determining how your service behaves.&lt;/p&gt;

&lt;h1&gt;
  
  
  Error handling
&lt;/h1&gt;

&lt;p&gt;There is a good explanation on how to do error handling in Microsoft documentation - &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-3.1#exception-handler-lambda"&gt;https://docs.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-3.1#exception-handler-lambda&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What I would like to point out is that if you want to have a good API(s), you should make their responses clear, especially error responses. Those responses are often not read by humans, but by someone else's code and they should be clear on what to do when error occurs.&lt;/p&gt;

&lt;p&gt;There is a well established &lt;a href="https://tools.ietf.org/html/rfc7807"&gt;standard for error responses&lt;/a&gt; implemented also in .NET as &lt;code&gt;ProblemDetails&lt;/code&gt; and &lt;code&gt;ValidationProblemDetails&lt;/code&gt; classes. It is also built into the &lt;code&gt;ControllerBase&lt;/code&gt; since Core 2.1.&lt;/p&gt;

&lt;p&gt;It is not a problem to use these, it is more of an issue what to put into their properties and based on what. In this sample, I tried to make things simple, but you might want to make them more complex for your case. My advice - do not go too complex as someone needs to implement their code based on them.&lt;/p&gt;

&lt;p&gt;In order to handle errors, I added the following line in Startup.cs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errorApp&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;errorApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateExceptionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Important stuff is in that method, which will handle exceptions with a few steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We need to get the exception via &lt;code&gt;IExceptionHandlerFeature&lt;/code&gt; and create some starting &lt;code&gt;ProblemDetails&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We then &lt;code&gt;switch&lt;/code&gt; by exception type and change some &lt;code&gt;ProblemDetails&lt;/code&gt; properties and at the same time add some log scope data.&lt;/li&gt;
&lt;li&gt;Finally, we write &lt;code&gt;ProblemDetails&lt;/code&gt; directly to Response Body stream.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In order not to c/p entire method and bloat the post, please check the code here &lt;a href="https://github.com/nikolic-bojan/soap-client/blob/master/Api/Handlers/ExceptionHandler.cs"&gt;https://github.com/nikolic-bojan/soap-client/blob/master/Api/Handlers/ExceptionHandler.cs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What you will notice that I have only 3 outcomes, all based on exception type:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Validation exception - we return &lt;code&gt;ValidationProblemDetails&lt;/code&gt; and log errors (maybe it could be LogWarning)&lt;/li&gt;
&lt;li&gt;Service exception - any exception that happens in Service class (adapter to 3rd party)&lt;/li&gt;
&lt;li&gt;Any other not handled exception&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Some advises on property values
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create few (fewer is better) exception types that will define some standard exceptional situations.&lt;/li&gt;
&lt;li&gt;Return 400 for Validation errors (thanks Mr. Obvious!)&lt;/li&gt;
&lt;li&gt;Return 404 from your controller (NotFound) if you want to inform there is no resource.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;app.UseStatusCodePages()&lt;/code&gt;, so e.g. wrong URL will not produce same error as when resource is not found.&lt;/li&gt;
&lt;li&gt;All exceptions that bubble up in your code should be returned as 500.

&lt;ul&gt;
&lt;li&gt;Do not try to be "smart" and correlate exceptions to status codes (we made that mistake and status codes became misleading).&lt;/li&gt;
&lt;li&gt;Correlate exception types to &lt;code&gt;type&lt;/code&gt; property in response either by exception type name or (better) by having URL to your documentation that will explain what that error type is all about.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way the consumer of your service will know if they should retry the call, was that a temporary issue, something to alarm on immediately (you should also do that by logging Critical error, for example); in general - how to behave.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample responses
&lt;/h2&gt;

&lt;p&gt;Here is how &lt;code&gt;BadRequest&lt;/code&gt; looks like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ValidationException"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"One or more validation errors occurred."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"traceId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"92d14dddb407d945909d970efdc03988"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"FirstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"First name must not be 400"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is some error calling 3rd party system (SoapUI mock was down)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ServiceException"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The content type text/html; charset=iso-8859-1 of the response message does not match the content type of the binding (text/xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 95 bytes of the response were: '&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;p&amp;gt;There are currently 0 running SoapUI MockServices&amp;lt;/p&amp;gt;&amp;lt;ul&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;'."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"traceId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"145be362dd1e7c42b84c4a566ae0ed24"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Log more information for diagnostics and alarming
&lt;/h1&gt;

&lt;p&gt;This is kind of separate subject, but in my head exceptional situations and logging additional information that can help identifying the pattern of the issue or can be used for diagnostics and alarming, goes in the same package.&lt;/p&gt;

&lt;p&gt;This logging is per request and contains all kind of request related data. You can write this code by yourself or use some add-on from your logging library, like Serilog has &lt;code&gt;RequestLoggingMiddleware&lt;/code&gt;. In the sample I use clean Microsoft Logger, so I had to write it by myself. Code is available here &lt;a href="https://github.com/nikolic-bojan/soap-client/blob/master/Api/Handlers/TraceContextMiddleware.cs"&gt;https://github.com/nikolic-bojan/soap-client/blob/master/Api/Handlers/TraceContextMiddleware.cs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On each request we &lt;code&gt;LogInformation&lt;/code&gt; on that request. Base on those data we can create some dashboard widgets that would show us the statistics on HTTP status code per Controller and Action. We could also setup some alarms based on certain thresholds, e.g. "if there are more than X error logs during Y minutes then..."&lt;/p&gt;

&lt;h1&gt;
  
  
  Feedback
&lt;/h1&gt;

&lt;p&gt;This post feels a bit "rough" and most likely opinionated (isn't everything?)&lt;/p&gt;

&lt;p&gt;I would really like to get a feedback on this subject as I do not think this is something you can call best-practice and all will agree with the approach.&lt;/p&gt;

&lt;p&gt;What are your thoughts? How do you handle error handling?&lt;/p&gt;

&lt;p&gt;BR,&lt;br&gt;
Bojan&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>exceptions</category>
      <category>logging</category>
    </item>
    <item>
      <title>Log HttpClient request and response based on custom conditions in .NET Core</title>
      <dc:creator>Bojan Nikolić</dc:creator>
      <pubDate>Thu, 13 Aug 2020 20:13:31 +0000</pubDate>
      <link>https://dev.to/nikolicbojan/log-httpclient-request-and-response-based-on-custom-conditions-in-net-core-412f</link>
      <guid>https://dev.to/nikolicbojan/log-httpclient-request-and-response-based-on-custom-conditions-in-net-core-412f</guid>
      <description>&lt;p&gt;In my team, we created a lot of code for calling some 3rd party services. From time to time we needed to see what how did the actual HTTP request and response looked like. Usually when something goes "south" from various reasons.&lt;/p&gt;

&lt;p&gt;It is not a hard task to log each request and response, but if you take into consideration performance, log size, logging of sensitive data, etc. you might not want to do that for each request.&lt;/p&gt;

&lt;p&gt;We knew when we would like to log:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If there was some error while communicating to 3rd party service, so we can have request/response for their support (or for our reproduction of the issue)&lt;/li&gt;
&lt;li&gt;We explicitly wanted to send some parameter to capture request/response, for various reasons, even though everything went fine (or looked like it did)&lt;/li&gt;
&lt;li&gt;Based on some response HTTP code or something else from the response, but that was different from service to service.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What we also knew is that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We would like to have it reusable&lt;/li&gt;
&lt;li&gt;It should not make our Service class (the one that implements adapter to 3rd party service) bloated and&lt;/li&gt;
&lt;li&gt;We should be able to add it as any other Delegating Handler through HttpClient setup&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Delegating Handler
&lt;/h2&gt;

&lt;p&gt;We created a delegating handler with two parameters in constructor&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TraceLogHandler&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DelegatingHandler&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;TraceLogHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IHttpContextAccessor&lt;/span&gt; &lt;span class="n"&gt;httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;shouldLog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;First we need &lt;code&gt;IHttpContextAccessor&lt;/code&gt; to allow us to acces current HttpContext. We need it for two purposes - to access to query parameters and check if "traceme" was sent.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;traceMe&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ContainsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"traceme"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That is a signal for us we should log, no matter what. It is also good to add that value through some middleware in all log messages, but I skip that part for this sample.&lt;/p&gt;

&lt;p&gt;Second one is for purpose of creating a logger as HttpContext also allow us access to &lt;code&gt;IServiceProvider&lt;/code&gt; (the &lt;code&gt;RequestServices&lt;/code&gt; property).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestServices&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TraceLogHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Second constructor parameter is a &lt;code&gt;Func&lt;/code&gt; that accepts HttpResponseMessage (response from 3rd party service) and should return &lt;code&gt;bool&lt;/code&gt; value. Remember I said we wanted some dynamic decision based on response? Well, this is it. To give you an example how it could look, let's go to the &lt;code&gt;Startup.cs&lt;/code&gt; where I setup &lt;code&gt;HttpClient&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HelloService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Some custom configuration like request timeout&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTraceLogHandler&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Here we setup that if Response status code is not 200-299, &lt;/span&gt;
  &lt;span class="c1"&gt;// we should log entire HttpClient request and response &lt;/span&gt;
  &lt;span class="c1"&gt;// to 3rd party service.&lt;/span&gt;
  &lt;span class="c1"&gt;// You can setup any condition based on the HttpResponseMessage.&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As explained in a comment, you can setup any condition based on &lt;code&gt;HttpResponseMessage&lt;/code&gt;. If your function returns &lt;code&gt;true&lt;/code&gt;, delegating handler will log request and response.&lt;/p&gt;

&lt;p&gt;We needed this to be variable per &lt;code&gt;HttpClient&lt;/code&gt;, since different 3rd party services behave differently - we have one that throws HTTP status 500 when record is not found, other one returns for the same situation status 200 with some text message... don't ask... &lt;/p&gt;

&lt;p&gt;&lt;code&gt;AddTraceLogHandler&lt;/code&gt; is an extension method so we can add handler easier. Here are two extension methods as an example.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HttpClientBuilderExtensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IHttpClientBuilder&lt;/span&gt; &lt;span class="nf"&gt;AddTraceLogHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IHttpClientBuilder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;shouldLog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpMessageHandler&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; 
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TraceLogHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IHttpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(),&lt;/span&gt; &lt;span class="n"&gt;shouldLog&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IHttpClientBuilder&lt;/span&gt; &lt;span class="nf"&gt;AddTraceLogHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IHttpClientBuilder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpMessageHandler&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; 
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TraceLogHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IHttpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(),&lt;/span&gt; 
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Show us the code!
&lt;/h2&gt;

&lt;p&gt;Right, almost forgot - code of the handler itself. You can find it here on the GitHub &lt;a href="https://github.com/nikolic-bojan/soap-client/blob/master/Api/Handlers/TraceLogHandler.cs" rel="noopener noreferrer"&gt;https://github.com/nikolic-bojan/soap-client/blob/master/Api/Handlers/TraceLogHandler.cs&lt;/a&gt; as a part of my &lt;code&gt;SoapClient&lt;/code&gt; repository.&lt;/p&gt;

&lt;p&gt;There I use it for calling some &lt;code&gt;SOAP&lt;/code&gt; 3rd party service with some "magic" for using &lt;code&gt;HttpClient&lt;/code&gt; for calling &lt;code&gt;SOAP&lt;/code&gt;, but that doesn't matter, that is just a regular delegating handler, so you can use it for any &lt;code&gt;HttpClient&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TraceLogHandler&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DelegatingHandler&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IHttpContextAccessor&lt;/span&gt; &lt;span class="n"&gt;_httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_shouldLog&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;TraceLogHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IHttpContextAccessor&lt;/span&gt; &lt;span class="n"&gt;httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;shouldLog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_httpContextAccessor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_shouldLog&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shouldLog&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpRequestMessage&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;logPayloads&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// If you pass a query string parameter "traceme", HttpClient request/response will be logged.&lt;/span&gt;
        &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;traceMe&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ContainsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"traceme"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;logPayloads&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logPayloads&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;traceMe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// We run the ShouldLog function that calculates, based on HttpResponseMessage, if we should log HttpClient request/response.&lt;/span&gt;
            &lt;span class="n"&gt;logPayloads&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logPayloads&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;_shouldLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// We want to log HttpClient request/response when some exception occurs, so we can reproduce what caused it.&lt;/span&gt;
            &lt;span class="n"&gt;logPayloads&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;finally&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Finally, we check if we decided to log HttpClient request/response or not.&lt;/span&gt;
            &lt;span class="c1"&gt;// Only if we want to, we will have some allocations for the logger and try to read headers and contents.&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logPayloads&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TraceLogHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
                &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

                &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"request_headers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"request_body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"response_headers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"response_body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[TRACE] request/response"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To explain briefly - if either of the conditions for logging was true (we have "traceme" query string parameter, there was some exception, function based on HTTP response returned &lt;code&gt;true&lt;/code&gt;), we will create logger, add request and response headers and content to log scope and log that on &lt;code&gt;information&lt;/code&gt; level.&lt;/p&gt;

&lt;p&gt;This is how that look in &lt;code&gt;Seq&lt;/code&gt;. Looks good to me!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvzixq74vxgjet19rjiht.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvzixq74vxgjet19rjiht.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check out the &lt;code&gt;SoapClient&lt;/code&gt; repository for full code &lt;a href="https://github.com/nikolic-bojan/soap-client" rel="noopener noreferrer"&gt;https://github.com/nikolic-bojan/soap-client&lt;/a&gt; and copy the parts you need to see it working in your project.&lt;/p&gt;

&lt;p&gt;Hope to see some comments and questions!&lt;/p&gt;

&lt;p&gt;BR,&lt;br&gt;
Bojan&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>httpclient</category>
      <category>logging</category>
    </item>
    <item>
      <title>Make SOAP requests using IHttpClientFactory in .NET Core</title>
      <dc:creator>Bojan Nikolić</dc:creator>
      <pubDate>Mon, 03 Aug 2020 14:29:38 +0000</pubDate>
      <link>https://dev.to/nikolicbojan/make-soap-requests-using-ihttpclientfactory-in-net-core-46hk</link>
      <guid>https://dev.to/nikolicbojan/make-soap-requests-using-ihttpclientfactory-in-net-core-46hk</guid>
      <description>&lt;p&gt;SOAP services became second class citizen in .NET Core. REST was "the-way-to-go" and who creates SOAP services these days anyways? Well, maybe not these days, but a lot of them were created back in the days when SOAP was the King. Some of them are still alive and you need to access them. &lt;/p&gt;

&lt;p&gt;Sure, accessing is not a big deal, VS still supports this, but if you worked with &lt;code&gt;IHttpClientFactory&lt;/code&gt; when calling REST services and liked it, you would want to have all that while working with SOAP.&lt;/p&gt;

&lt;p&gt;Is this possible?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TL;DR;&lt;br&gt;
Yes it is! You can find sample application here on my GitHub repository &lt;a href="https://github.com/nikolic-bojan/soap-client" rel="noopener noreferrer"&gt;https://github.com/nikolic-bojan/soap-client&lt;/a&gt; Browse through the code to figure what I did or just continue reading this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Problem and digging out the solution
&lt;/h1&gt;

&lt;p&gt;Around a year and a half ago we moved our first service from "old" Framework to .NET Core. Later that year I started looking for a way to move to .NET Core some services that call SOAP 3rd party services. I wasn't satisfied with the basic stuff Core offers and I wanted something more similar to the options we have with IHttpClientFactory. Actually, I wanted the same experience!&lt;/p&gt;

&lt;p&gt;I started looking for a solution. There were some custom libraries, but that was out of the question. They also felt hacky. I played around writing &lt;code&gt;ISoapClientFactory&lt;/code&gt; as a copy of HTTP one, but only to realize I do not want to maintain something like that. At last, I postponed this for later.&lt;/p&gt;

&lt;p&gt;Few months back, I gave it another go. This time I ran into GitHub issue &lt;a href="https://github.com/dotnet/wcf/issues/3230" rel="noopener noreferrer"&gt;https://github.com/dotnet/wcf/issues/3230&lt;/a&gt; that pointed me to both this blog post &lt;a href="https://medium.com/trueengineering/realization-of-the-connections-pool-with-wcf-for-net-core-with-usage-of-httpclientfactory-c2cb2676423e" rel="noopener noreferrer"&gt;https://medium.com/trueengineering/realization-of-the-connections-pool-with-wcf-for-net-core-with-usage-of-httpclientfactory-c2cb2676423e&lt;/a&gt; and this PR added to Core &lt;a href="https://github.com/dotnet/wcf/pull/2534/files" rel="noopener noreferrer"&gt;https://github.com/dotnet/wcf/pull/2534/files&lt;/a&gt; by a demigod of WCF - Matt Connew. Big &lt;em&gt;thank you&lt;/em&gt; for these people!&lt;/p&gt;

&lt;p&gt;So, is this article more/less a re-chewing of the GitHub Issue, PR and the blog post? Yes, you can say that, but I will explain one or two other walls I hit with this, so it is up to you where you will continue reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add SOAP service
&lt;/h2&gt;

&lt;p&gt;Adding existing SOAP/WCF service should not be a big issue. You should just follow the official documentation here &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/additional-tools/wcf-web-service-reference-guide" rel="noopener noreferrer"&gt;https://docs.microsoft.com/en-us/dotnet/core/additional-tools/wcf-web-service-reference-guide&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveat #1
&lt;/h2&gt;

&lt;p&gt;What I encountered is that I couldn't do it. I kept getting the error that tool can't work on my .NET Core 3.x (I removed all previous versions) and requires v2.1. In order not to be blocked, I managed to find and install v2.1. Runtime installation should do the trick (I am not 100% sure, it was 2 months ago, do not hunt me down if runtime doesn't work and you need SDK). Visual Studio was now creating proxy as it should. Great!&lt;/p&gt;

&lt;h2&gt;
  
  
  How to make SOAP client work with IHttpClientFactory
&lt;/h2&gt;

&lt;p&gt;When I went through those GitHub issue and PR, it is quite simple to answer - you need to add new Endpoint Behavior that will basically replace the default &lt;code&gt;HttpClientHandler&lt;/code&gt; with the &lt;code&gt;HttpMessageHandler&lt;/code&gt; that will be created by our beloved &lt;code&gt;IHttpMessageHandlerFactory&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;OK, how do I do that? First, you will need a new class that will implement &lt;code&gt;IEndpointBehavior&lt;/code&gt; and will look like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HttpMessageHandlerBehavior&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEndpointBehavior&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpMessageHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_httpMessageHandler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;HttpMessageHandlerBehavior&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IHttpMessageHandlerFactory&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Here we prescribe how handler will be created.&lt;/span&gt;
        &lt;span class="c1"&gt;// Since it uses IHttpMessageHandlerFactory, this factory will manage the setup and lifetime of the handler, &lt;/span&gt;
        &lt;span class="c1"&gt;// based on the configuration we provided with AddHttpClient(serviceName) &lt;/span&gt;
        &lt;span class="n"&gt;_httpMessageHandler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AddBindingParameters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceEndpoint&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BindingParameterCollection&lt;/span&gt; &lt;span class="n"&gt;bindingParameters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// We need this line to add our HttpMessageHandler as HttpClientHandler.&lt;/span&gt;
        &lt;span class="n"&gt;bindingParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpClientHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpMessageHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;_httpMessageHandler&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ApplyClientBehavior&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceEndpoint&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ClientRuntime&lt;/span&gt; &lt;span class="n"&gt;clientRuntime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ApplyDispatchBehavior&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceEndpoint&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EndpointDispatcher&lt;/span&gt; &lt;span class="n"&gt;endpointDispatcher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceEndpoint&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;        

&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There you have all the "magic" where constructor accepts &lt;code&gt;IHttpMessageHandlerFactory&lt;/code&gt; and defines function for creating a new handler; plus the part where we define how newly created &lt;code&gt;HttpMessageHandler&lt;/code&gt; should be used as &lt;code&gt;HttpClientHandler&lt;/code&gt; when calling SOAP service.&lt;/p&gt;

&lt;p&gt;Of course, I had to add this EndpointBehavior in constructor of my &lt;code&gt;HelloService&lt;/code&gt; singleton that injects &lt;code&gt;IHttpMessageHandlerFactory&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;HelloService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IHttpMessageHandlerFactory&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HelloService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;_client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Hello_PortTypeClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EndpointBehaviors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpMessageHandlerBehavior&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ServiceName&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Wait! What is this &lt;em&gt;ServiceName&lt;/em&gt;? Well, I need to configure HttpClient, so I will refer it with a &lt;em&gt;ServiceName&lt;/em&gt; both in &lt;code&gt;HelloService&lt;/code&gt; class and in &lt;code&gt;Startup&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// Here we configure how the HttpClient with HtpMessagehandler will be configured, like for any HTTP client (e.g. calling REST/JSON service)&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HelloService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Some custom configuration like request timeout&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There you will put your additional handlers, Polly resilience and all those nice things, like you already do when calling REST.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveat #2
&lt;/h2&gt;

&lt;p&gt;You can't put in &lt;code&gt;AddHttpClient&lt;/code&gt; stuff like &lt;code&gt;BaseAddress&lt;/code&gt; or Authentication etc. That stuff needs to be setup on Endpoint or Binding, like explained in this blog post &lt;a href="https://medium.com/grensesnittet/integrating-with-soap-web-services-in-net-core-adebfad173fb" rel="noopener noreferrer"&gt;https://medium.com/grensesnittet/integrating-with-soap-web-services-in-net-core-adebfad173fb&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I suggest you setup some of the things in constructor of your service (HelloService in my example) with injecting of Options, but I will leave that up to you, depending on how much things you need to setup and your best practices on that. &lt;/p&gt;

&lt;h2&gt;
  
  
  Caveat #3
&lt;/h2&gt;

&lt;p&gt;I was so happy with the solution, so I just wanted to confirm it like in blog post with some load test with JMeter. I started the SoapUI mock (provided in the repository), opened CurrPorts and I didn't like what I found there. Even though calls did work, application opened too many connections - no connection pooling! Basically opened a connection per request! That was unacceptable.&lt;/p&gt;

&lt;p&gt;It looked like solution was not using &lt;code&gt;IHttpClientFactory&lt;/code&gt;. That was not possible, I went through debugger, it should work as advertised. CurrPorts was looking good on that blog post. What am I missing?&lt;/p&gt;

&lt;p&gt;After few hours wasted on looking what could be wrong, I noticed that there are new NuGet versions available for &lt;code&gt;System.ServiceModel.*&lt;/code&gt; packages. Mine (generated by VS) were &lt;code&gt;4.4.0&lt;/code&gt;, latest were &lt;code&gt;4.7.0&lt;/code&gt;. No, it can't be something stupid like that... yes it was.&lt;/p&gt;

&lt;p&gt;I updated NuGet packages and all started working as it should!&lt;/p&gt;

&lt;p&gt;I hit it from JMeter with 5 threads (and 100 calls per thread) and &lt;em&gt;voila&lt;/em&gt; - 5 connections opened, just like it should!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6zuv8jz3ymg3ym4s343b.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6zuv8jz3ymg3ym4s343b.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;If you would like to recreate code from repository, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SoapUI, to run mock (&lt;a href="https://www.soapui.org/downloads/soapui/" rel="noopener noreferrer"&gt;https://www.soapui.org/downloads/soapui/&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;JMeter to run load tests (&lt;a href="https://jmeter.apache.org/download_jmeter.cgi" rel="noopener noreferrer"&gt;https://jmeter.apache.org/download_jmeter.cgi&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;CurrPorts to see how many ports have been open (&lt;a href="https://www.nirsoft.net/utils/cports.html#DownloadLinks" rel="noopener noreferrer"&gt;https://www.nirsoft.net/utils/cports.html#DownloadLinks&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted to keep this short (failed as usual), so please ask questions if you are not clear on some of the steps.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>soap</category>
      <category>httpclient</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Ocelot Gateway with "auto-generated" Swagger</title>
      <dc:creator>Bojan Nikolić</dc:creator>
      <pubDate>Sun, 10 Nov 2019 16:00:48 +0000</pubDate>
      <link>https://dev.to/nikolicbojan/ocelot-gateway-with-auto-generated-swagger-4ofc</link>
      <guid>https://dev.to/nikolicbojan/ocelot-gateway-with-auto-generated-swagger-4ofc</guid>
      <description>&lt;p&gt;We have a Gateway API that routes all calls to services below. The simplest initial solution was to create it with controllers and to define return types and other annotations, so we could have Swagger UI for other business units that use our "common" services.&lt;/p&gt;

&lt;p&gt;I re-invented the wheel with creating a library on top of the &lt;code&gt;HttpClient&lt;/code&gt;, just in order to be able to call services below. I was aware of &lt;code&gt;Ocelot&lt;/code&gt;, but didn't have a solution to make it work and have Swagger file generated.&lt;/p&gt;

&lt;p&gt;Now I have a workaround, that will not require team to learn swagger's JSON/YAML syntax. We can continue working as we did and also have &lt;code&gt;Ocelot&lt;/code&gt; gateway with all its' possibilities.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TL;DR; &lt;br&gt;
I am keeping my &lt;strong&gt;OLD&lt;/strong&gt; gateway in order to generate &lt;code&gt;swagger.json&lt;/code&gt;, but will not deploy it any more to server. I am also creating new &lt;strong&gt;OCELOT&lt;/strong&gt; gateway that will just use that generated &lt;code&gt;swagger.json&lt;/code&gt;. Visit Git repository &lt;a href="https://github.com/nikolic-bojan/ocelot-with-swagger"&gt;https://github.com/nikolic-bojan/ocelot-with-swagger&lt;/a&gt; that contains code and build definition for this transition.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Initial state
&lt;/h1&gt;

&lt;p&gt;I guess you already opened GitHub repository, so I would like to explain what was the initial state of the solution. There were 2 projects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gateway - This is initial gateway with controllers and Swashbuckle library to generate and show Swagger UI.&lt;/li&gt;
&lt;li&gt;Api - This represents some underlying service that gateway is calling.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I added some sample call from Gateway to Api, so you can verify it works. I commented it out in code so we can prove later we do not need it really, but you can uncomment to see it works. Of course, this is super-simplified code, for easier understanding. Grab a look at the &lt;code&gt;WeatherForecastController&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecast&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_httpClientFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"weatherforecast"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecast&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsByteArrayAsync&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, that works. We have return type, we can add more annotations, so Swagger definition can be more rich, with samples etc. On start it generates &lt;code&gt;swagger.json&lt;/code&gt; and all is good. All except I use some Ocelot-like library I made, that is proven in Production, but doesn't have all features Ocelot has and it is not battle tested with hundreds of projects, so some edge cases could occur. In Production. Not so fun.&lt;/p&gt;

&lt;h1&gt;
  
  
  Googling for the solution
&lt;/h1&gt;

&lt;p&gt;I was hoping there is some solution to jump from internal library to Ocelot without too much fuzz. The conclusions were next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is no automatic solution as it is impossible (&lt;a href="https://github.com/ThreeMammals/Ocelot/issues/161"&gt;https://github.com/ThreeMammals/Ocelot/issues/161&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;swagger.json&lt;/code&gt; and maintain it manually (entire team should learn syntax and know what they are doing)&lt;/li&gt;
&lt;li&gt;Somehow generate &lt;code&gt;swagger.json&lt;/code&gt; on each model change and just use it in Ocelot gateway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Second was an option, but I am already pushing my luck with the team with jumping to new framework, changes in our CI, etc. Dropping "we need to learn Swagger syntax" would maybe be too much. Issues with adoption, bugs introduced in first few weeks/months, etc. Not worth of it. Maybe in 2020 :)&lt;/p&gt;

&lt;h1&gt;
  
  
  Solution and Pros/Cons
&lt;/h1&gt;

&lt;p&gt;OK, then what? Easy - we can keep the Old Gateway project to generate &lt;code&gt;swagger.json&lt;/code&gt; and just use that in new Ocelot Gateway!&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We use Ocelot! With all its' possibilities and extensibility.&lt;/li&gt;
&lt;li&gt;We do not need to learn Swagger specification.&lt;/li&gt;
&lt;li&gt;We change very little how we work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have a project that has sole purpose to generate &lt;code&gt;swagger.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We need to add manually &lt;code&gt;ReRoutes&lt;/code&gt; (we had to do similar in Old Gateway).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my opinion pros/cons looks good. I like it. Someone could say that you can do some magic stuff in internal library with some handlers, but you can also use handlers in Ocelot. Also... magic... not sure if it is good always.&lt;/p&gt;

&lt;h1&gt;
  
  
  Generate &lt;code&gt;swagger.json&lt;/code&gt; for Ocelot
&lt;/h1&gt;

&lt;p&gt;Small problem was - how to generate &lt;code&gt;swagger.json&lt;/code&gt;. It is generated when application starts. I didn't want Old Gateway to start on run anywhere.&lt;/p&gt;

&lt;p&gt;No problem, Test project and &lt;strong&gt;WebApplicationFactory&lt;/strong&gt; to the rescue!&lt;br&gt;
You create new (or use existing) Test project and create the following test case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TestMethod&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;CreateSwaggerJson&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;WebApplicationFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OldGateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;WebApplicationFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OldGateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;swaggerResponse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/swagger/v1/swagger.json"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteAllTextAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"../../../../../src/OcelotGateway/wwwroot/swagger.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;swaggerResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To explain it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;WebApplicationFactory&lt;/strong&gt; will run your Old Gateway in-memory.&lt;/li&gt;
&lt;li&gt;HttpClient will GET &lt;code&gt;swagger.json&lt;/code&gt; that was generated on start.&lt;/li&gt;
&lt;li&gt;You will save it in new Ocelot Gateway &lt;code&gt;wwwroot&lt;/code&gt; folder so it can be picked up by SwaggerUI middleware.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is it! Done! You have &lt;code&gt;swagger.json&lt;/code&gt; generated from a project that will never be deployed or ran anywhere!&lt;/p&gt;

&lt;h1&gt;
  
  
  Swagger UI in new Ocelot Gateway
&lt;/h1&gt;

&lt;p&gt;With previous step, &lt;code&gt;swagger.json&lt;/code&gt; is already in Ocelot's &lt;code&gt;wwwroot&lt;/code&gt; folder and just needs to be configured with one simple step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSwaggerUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SwaggerEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/swagger.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"My API V1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I deliberately added a &lt;code&gt;dummy.txt&lt;/code&gt; file in &lt;code&gt;wwwroot&lt;/code&gt; as I had issue with build on Azure DevOps - as nothing was in that folder, it was not created, so saving &lt;code&gt;swagger.json&lt;/code&gt; from Test case was failing. Now it is all good.&lt;/p&gt;

&lt;h1&gt;
  
  
  Azure DevOps build
&lt;/h1&gt;

&lt;p&gt;So, it was all fine in local. I manually run Tests and &lt;code&gt;swagger.json&lt;/code&gt; is saved to Ocelot's &lt;code&gt;wwwroot&lt;/code&gt; folder. Then I start Ocelot Gateway and it picks it up from that folder.&lt;/p&gt;

&lt;p&gt;That is not how it will look on a build server. That is why I created a YAML build to show you how it should work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;

&lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;vmImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;windows-latest'&lt;/span&gt;

&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;buildConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Release'&lt;/span&gt;

&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DotNetCoreCLI@2&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build'&lt;/span&gt;
    &lt;span class="na"&gt;projects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OcelotWithSwagger.sln'&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DotNetCoreCLI@2&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test'&lt;/span&gt;
    &lt;span class="na"&gt;projects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test/Gateway.Tests/Gateway.Tests.csproj'&lt;/span&gt;
    &lt;span class="na"&gt;testRunTitle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Generate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Old&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Gateway&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;swagger.json'&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DotNetCoreCLI@2&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;publish'&lt;/span&gt;
    &lt;span class="na"&gt;publishWebProjects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;projects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;src/OcelotGateway/OcelotGateway.csproj'&lt;/span&gt;
    &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--configuration&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$(buildConfiguration)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--output&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$(Build.ArtifactStagingDirectory)'&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishBuildArtifacts@1&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;PathtoPublish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$(Build.ArtifactStagingDirectory)'&lt;/span&gt;
    &lt;span class="na"&gt;ArtifactName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OcelotGateway'&lt;/span&gt;
    &lt;span class="na"&gt;publishLocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Container'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It has the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build entire solution (all projects)&lt;/li&gt;
&lt;li&gt;Run Gateway.Tests project, to create swagger file&lt;/li&gt;
&lt;li&gt;Publish Ocelot Gateway&lt;/li&gt;
&lt;li&gt;Create build artifact (zipped Ocelot Gateway as a single EXE)&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Final words
&lt;/h1&gt;

&lt;p&gt;Now you have all to transform your API based Gateway to Ocelot and keep Swagger UI auto-generated.&lt;/p&gt;

&lt;p&gt;TBH, I plan to push my team toward learning Swagger/OpenApi specification, but YAML style. Maybe in 2020, but at least we can now use great library like Ocelot!&lt;/p&gt;

&lt;p&gt;I would like to hear comments and questions, so we can improve this some more, if needed.&lt;/p&gt;

&lt;p&gt;BR,&lt;br&gt;
Bojan&lt;/p&gt;

</description>
      <category>ocelot</category>
      <category>gateway</category>
      <category>dotnet</category>
      <category>swagger</category>
    </item>
    <item>
      <title>Memory+Distributed Caching in .NET Core</title>
      <dc:creator>Bojan Nikolić</dc:creator>
      <pubDate>Sun, 08 Sep 2019 21:54:31 +0000</pubDate>
      <link>https://dev.to/nikolicbojan/memory-distributed-caching-in-net-core-5hm3</link>
      <guid>https://dev.to/nikolicbojan/memory-distributed-caching-in-net-core-5hm3</guid>
      <description>&lt;p&gt;We were building yet another adapter toward some 3rd party service. They are caching results on their side for 1 day (7 days for one of the routes), but charging you per call.&lt;br&gt;
OK, if you want to play like that... We can cache also!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TL;DR; &lt;br&gt;
I created a small service class that allows you to cache in both Memory and some Distributed cache and take best of both worlds.&lt;br&gt;
Visit Git repository &lt;a href="https://github.com/nikolic-bojan/common-utilities" rel="noopener noreferrer"&gt;https://github.com/nikolic-bojan/common-utilities&lt;/a&gt;&lt;br&gt;
That contains code, unit tests and sample console application.&lt;br&gt;
Copy/Paste the code and make your flavor.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;
  
  
  Caching in Core
&lt;/h1&gt;

&lt;p&gt;Documentation is really good in .NET Core, so this is no exception. You have 2 options when caching - IMemoryCache and IDistributedCache.&lt;/p&gt;

&lt;p&gt;Since our apps run on IIS, even though they are set to Allways Running, our Ops crew set App Pools to restart every 29 hours. So, caching just to memory could work, but we have several servers, we could redeploy, some server restarts...&lt;/p&gt;

&lt;p&gt;OK, let's do it then in some Distributed cache. We had Redis setup and that is all good, but it is slower than memory cache. But what if...&lt;/p&gt;
&lt;h1&gt;
  
  
  Best of both worlds
&lt;/h1&gt;

&lt;p&gt;Why not caching to both memory and to Redis (or some other Distrubuted cache)? I searched a little bit and stumbled upon a great post by Nick Craver about how they do it in Stack Overflow &lt;a href="https://nickcraver.com/blog/2019/08/06/stack-overflow-how-we-do-app-caching/" rel="noopener noreferrer"&gt;How We Do App Caching&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;OK, we are super-far from that number of requests and needs for caching, but why not make some reusable code for some Caching Service that would have options for both memory and distributed caching?&lt;/p&gt;

&lt;p&gt;Here is the logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check if you have cached item in memory cache (on that server)&lt;/li&gt;
&lt;li&gt;If you have, all good, no need to do anything else&lt;/li&gt;
&lt;li&gt;If you do not - go to Distributed cache and check there&lt;/li&gt;
&lt;li&gt;If it is there, grab it and store it in memory cache also&lt;/li&gt;
&lt;li&gt;If it is not, execute some &lt;strong&gt;"factory"&lt;/strong&gt; method that will create the value for cache and store it in distributed and then memory cache&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the example of how would you call Caching Service&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cachingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetOrCreateAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TestObject&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TestObject&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; 
    &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Most interesting part is second parameter and that one is a &lt;code&gt;Func&amp;lt;Task&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt;. That function will be only executed if Caching Service doesn't find &lt;strong&gt;key&lt;/strong&gt; in both memory and distributed cache.&lt;br&gt;
That function will contain some "expensive" part - from code execution or from pricey-3rd-party-call perspective.&lt;/p&gt;
&lt;h1&gt;
  
  
  Show us the code!
&lt;/h1&gt;

&lt;p&gt;This is the current state of code on GitHub. I deliberately didn't want to create some NuGet, because I didn't do that yet for our local use also. When I figure out the needs from several projects, I will build something truly reusable, even though it is very reusable now.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/nikolic-bojan" rel="noopener noreferrer"&gt;
        nikolic-bojan
      &lt;/a&gt; / &lt;a href="https://github.com/nikolic-bojan/common-utilities" rel="noopener noreferrer"&gt;
        common-utilities
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Common utilities that can be reused for any project
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Memory+Distributed Caching in .NET Core&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;We were building yet another adapter toward some 3rd party service. They are caching results on their side for 1 day (7 days for one of the routes), but charging you per call
OK, if you want to play like that... We can cache also!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TL;DR
I created a small service class that allows you to cache in both Memory and some Distributed cache and take best of both worlds.
Visit Git repository &lt;a href="https://github.com/nikolic-bojan/common-utilities" rel="noopener noreferrer"&gt;https://github.com/nikolic-bojan/common-utilities&lt;/a&gt;
That contains code, unit tests and sample console application.
Copy/Paste the code and make your flavor.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;#Caching in Core&lt;/p&gt;
&lt;p&gt;Documentation is really good in .NET Core, so this is no exception. You have 2 options when caching - IMemoryCache and IDistributedCache.&lt;/p&gt;
&lt;p&gt;Since our apps run on IIS, even though they are set to Allways Running, our Ops crew set App Pools to restart every 29 hours. So, caching just to memory…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/nikolic-bojan/common-utilities" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Interface is fairly simple&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ICommonCachingService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GetOrCreateAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt; &lt;span class="n"&gt;memoryCacheExpiration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;distributedCacheExpiration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;

    &lt;span class="nc"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetOrCreateStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt; &lt;span class="n"&gt;memoryCacheExpiration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;distributedCacheExpiration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GetOrCreateAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;IConverter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt; &lt;span class="n"&gt;memoryCacheExpiration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;distributedCacheExpiration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You have 3 methods:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Caching objects with JSON serialization.&lt;/li&gt;
&lt;li&gt;Caching strings without serialization (because it didn't work for me with JSON serialization; I am lazy to figure out why)&lt;/li&gt;
&lt;li&gt;Caching whatever with custom Converter (implement your serialization)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First two are just calling the third, where the actual implementation is, with a pre-selected Converter.&lt;/p&gt;

&lt;p&gt;Parameters for the methods are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;key&lt;/strong&gt; - A key to cache by, make some method for generating unique one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;factory&lt;/strong&gt; - I explained it earlier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;memoryCacheExpiration&lt;/strong&gt; - Time-to-live (TTL) in memory cache. There is no sliding option.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;distributedCacheExpiration&lt;/strong&gt; - TTL in distributed cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;converter&lt;/strong&gt; - Simple serialize/deserialize interface as code below shows.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IConverter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="nf"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Memory cache
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GetOrCreateAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;IConverter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt; &lt;span class="n"&gt;memoryCacheExpiration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;distributedCacheExpiration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_memoryCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetOrCreateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;TimeSpan&lt;/span&gt; &lt;span class="n"&gt;calculatedDistributedCacheExpiration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;distributedCacheExpiration&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;memoryCacheExpiration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AbsoluteExpiration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memoryCacheExpiration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;GetFromDistributedCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calculatedDistributedCacheExpiration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;MemoryCache already has this handy &lt;code&gt;GetOrCreateAsync&lt;/code&gt; method that accepts &lt;strong&gt;"factory"&lt;/strong&gt; method. I just added setup for expiration and forwarded function call with all the parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Distributed cache
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GetFromDistributedCache&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;IConverter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;generatedKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt; &lt;span class="n"&gt;calculatedDistributedCacheExpiration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogDebug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Getting cached value from Distributed cache for key {Key}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generatedKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cachedItem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_distributedCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;generatedKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cachedItem&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogDebug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Read cached value from Distributed cache for key {Key}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generatedKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cachedItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Exception getting cached item from Distributed cache."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cacheEntryOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;DistributedCacheEntryOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;AbsoluteExpirationRelativeToNow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calculatedDistributedCacheExpiration&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;serializedValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_distributedCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;generatedKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serializedValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cacheEntryOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogDebug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Stored in Distributed cache for key {Key}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generatedKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Exception storing cached item in Distributed cache."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This little fellow has a bit more logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tries to get value from distributed cache and return value if available,&lt;/li&gt;
&lt;li&gt;If it is not there, calls &lt;strong&gt;"factory"&lt;/strong&gt; method to create value and then store it in cache.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also added basic try-catch, so nothing breaks if there is some issue with Redis or some other distributed cache, both for reading and storing.&lt;/p&gt;

&lt;h1&gt;
  
  
  Example
&lt;/h1&gt;

&lt;p&gt;You already saw it on the beginning, but let's do it again&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cachingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetOrCreateAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TestObject&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TestObject&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; 
    &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now you know this tells a story - &lt;strong&gt;try to get from memory/distributed cache a value with a key "key"; if you do not find it, crate it with "factory" method and keep it in memory cache for 1 minute and in distributed cache for 5 minutes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can use it in your Controllers, Core/Domain or your Infrastructure (some of the stuff refers to Onion/Clean/whatever-name architecture). I do not think there is just one answer where to use it. Actually, there is - anywhere!&lt;/p&gt;

&lt;h1&gt;
  
  
  Improvements?
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Add some &lt;code&gt;CacheEntryOptions&lt;/code&gt; class for more fine-grained cache setup.&lt;/li&gt;
&lt;li&gt;Add resilience for calls to distributed cache like retry with circuit breaker.&lt;/li&gt;
&lt;li&gt;Allow more than one Distributed cache (Why dude? It's just more headache!).&lt;/li&gt;
&lt;li&gt;Remove dependency on Logging as I just use it to write unit tests with less hustle.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Words of caution!
&lt;/h1&gt;

&lt;p&gt;Since one of the toughest things in programming is caching, beware of all potential issues, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory pressure if you keep to many objects in cache that are big-ish&lt;/li&gt;
&lt;li&gt;Large object heap, if your objects are &amp;gt;85k&lt;/li&gt;
&lt;li&gt;Invalidation - I am not even thinking about it here, but you might need it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why I am not concerned about those in our use-case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I am setting low Memory Cache Expiration (e.g. 10 minutes), so hardly I can come to memory pressure situation.&lt;/li&gt;
&lt;li&gt;I know how big are my objects.&lt;/li&gt;
&lt;li&gt;I setup Distributed Cache Expiration also to some time span that I know it will not require me to regret I do not have Invalidate method.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Know your stuff!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Comments, questions and suggestions are very much welcome!&lt;/p&gt;

&lt;p&gt;Best regards,&lt;br&gt;
Bojan&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>redis</category>
      <category>caching</category>
      <category>core</category>
    </item>
    <item>
      <title>Azure DevOps YAML build for Mono Repository with multiple projects</title>
      <dc:creator>Bojan Nikolić</dc:creator>
      <pubDate>Wed, 21 Aug 2019 23:34:30 +0000</pubDate>
      <link>https://dev.to/nikolicbojan/azure-devops-yaml-build-for-mono-repository-with-multiple-projects-146g</link>
      <guid>https://dev.to/nikolicbojan/azure-devops-yaml-build-for-mono-repository-with-multiple-projects-146g</guid>
      <description>&lt;p&gt;I will show you how I setup the YAML build in Azure DevOps for our Mono repository that contains multiple (20-ish) Services that are part of one Product.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TL;DR; &lt;br&gt;
Visit Git repository &lt;a href="https://github.com/nikolic-bojan/azure-yaml-build"&gt;https://github.com/nikolic-bojan/azure-yaml-build&lt;/a&gt;&lt;br&gt;
It contains a working sample of YAML build for 3 services.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Move to Mono repository
&lt;/h1&gt;

&lt;p&gt;We have ~20 different Services (REST APIs), with a Gateway in front of them that routes calls to those services and orchestrations. You can call it micro-service architecture or not, but let's not get into that discussion now.&lt;/p&gt;

&lt;p&gt;It was organized like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each Service had it's own repository&lt;/li&gt;
&lt;li&gt;Each Model NuGet (that contained shared request/response classes) for inter-service communication had it's repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, around 40 repositories and same number of Build definitions that were created and maintained. Plus, we did GitFlow as a small team. Nightmare!&lt;/p&gt;

&lt;p&gt;When I finally got time, I know where we should go - Mono repository. I do not want to convince you it is for everybody, but if you are already (or on your way) there and need some help, keep on reading.&lt;/p&gt;

&lt;p&gt;This is the &lt;code&gt;short&lt;/code&gt; story how it was done.&lt;/p&gt;

&lt;h2&gt;
  
  
  New repository
&lt;/h2&gt;

&lt;p&gt;Creating a new repository is easy, but what about moving the code? Copy/paste - no way, we lose all the Git history. I found a few good articles online and with a bit help of Sourcetree (I am not Git console guy. Yet) and Git console (you can't avoid it), here are the steps I did &lt;strong&gt;for each&lt;/strong&gt; project. Of course, make sure you are in the new repository's master branch:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add Remote repository in Sourcetree pointing to your &lt;strong&gt;old&lt;/strong&gt; project's repository and call it &lt;strong&gt;repo&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Execute following GIT command &lt;code&gt;git merge repo/master --allow-unrelated-histories&lt;/code&gt;. Of course, chose to move from master or any other old repo's branch.
Very important to use allow-unrelated-histories or it will not work.&lt;/li&gt;
&lt;li&gt;Create in new repository a folder for your Service. You do not want to have all projects in same folder, right?&lt;/li&gt;
&lt;li&gt;Move all files to a new folder.&lt;/li&gt;
&lt;li&gt;Resolve conflicts, if any.&lt;/li&gt;
&lt;li&gt;Push changes.&lt;/li&gt;
&lt;li&gt;Lock old repository's branches to prevent some developer to continue working there.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you have some feature branches (not all new code is on master), you can create new branch on a New Repository and just copy files from Old repository to that Service's folder. Git will figure that out like changes and you can push them. You will loose some commit history on that feature branch, but you can live with that.&lt;/p&gt;

&lt;p&gt;This way I moved all my Services to a new repository, but after these 7 steps I also did few more related to Build definition for each Service. Do not rush into moving all at once. Move Service by Service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build definition changes
&lt;/h2&gt;

&lt;p&gt;Everything is now in the same repository. Also, all Services are in their new folders. Now we come to the problem - we need to change our build definition, depending on how it was made, of course.&lt;/p&gt;

&lt;p&gt;What I had to do is next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allowed trigger only on &lt;strong&gt;master&lt;/strong&gt; and only for a folder where Service is. I just wanted to see builds work with the New Repository.&lt;/li&gt;
&lt;li&gt;Change all the steps/variables that point to *.sln or *.csproj files to follow new folder structure in order to build just what you want, not the whole darn thing.&lt;/li&gt;
&lt;li&gt;Try the build. Build Service, push it on some lower environment and hit it with regression tests. All needs to be in perfect order.&lt;/li&gt;
&lt;li&gt;Now you can migrate next Service.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Model NuGets
&lt;/h2&gt;

&lt;p&gt;Remember I was mentioning those? Those were simple .NET Standard 2.0 projects that contain just POCO models. I decided to move them to a Service's solution as they represent the &lt;strong&gt;interface&lt;/strong&gt; for that Service.&lt;/p&gt;

&lt;p&gt;All other Services that need them also (that call that Service) have them also referenced directly. It is the same Repository!&lt;/p&gt;

&lt;p&gt;I wasn't trying there to keep the history. Copy/paste did the thing. But it is possible as we did with the Services.&lt;/p&gt;

&lt;p&gt;Regressions were all green after this. That was the most important.&lt;/p&gt;

&lt;h1&gt;
  
  
  One Build to rule them all
&lt;/h1&gt;

&lt;p&gt;I wasn't very happy with having build triggers only on &lt;strong&gt;master&lt;/strong&gt;. I could have put triggers on all branches, but I wanted more control. &lt;/p&gt;

&lt;p&gt;What if I wanted to build 2 or 3 Services when code in one changes (remember those Model projects?). I know, that is not clean separation, but after all, the point of having things in one repository is because they are - connected.&lt;/p&gt;

&lt;p&gt;I wasn't keen on maintaining 20+ builds also. I wanted control in one place. &lt;/p&gt;

&lt;h2&gt;
  
  
  PowerShell + YAML to the resque!
&lt;/h2&gt;

&lt;p&gt;It would be very nasty to setup CI through UI to know when to trigger and which Services to build. Yes, I said "Services", not Service. That is due to how I want things to work. If I make changes to several Services at once, I want all of them build. Sometimes if I make change to 1 Service, I want 2 of them to build.&lt;/p&gt;

&lt;p&gt;Good thing is that all our Builds had same steps and we parameterized one build, so it can be easily cloned and specialized for particular Service using Variables.&lt;/p&gt;

&lt;p&gt;First, create a new Build definition. You need to select YAML build. There you should enter following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;

&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;self&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build_Queue&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;powershell@2&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;setBuildQueue&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;targetType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;filePath&lt;/span&gt;
      &lt;span class="na"&gt;filePath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./build/get-service-queue.ps1&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Get&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Service&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Queue'&lt;/span&gt;    

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-template.yml&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service_01&lt;/span&gt;
    &lt;span class="na"&gt;solutionFolder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service1'&lt;/span&gt;
    &lt;span class="na"&gt;projectFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/service1.api/service1.api.csproj'&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-template.yml&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service_02&lt;/span&gt;
    &lt;span class="na"&gt;solutionFolder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service2'&lt;/span&gt;
    &lt;span class="na"&gt;projectFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/service2.api/service2.api.csproj'&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-template.yml&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service_03&lt;/span&gt;
    &lt;span class="na"&gt;solutionFolder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service3'&lt;/span&gt;
    &lt;span class="na"&gt;projectFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/service3.api/service3.api.csproj'&lt;/span&gt;

&lt;span class="c1"&gt;# Add more template jobs with parameters for other Services&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, what I just did was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set the trigger on &lt;strong&gt;all&lt;/strong&gt; branches.&lt;/li&gt;
&lt;li&gt;Set repository to itself.&lt;/li&gt;
&lt;li&gt;Created a PowerShell Job to get list of services to build.&lt;/li&gt;
&lt;li&gt;Defined to run build template for each of my services (here just 3)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What will happen is that a PowerShell Script will determine which services should be built (custom logic) and output that as a list of semi-colon (;) separated Service folder names (not perfect, still polishing that one, my PowerShell is beginner level).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;buildQueueVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;buildSeparator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;";"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;Function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;AppendQueueVariable&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$folderName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$folderNameWithSeparator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$folderName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;buildSeparator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;buildQueueVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-notmatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$folderNameWithSeparator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;buildQueueVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;buildQueueVariable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$folderNameWithSeparator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;BUILDQUEUEINIT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Build Queue Init: &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;BUILDQUEUEINIT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"##vso[task.setvariable variable=buildQueue;isOutput=true]&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;BUILDQUEUEINIT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Get all files that were changed&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$editedFiles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HEAD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HEAD~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--name-only&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Check each file that was changed and add that Service to Build Queue&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$editedFiles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="kr"&gt;Switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Wildcard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;        
        &lt;/span&gt;&lt;span class="s2"&gt;"service1/*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service 1 changed"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;AppendQueueVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"service1"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"service2/*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service 2 changed"&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="n"&gt;AppendQueueVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"service2"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"service3/*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service 3 changed"&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="n"&gt;AppendQueueVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"service3"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;AppendQueueVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"service2"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="c"&gt;# The rest of your path filters&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Build Queue: &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;buildQueueVariable&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"##vso[task.setvariable variable=buildQueue;isOutput=true]&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;buildQueueVariable&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the logic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I first check if anyone set &lt;strong&gt;buildQueueInit&lt;/strong&gt; variable on build. I want to have option to set which services to build manually. If yes, I set &lt;strong&gt;buildQueue&lt;/strong&gt; output (very important to be &lt;strong&gt;output&lt;/strong&gt;) variable and skip the part with GIT diff.&lt;/li&gt;
&lt;li&gt;If &lt;strong&gt;buildQueueInit&lt;/strong&gt; was not set, I will do GIT diff, pickup files that were changed in last commit and figure out in which folders they are, so I can make a list of changed Services. Then I set &lt;strong&gt;buildQueue&lt;/strong&gt; output variable with that value.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That GIT diff magic was picked up from Taul's answer and gets all files that were changed in last commit&lt;br&gt;
&lt;a href="https://stackoverflow.com/questions/53227343/triggering-azure-devops-builds-based-on-changes-to-sub-folders/53837840#53837840"&gt;https://stackoverflow.com/questions/53227343/triggering-azure-devops-builds-based-on-changes-to-sub-folders/53837840#53837840&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Build template
&lt;/h2&gt;

&lt;p&gt;OK, last piece of the puzzle - Build template. I grabbed it by clicking on &lt;strong&gt;View YAML&lt;/strong&gt; button of my existing build pipeline. Then I had to adjust it a bit. Here is how the first part of it looks (I just kept the Restore task for sample):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--'&lt;/span&gt;
    &lt;span class="na"&gt;BuildConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Release'&lt;/span&gt;
    &lt;span class="na"&gt;BuildPlatform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;any&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;cpu'&lt;/span&gt;
    &lt;span class="na"&gt;solutionFolder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--'&lt;/span&gt;
    &lt;span class="na"&gt;projectFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/Api/Api.csproj'&lt;/span&gt;
    &lt;span class="na"&gt;RestoreBuildProjects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$(project.file)'&lt;/span&gt;    

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ parameters.name }}&lt;/span&gt;
  &lt;span class="na"&gt;dependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build_Queue&lt;/span&gt;
  &lt;span class="na"&gt;continueOnError&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;BuildConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ parameters.BuildConfiguration }}&lt;/span&gt;
    &lt;span class="na"&gt;BuildPlatform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ parameters.BuildPlatform }}&lt;/span&gt;
    &lt;span class="s"&gt;project.file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ parameters.solutionFolder }}${{ parameters.projectFile }}&lt;/span&gt;
    &lt;span class="s"&gt;Parameters.RestoreBuildProjects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ parameters.RestoreBuildProjects }}&lt;/span&gt;
    &lt;span class="na"&gt;myBuildQueue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$[ dependencies.Build_Queue.outputs['setBuildQueue.buildQueue'] ]&lt;/span&gt;
  &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;and(succeeded(), contains(dependencies.Build_Queue.outputs['setBuildQueue.buildQueue'], '${{ parameters.solutionFolder }}'))&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;powershell@2&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;targetType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inline&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Write-Host&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Queue&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;init:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$(buildQueueInit)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;parameters&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$(myBuildQueue)"'&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DotNetCoreCLI@2&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;restore&lt;/span&gt;
      &lt;span class="na"&gt;projects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$(Parameters.RestoreBuildProjects)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parameters are input parameters for the template. You remember we used &lt;strong&gt;name&lt;/strong&gt; and &lt;strong&gt;solutionFolder&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Variables are for the Job and we read some of the input parameters in order to set them up.&lt;/p&gt;

&lt;p&gt;Important here is &lt;strong&gt;myBuildQueue&lt;/strong&gt; that contains entire list of all Services that should be built.&lt;br&gt;
Even more important is the &lt;strong&gt;condition&lt;/strong&gt; that checks if this entire Job should be executed. It checks if &lt;strong&gt;solutionFolder&lt;/strong&gt; is in that semi-colon separated list that was populated in that crazy PowerShell script.&lt;/p&gt;

&lt;p&gt;If it matches - Tasks in this Job will be ran. If not, it will just skip this entire Job.&lt;/p&gt;

&lt;p&gt;In the PowerShell script, you can define that Services have dependencies, so you can say that if there is a change in Service 3, you should also build Service 2. That is what I did for Service 3 in this part of the script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;Switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Wildcard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"service3/*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service 3 changed"&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="n"&gt;AppendQueueVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"service3"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;AppendQueueVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"service2"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is it! You can setup some interesting rules within the scripts in order to define what should be built on which change.&lt;/p&gt;

&lt;p&gt;Now, you have just one Build to maintain!&lt;/p&gt;

&lt;h1&gt;
  
  
  Queue build manually with a list of Services to buils
&lt;/h1&gt;

&lt;p&gt;Remember when I mentioned &lt;strong&gt;buildQueueInit&lt;/strong&gt; and you setup a Variable. Here are the steps to do it in Azure DevOps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to your Pipeline -&amp;gt; Builds and select your YAML build&lt;/li&gt;
&lt;li&gt;Click to Edit build and you will see a button Variables&lt;/li&gt;
&lt;li&gt;Click on it and then to + to create new one&lt;/li&gt;
&lt;li&gt;Set it's name to &lt;strong&gt;buildQueueInit&lt;/strong&gt; and check &lt;strong&gt;Let users override this value when running this pipeline&lt;/strong&gt; box&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, when you manually queue a build, you can add a value in it, e.g. &lt;strong&gt;service1;service2;&lt;/strong&gt; and Service 1 and 2 will be built for you.&lt;/p&gt;

&lt;p&gt;One scenario where you do want this is maybe for some Release branches, where you do not want stuff to be auto-triggered based on just last commit. You want to decide "those Services will be released after this Sprint".&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>azure</category>
      <category>devops</category>
      <category>git</category>
      <category>powershell</category>
    </item>
  </channel>
</rss>
