<?xml version="1.0" encoding="utf-8"?><rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0"><channel><title>OdeToCode by K. Scott Allen</title><description>OdeToCode by K. Scott Allen</description><copyright>(c) 2004 to 2026 OdeToCode LLC</copyright><managingEditor>scott@OdeToCode.com</managingEditor><generator>OdeToCode 2.0</generator><image><url>https://odetocode.com/images/odetocode.jpg</url><title>OdeToCode by K. Scott Allen</title><link /></image><a10:link href="https://odetocode.com/" /><item><guid isPermaLink="true">http://odetocode.com/blogs/scott/archive/2020/01/30/markdown-front-matter-for-metadata.aspx</guid><link>http://odetocode.com/blogs/scott/archive/2020/01/30/markdown-front-matter-for-metadata.aspx</link><author>scott@OdeTocode.com</author><title> Markdown Front Matter For Metadata</title><description>&lt;p&gt;Continuing the story of &lt;a href="https://odetocode.com/blogs/scott/archive/2020/01/23/a-custom-renderer-extension-for-markdig.aspx"&gt;replacing Windows Live Writer&lt;/a&gt; with some &lt;a href="https://odetocode.com/blogs/scott/archive/2020/01/26/automatic-image-uploads-with-markdig-processing.aspx"&gt;custom tools&lt;/a&gt;...&lt;/p&gt;
&lt;p&gt;In addition to image uploads and code formatting, I also needed the ability to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Specify the date and time for the post to turn publicly visible (future posting).&lt;/li&gt;
&lt;li&gt;Save the database-generated ID for a post so the post can be updated in the future.&lt;/li&gt;
&lt;li&gt;Save the server-generated URL for a post to easily preview the post.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of this metadata was information I wanted to track for each markdown file, but I didn't want to keep the metadata &lt;em&gt;outside&lt;/em&gt; of the markdown file in a separate store, because synchronization only adds complexity. I was looking for a way to embed metadata into the markdown file itself, but the &lt;a href="https://commonmark.org/"&gt;CommonMark&lt;/a&gt; specification doesn't provide for metadata.&lt;/p&gt;
&lt;p&gt;Fortunately, I found a number of tools and libraries support &lt;a href="https://assemble.io/docs/YAML-front-matter.html"&gt;YAML front matter&lt;/a&gt; in markdown. Front matter is easy to write, easy to parse, and a good solution if you need to keep metadata associated with your markdown &lt;em&gt;with&lt;/em&gt; you markdown.&lt;/p&gt;
&lt;p&gt;The picture below shows a post after publication, so the tools have added an ID and a URL. I add the date to publish and the title.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/frontmatter/frontmatter.jpg" class="image-center" alt="front matter" /&gt;&lt;/p&gt;
</description><pubDate>Thu, 30 Jan 2020 21:12:00 Z</pubDate></item><item><guid isPermaLink="true">http://odetocode.com/blogs/scott/archive/2020/01/26/automatic-image-uploads-with-markdig-processing.aspx</guid><link>http://odetocode.com/blogs/scott/archive/2020/01/26/automatic-image-uploads-with-markdig-processing.aspx</link><author>scott@OdeTocode.com</author><title> Automatic Image Uploads with Markdig Processing</title><description>&lt;p&gt;Continuing the story of &lt;a href="https://odetocode.com/blogs/scott/archive/2020/01/23/a-custom-renderer-extension-for-markdig.aspx"&gt;replacing Windows Live Writer&lt;/a&gt; with some custom tools...&lt;/p&gt;
&lt;p&gt;One of the best features of WLW was the ability to paste an image into a post and have the image magically appear on the server in an expected location. For my new workflow, I needed a second Markdig extension to look for images in a post and automatically upload the images through a WebAPI I built into this blog. The WebAPI returns the URL of the uploaded (or updated) image. This API, by the way, was a simple replacement for the ancient &lt;a href="http://xmlrpc.com/"&gt;XML RPC&lt;/a&gt; API that WLW relied on.&lt;/p&gt;
&lt;h3&gt;The Markdig Document Processed Event&lt;/h3&gt;
&lt;p&gt;For this extension I didn't need to custom render an image tag. Instead, I could wait until Markdig finished converting markdown into HTML and go looking for &lt;code&gt;img&lt;/code&gt; tags. One way to implement this style of extension for Markdig is to implement the low level &lt;code&gt;IMarkdownExtension&lt;/code&gt; interface.&lt;/p&gt;
&lt;pre class="brush: csharp; gutter: false; toolbar: false; "&gt;
public class ImageProcessingExtension : IMarkdownExtension
{
   private readonly IFileSystem fileSystem;
   private readonly IBlogServer blogServer;

   public ImageProcessingExtension(IFileSystem fileSystem, IBlogServer blogServer)
   {
       this.fileSystem = fileSystem;
       this.blogServer = blogServer;
   }

   public void Setup(MarkdownPipelineBuilder pipeline)
   {
       pipeline.DocumentProcessed += Pipeline_DocumentProcessed;  
   }

   public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
   {
   }

   private void Pipeline_DocumentProcessed(MarkdownDocument document)
   {
       var damn = document.Descendants().ToArray();
       foreach(var node in document.Descendants()
                                   .OfType&amp;lt;LinkInline&amp;gt;()
                                   .Where(l =&amp;gt; l.IsImage))
       {
           var localName = node.Url;
           if (!fileSystem.Exists(localName))
           {
               throw new ArgumentException($&amp;quot;Cannot find file {localName}&amp;quot;);
           }
           var bytes = fileSystem.ReadBinary(localName);
           var newUrl = blogServer.UploadMedia(localName, bytes);
           node.Url = newUrl;
       }
   }
}      
&lt;/pre&gt;
&lt;p&gt;This extension searches the rendered HTML nodes for images, uploads the image in the URL, and then updates the URL with the remote address.&lt;/p&gt;
&lt;p&gt;Note that all this happens inside an event handler which must return &lt;code&gt;void&lt;/code&gt;. However, the &lt;code&gt;UploadMedia&lt;/code&gt; method uses &lt;code&gt;HttpClient&lt;/code&gt; behind the scenes, and must be &lt;code&gt;async&lt;/code&gt;. As you know, &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;void&lt;/code&gt; return types mix together like potassium and water - an example of why I say we must always &lt;a href="https://odetocode.com/blogs/scott/archive/2019/03/04/await-the-async-letdown.aspx"&gt;await the async letdown&lt;/a&gt;.&lt;/p&gt;
</description><pubDate>Sun, 26 Jan 2020 21:12:00 Z</pubDate></item><item><guid isPermaLink="true">http://odetocode.com/blogs/scott/archive/2020/01/23/a-custom-renderer-extension-for-markdig.aspx</guid><link>http://odetocode.com/blogs/scott/archive/2020/01/23/a-custom-renderer-extension-for-markdig.aspx</link><author>scott@OdeTocode.com</author><title> A Custom Renderer Extension for Markdig</title><description>&lt;p&gt;A couple years ago I decided to stop using Windows Live Writer for authoring blog posts and build my own publishing tools using markdown and VSCode. Live Writer was a fantastic tool during its heyday, but some features started to feel cumbersome. Adding code into a blog post, as one example.&lt;/p&gt;
&lt;p&gt;This blog uses &lt;a href="https://alexgorbatchev.com/SyntaxHighlighter/"&gt;SyntaxHighlighter&lt;/a&gt; to render code blocks, which requires HTML in a specific format. With WLW the HTML formatting required a toggle into HTML mode, or using an extension which was no longer supported in the OSS version of WLW.&lt;/p&gt;
&lt;p&gt;What I really wanted was to author a post in markdown and use simple code fences to place code into a post.&lt;/p&gt;
&lt;p&gt;``` csharp&lt;br /&gt;
public void AnOdeToCode()&lt;br /&gt;
{&lt;/p&gt;
&lt;p&gt;}&lt;br /&gt;
```&lt;/p&gt;
&lt;p&gt;Simple!&lt;/p&gt;
&lt;p&gt;All I'd need is a markdown processor that would allow me to add some custom rendering for code fences.&lt;/p&gt;
&lt;h3&gt;Markdig Extensions&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/lunet-io/markdig"&gt;Markdig&lt;/a&gt; is a fast, powerful, CommonMark compliant, &lt;em&gt;extensible&lt;/em&gt; Markdown processor for .NET. Thanks to &lt;a href="https://weblog.west-wind.com/posts/2018/mar/23/creating-an-aspnet-core-markdown-taghelper-and-parser"&gt;Rick Strahl&lt;/a&gt; for bringing the library to my attention. I use Markdig in my tools to transform a markdown file into HTML for posting here on the site.&lt;/p&gt;
&lt;p&gt;There are at least a couple different techniques you can use to write an extension for Markdig. What I needed was an extension point to render SyntaxHighlighter flavored HTML for every code fence in a post. With Markdig, this means adding an &lt;code&gt;HtmlOBjectRenderer&lt;/code&gt; into the processing pipeline.&lt;/p&gt;
&lt;pre class="brush: csharp; gutter: false; toolbar: false; "&gt;
public class PreCodeRenderer : HtmlObjectRenderer&amp;lt;CodeBlock&amp;gt;
{
    private CodeBlockRenderer originalCodeBlockRenderer;
    
    public PreCodeRenderer(CodeBlockRenderer originalCodeBlockRenderer = null)
    {
        this.originalCodeBlockRenderer = originalCodeBlockRenderer ?? new CodeBlockRenderer();
    }
    public bool OutputAttributesOnPre { get; set; }
    protected override void Write(HtmlRenderer renderer, CodeBlock obj)
    {
        renderer.EnsureLine();
    
        var fencedCodeBlock = obj as FencedCodeBlock;
        if (fencedCodeBlock?.Info != null)
        {
            renderer.Write($&amp;quot;&amp;lt;pre class=\&amp;quot;brush: {fencedCodeBlock.Info}; gutter: false; toolbar: false; \&amp;quot;&amp;gt;&amp;quot;);
            renderer.EnsureLine();
            renderer.WriteLeafRawLines(obj, true, true);
            renderer.WriteLine(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);
        }
        else
        {
            originalCodeBlockRenderer.Write(renderer, obj);
        }                
    }
}
&lt;/pre&gt;
&lt;p&gt;Note that the &lt;code&gt;Info&lt;/code&gt; property of a &lt;code&gt;FencedCodeBlock&lt;/code&gt; will contain the &lt;a href="https://github.github.com/gfm/#info-string"&gt;info string&lt;/a&gt;, which is commonly used to specify the language of the code (csharp, xml, javascript, plain, go). The renderer builds a &lt;code&gt;pre&lt;/code&gt; tag that SyntaxHighlighter will know how to use. The last step, the easy step, is to add &lt;code&gt;PerCodeRenderer&lt;/code&gt; into a &lt;code&gt;MarkdownPipelineBuilder&lt;/code&gt; before telling Markdig to process your markdown.&lt;/p&gt;
</description><pubDate>Thu, 23 Jan 2020 21:12:00 Z</pubDate></item><item><guid isPermaLink="true">http://odetocode.com/blogs/scott/archive/2020/01/21/the-c-interactive-window.aspx</guid><link>http://odetocode.com/blogs/scott/archive/2020/01/21/the-c-interactive-window.aspx</link><author>scott@OdeTocode.com</author><title> The C# Interactive Window</title><description>&lt;p&gt;The C# Interactive window in VS is not the best interactive C# experience, LINQPad still has the honor, I believe, but it is a quick and convenient option for trying out a few lines of code.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/csinteractive/useinteractivewindow.jpg" class="image-center" alt="C# Interactive" /&gt;&lt;/p&gt;
</description><pubDate>Tue, 21 Jan 2020 21:12:00 Z</pubDate></item><item><guid isPermaLink="true">http://odetocode.com/blogs/scott/archive/2020/01/19/avoiding-the-debugger-with-better-logging.aspx</guid><link>http://odetocode.com/blogs/scott/archive/2020/01/19/avoiding-the-debugger-with-better-logging.aspx</link><author>scott@OdeTocode.com</author><title> Avoiding the Debugger with Better Logging</title><description>&lt;p&gt;There's actually two reasons why I tend to avoid using debuggers. The first reason is a genuine belief that debuggers encourage short term thinking and quick fixes in software. The second reason is the terrible sights and sounds I witness when I launch a debugger like the VS debugger. It is the noise of combustion and heavy gears engaging. My window arrangement shatters and the work space transforms into a day-trading app with real time graphs and event tickers. A modal dialog pops up and tells me a thread was caught inside the reverse flux capacitor and allowed to execute freely with side-effects. I don't know what any of this means or has to do with finding my off-by-one error, which only adds to my sense of fear and confusion.&lt;/p&gt;
&lt;p&gt;One way I avoid the debugger is by adding better logging to my software. The best time to think about what you need to log is when the software is misbehaving, and ideally &lt;em&gt;before&lt;/em&gt; the software misbehaves in front of strangers. Sometimes the logging becomes verbose.&lt;/p&gt;
&lt;pre class="brush: csharp; gutter: false; toolbar: false; "&gt;
logger.Verbose($&amp;quot;ABBREVIATIONS&amp;quot;);;
for (var index = ABBRSTART; index &amp;lt;= ABBREND; index++)
{
    for (var number = 0; number &amp;lt; NUMABBR; number++)
    {
        var offset = (NUMABBR * (index - 1)) + (WORDSIZE * 2);
        logger.Verbose($&amp;quot;For [{index}][{number}] the offset is {offset}&amp;quot;);

        var ppAbbreviation = machine.Memory.WordAt(Header.ABBREVIATIONS);
        logger.Verbose($&amp;quot;For [{index}][{number}] the ppointer is {ppAbbreviation:X}&amp;quot;);

        var pAbbreviation = machine.Memory.WordAddressAt(ppAbbreviation + offset);
        logger.Verbose($&amp;quot;For [{index}][{number}] the pointer is {pAbbreviation:X}&amp;quot;);

        var location = machine.Memory.SpanAt(pAbbreviation);
        var result = decoder.Decode(location).Text;

        logger.Verbose($&amp;quot;Abbreviation [{index}][{number}] : {result}&amp;quot;);
    }
}
&lt;/pre&gt;
&lt;p&gt;Verbosity works well if you categorize correctly. Again, the best proving ground for a logging strategy is when the software is misbehaving. You can learn what knobs you need to tweak and what categories work well. With Serilog, which I still prefer, you can set the category to match type names in your software, then configure the log messages you want to see using code or configuration files.&lt;/p&gt;
&lt;pre class="brush: csharp; gutter: false; toolbar: false; "&gt;
public ILogger CreateLogger()
{
    var logger = new LoggerConfiguration()
        .MinimumLevel.Warning()
        .MinimumLevel.Override(typeof(FrameCollection).FullName, LogEventLevel.Verbose)
        .MinimumLevel.Override(typeof(Machine).FullName, LogEventLevel.Verbose)
        .MinimumLevel.Override(typeof(DebugOutputStream).FullName, LogEventLevel.Verbose)
        .MinimumLevel.Override(typeof(Instruction).FullName, LogEventLevel.Verbose)
        .MinimumLevel.Override(typeof(ZStringDecoder).FullName, LogEventLevel.Verbose)
        .Enrich.FromLogContext()
        .WriteTo.File(@&amp;quot;trace.log&amp;quot;,
                      outputTemplate: &amp;quot;{SourceContext:lj}\n{Message:lj}{NewLine}{Exception}&amp;quot;)
        .CreateLogger();
    return logger;
}
&lt;/pre&gt;
&lt;h3&gt;When is Visual Studio Not an Editor?&lt;/h3&gt;
&lt;p&gt;To use logs during test runs you need to sink log events into XUnit's &lt;code&gt;ITestOutputHelper&lt;/code&gt;. The logs are available from the VS test runner by clicking on an &amp;quot;Open additional output for this result&amp;quot; link.&lt;/p&gt;
&lt;p&gt;For one particular integration style test I have, the logs can get lengthy, which leads to an amusing message from VS.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/lognodebug/openaneditor.jpg" class="image-center" alt="Open an editor" /&gt;&lt;/p&gt;
&lt;p&gt;An editor like Notepad? Am I not already &lt;em&gt;in&lt;/em&gt; a piece of software that can edit text? It's like having the GPS in a Tesla tell me I'll need to use a 1988 Oldsmobile to reach my destination.&lt;/p&gt;
</description><pubDate>Sun, 19 Jan 2020 21:12:00 Z</pubDate></item><item><guid isPermaLink="true">http://odetocode.com/blogs/scott/archive/2020/01/16/moving-complexity.aspx</guid><link>http://odetocode.com/blogs/scott/archive/2020/01/16/moving-complexity.aspx</link><author>scott@OdeTocode.com</author><title> Moving Complexity</title><description>&lt;p&gt;I always feel a sense of satisfaction when I move a piece of complexity from &lt;em&gt;outside&lt;/em&gt; an object to &lt;em&gt;inside&lt;/em&gt; an object. It doesn't need to be a large amount of code, I've learned. Every little step helps in the long run.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/encapsulation/refactordiff.jpg" class="image-center" alt="Simple Refactoring" /&gt;&lt;/p&gt;
&lt;p&gt;If I could go back 20 years and give myself some programming tips, one of those tips would certainly be this: You don't move code into an abstraction to &lt;em&gt;reuse&lt;/em&gt; the code, you move code into an abstraction to &lt;em&gt;use&lt;/em&gt; the code.&lt;/p&gt;
</description><pubDate>Thu, 16 Jan 2020 21:12:00 Z</pubDate></item><item><guid isPermaLink="true">http://odetocode.com/blogs/scott/archive/2020/01/14/nine-performance-tips-for-azure-app-services.aspx</guid><link>http://odetocode.com/blogs/scott/archive/2020/01/14/nine-performance-tips-for-azure-app-services.aspx</link><author>scott@OdeTocode.com</author><title> Nine Performance Tips for Azure App Services</title><description>&lt;p&gt;This post originally appeared on the &lt;a href="https://www.telerik.com/blogs/nine-performance-tips-for-azure-app-services"&gt;Progress Telerik blog&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;We always want the best performance from the software we deploy to Azure App Services. Not only does better performance make our customers happy, but better performance can also save us money if we &amp;quot;do more with less&amp;quot; in Azure. In this article we'll look at settings and strategies for improving the performance of web applications and web APIs running in an Azure App Service. We'll start with some easy configuration changes you can make for an instant improvement.&lt;/p&gt;
&lt;h3&gt;Enable HTTP/2&lt;/h3&gt;
&lt;p&gt;Microsoft announced support for HTTP/2 in App Services early in 2018. However, when you create a new App Service today, Azure will start you with HTTP 1.1 configured as the default protocol. HTTP/2 brings major changes to our favorite web protocol, and many of the changes aim to improve performance and reduce latency on the web. For example, header compression and binary formatting in HTTP/2 will reduce payload sizes. An even better example is the use of request pipelineing and multiplexing. These features allow for more concurrent requests using fewer network sockets and help to avoid one slow request from blocking all subsequent requests, which is a frequent problem in HTTP 1.1 that we call the &amp;quot;head-of-line&amp;quot; blocking problem.&lt;/p&gt;
&lt;p&gt;To configure your App Service to use HTTP/2 with the portal, go to Platform Settings in the Configuration blade. Here you will find a dropdown to specify the HTTP version. With 2.0 selected, any clients that support HTTP/2 will upgrade their connection automatically.
&lt;img src="/images3/appserviceperf/httpversionselection.jpg" class="image-center" alt="HTTP Version Selection in App Services" /&gt;&lt;/p&gt;
&lt;p&gt;HTTP/2 might not benefit every application, so you will want to run performance tests and stress tests to document your improvements. Here's a simple test where I used the network tools in Firefox against a page hosted in an App Service. The page references a handful of script and CSS resources, and also includes 16 images.  Each image is over 200 KB in size. First, I used the developer tools to record what happens on an App Service using HTTP 1.1. Notice how the later requests start in a blocked state (the red section of the bars). This is the dreaded &amp;quot;head-of-line blocking&amp;quot; problem where limitations on the number of connections and concurrent requests throttle the throughput between the client and the server. The client doesn't receive the final bytes for the page until 800ms after the first request starts.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/appserviceperf/http11.jpg" class="image-center" alt="HTTP 1.1 Blocking" /&gt;&lt;/p&gt;
&lt;p&gt;Next, I switched on HTTP/2 support in the App Service. I didn't need to make any other configuration changes on the client or the server. The last byte arrives in less than 500ms. We avoid blocking thanks to the improved network utilization of HTTP/2.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/appserviceperf/http20.jpg" class="image-center" alt="HTTP/2 Improvemewnts" /&gt;&lt;/p&gt;
&lt;h3&gt;Turn Off the Application Request Routing Cookie&lt;/h3&gt;
&lt;p&gt;In front of every Azure App Service is a load balancer, even if you only run a single instance of your App Service Plan. The load balancer intercepts every request heading for your app service so when you do move to multiple instances of an app service plan, the load balancer can start to balance the request load against available instances. By default, Azure will make sure clients continue reaching the same app service instance during a session, because Azure can't guarantee your application isn't storing session state in server memory. To provide this behavior the load balancer will inject a cookie into the first response to a client. This cookie is what Azure calls the Application Request Routing Cookie.&lt;/p&gt;
&lt;p&gt;If you have a stateless application and can allow the load balancer to distribute requests across instances without regard to previous requests, then turn off the routing cookie in the Configuration blade to improve performance and resiliency. You won't have requests waiting for a server restart, and when failures do happen, the load balancer can shift clients to a working instance quickly.&lt;/p&gt;
&lt;p&gt;The routing configuration is another item you'll find in the Platform Settings of the App Service Configuration blade.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/appserviceperf/arraffinity.jpg" class="image-center" alt="Turning off Instance Affinity" /&gt;&lt;/p&gt;
&lt;h3&gt;Keep the App Service Always On&lt;/h3&gt;
&lt;p&gt;If you've deployed applications into IIS in the past, you'll know that IIS will unload idle web sites after a period of inactivity. Azure App Services will also unload idle web sites. Although the unloading can free up resources for other applications that might be running on the same App Service Plan, this strategy hurts the performance of the app because the next incoming request will wait as the web application starts from nothing. Web application startup time can be notoriously slow, regardless of the technologies involved. The caches are empty, the connection pools are empty, and all requests are slower than normal as the site needs to warms up.&lt;/p&gt;
&lt;p&gt;To prevent the idle shutdown, you can set the Always On flag in the App Service Configuration blade.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/appserviceperf/alwayson.jpg" class="image-center" alt="The Always On App Service Flag" /&gt;&lt;/p&gt;
&lt;h3&gt;Use a Local Cache&lt;/h3&gt;
&lt;p&gt;By default, the file system for your App Service is mounted from Azure Storage. The good news is your file system is durable, highly available, and accessible from multiple App Service instances. The sad news is your application makes a network call every time the app touches the file system.
Some applications require the Azure Storage solution. These are the applications that write to the file system, perhaps when a user uploads a file, and they expect the file system changes to be durable, permanent, and immediately visible across all running instances of the application. Other applications might benefit from having a faster, local, read-only copy of the web site content. If this sounds like your application, or you want to run a test, then create a new App Setting for the app with a key of WEBSITE_LOCAL_CACHE_OPTION and a value of Always. You'll then have a d:\home folder pointing to a local cache on the machine and populated with a copy of your site content.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/appserviceperf/localcache.jpg" class="image-center" alt="Using a Local Cache" /&gt;&lt;/p&gt;
&lt;p&gt;Although I say the cache is read-only, the truth is you can write into the local cache folder. However, you'll lose any changes you make after an app restart. For more information on the tradeoffs involved and how the local cache works with log files, see the &lt;a href="https://docs.microsoft.com/en-us/azure/app-service/overview-local-cache"&gt;Azure App Service Local Cache overview&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Keep Your Customers Close, and Your Resources Even Closer&lt;/h3&gt;
&lt;p&gt;All the performance improvements we've looked at so far only require configuration changes. The next set of improvements require some additional infrastructure planning or restructuring, and in some cases changes to the application itself. The common theme in the next set of tips is to reduce the distance bits need to travel over the network. The speed of light is finite, so the further a bit has to travel, the longer the bit needs to reach a destination.&lt;/p&gt;
&lt;h3&gt;Co-locate Your App Service and Your Database&lt;/h3&gt;
&lt;p&gt;In Azure you assign most resources you create to a specific region. For example, when I create an App Service, I can place the service close to me in the East US region, or, if I'm on an extended visit to Europe, I can select the North Europe region. If you create multiple resources that work together closely, you'll want to place the resources together in the same region. In the past I've seen performance suffer when someone at the company accidentally places an App Service in one region and an associated Azure SQL instance in a different region. Every database query from the App Service becomes a trip across the continent, or around the world.&lt;/p&gt;
&lt;p&gt;How do you check your existing subscriptions to make sure your resources are properly co-located? Assuming you don't want to click through the portal to check manually, you can write a custom script or program, or use &lt;a href="https://docs.microsoft.com/en-us/azure/governance/policy/overview"&gt;Azure Policy&lt;/a&gt;. Azure Policy has a built-in rule to check every resource to ensure the resource location matches the location of the resource's parent resource group. All you need to do with this rule in place is make sure your associated resources are all in the same resource group. The policy definition for this audit rule looks like the following.&lt;/p&gt;
&lt;pre class="brush: javascript; gutter: false; toolbar: false; "&gt;
{
  &amp;quot;if&amp;quot;: {
    &amp;quot;field&amp;quot;: &amp;quot;location&amp;quot;,
    &amp;quot;notIn&amp;quot;: [
      &amp;quot;[resourcegroup().location]&amp;quot;,
      &amp;quot;global&amp;quot;
    ]
  },
  &amp;quot;then&amp;quot;: {
    &amp;quot;effect&amp;quot;: &amp;quot;audit&amp;quot;
  }
}
&lt;/pre&gt;
&lt;h3&gt;Keep Your App Service Close to Your Customer&lt;/h3&gt;
&lt;p&gt;If most of your customer traffic originates from a specific area of the world, it makes sense to place your resources in the Azure region closest to your customers. Of course, many of us have customers fairly distributed around the world. In this case, you might consider geo-replicating your resources across multiple Azure regions and stay close to everyone. For App Services, this means creating multiple App Service plans inside of multiple Azure data centers around the world. Then, you'll typically use a technology like Azure Traffic Manager to direct customer traffic to the closest App Service instance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;/strong&gt;: since I wrote this article, Microsoft introduced &lt;a href="https://azure.microsoft.com/en-us/services/frontdoor/"&gt;Azure Front Door&lt;/a&gt;. Front Door offers some additional capabilities that are not available from Traffic Manager. Features like SSL offload, instead failover, and DDoS protection. If you need global load balancing, you should also look at what the Front Door Service offers.&lt;/p&gt;
&lt;p&gt;Traffic Manager is a DNS based load balancer.  So, when a customer's web browser asks for the IP address associated with your application's domain, Traffic Manager can use rules you provide and other heuristics to select the IP address of a specific App Service. Traffic Manager can select the App Service with the lowest latency for a given customer request, or, you can also configure Traffic Manager to enforce geo-fencing where the load balancer sends all customers living in a specific province or country to the App Service you select. You can see the routing methods built into Traffic Manager in the Create Traffic Manager profile blade below.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/appserviceperf/trafficmanager.jpg" class="image-center" alt="Setting up a Traffic Manager Profile" /&gt;&lt;/p&gt;
&lt;p&gt;There are tradeoffs and complications introduced by Traffic Manager. It is easy to replicate stateless web applications and web services across data centers around the world, but you'll need to spend some time planning a data access strategy. Keeping one database as the only source of truth is the easiest data access approach. But, if your App Service in Australia is reading data from a database in the U.K., you might be losing the performance benefits of geo-replicating the App Service. Another option is to replicate your data, too, but much depends on your business requirements for consistency. Data replication is typically asynchronous and delayed, and your business might not be able to live with the implications of eventual consistency.&lt;/p&gt;
&lt;h3&gt;Keep Your Content Close to the Customer&lt;/h3&gt;
&lt;p&gt;Azure's content delivery network allows you to take static content from Azure Storage, or from inside your App Service, and distribute the content to edge servers around the world. Again, the idea is to reduce the distance information need to travel, and therefore reduce the latency in network requests. Static files like script files, images, CSS files, and videos, and are all good candidates for caching on the CDN edge servers. A CDN can have other benefits, too. Since your App Service doesn't need to spend time or bandwidth serving files cached on a CDN, it has more resources available to produce your dynamic content.&lt;/p&gt;
&lt;p&gt;When setting up a CDN profile in Azure, you can select a pricing plan with the features you need from a set of providers that includes Microsoft, Verizon, and Akamai.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/appserviceperf/cdn.jpg" class="image-center" alt="Setting up a CDN Profile" /&gt;&lt;/p&gt;
&lt;h3&gt;Keep Your Apps Together&lt;/h3&gt;
&lt;p&gt;Today's architecture fashion is to decompose systems into a set of microservices. These microservices need to communicate with each other to process customer requests. Just like keeping your application and database close together can benefit performance, keeping your microservices close to each other can benefit performance, too.&lt;/p&gt;
&lt;p&gt;With App Services, remember that multiple services can live on the same App Service Plan. Think of the plan like a virtual machine dedicated to the role of a web server. You can place as many applications on the web server as you like, and keeping services together can reduce network latency. However, keep in mind that having too many services on the same machine can stretch resources thin. It will take some experimentation and testing to figure out the best distribution of services, the ideal size of the App Service Plans, and the number of instances you need to handle all your customer requests.&lt;/p&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;We've looked at several strategies we can use to improve the performance of web applications and web APIs we've deployed to Azure App Services. Just remember that your first step before trying one of these strategies should be to measure your application performance and obtain a good baseline number. Not every strategy in this article will benefit every application. Starting with baseline performance numbers will allow you to compare strategies and see which ones are the most effective for your application.&lt;/p&gt;
</description><pubDate>Tue, 14 Jan 2020 21:12:00 Z</pubDate></item><item><guid isPermaLink="true">http://odetocode.com/blogs/scott/archive/2020/01/12/solving-access-denied-in-crypto-machine-keys.aspx</guid><link>http://odetocode.com/blogs/scott/archive/2020/01/12/solving-access-denied-in-crypto-machine-keys.aspx</link><author>scott@OdeTocode.com</author><title> Solving Access Denied in Crypto Machine Keys</title><description>&lt;p&gt;Previously on OdeToCode I posted about &lt;a href="https://odetocode.com/blogs/scott/archive/2020/01/09/tracking-down-an-aspnetcore-build-errors.aspx"&gt;tracking down an AspNetCore build error&lt;/a&gt;. Once I realized the access denied message came from the &lt;code&gt;ProgramData\Microsoft\Crypto\RSA\MachineKeys&lt;/code&gt; folder, I went searching for reasons why this might happen, and to find possible solutions. Here are some observations based on my research.&lt;/p&gt;
&lt;h3&gt;For Many Developers, Security Only Gets In The Way&lt;/h3&gt;
&lt;p&gt;During my NodeJS days I developed a strange vocalization tic. The tic manifested itself every time I found an answer on Stack Overflow suggesting &lt;code&gt;sudo&lt;/code&gt; as the answer to a security problem. As in, &amp;quot;just use &lt;code&gt;sudo npm install&lt;/code&gt; and you'll be done&amp;quot;. The tic resurfaced during my research for access denied messages around &lt;code&gt;MachineKeys&lt;/code&gt;.  From GitHub issues to Stack Overflow and Microsoft support forms, nearly everyone suggests the developer give themselves &amp;quot;Full Control&amp;quot; of the folder and consider the job finished. There is no root cause analysis or thought given to the idea that although &amp;quot;Full Control&amp;quot; might work, it still might be &lt;em&gt;the wrong answer&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dotnet/corefx/issues/3784#issuecomment-404795417"&gt;Here is an example&lt;/a&gt; where the &lt;code&gt;Full Control&lt;/code&gt; solution is encouraged, favorited, and linked to from &lt;a href="https://github.com/aspnet/AspNetCore/issues/17534"&gt;other issues&lt;/a&gt; that encourage developers to use the same work around.&lt;/p&gt;
&lt;p&gt;Given that the &lt;code&gt;MachineKeys&lt;/code&gt; folder is not in an obvious location for storing user specific data, and given the folder name itself implies the contents are for &lt;em&gt;all the users on the machine&lt;/em&gt;, and given the folder &lt;em&gt;can contain the private keys&lt;/em&gt; of cryptographic key pairs, I immediately dismissed any answer suggesting full control or taking ownership of the folder.&lt;/p&gt;
&lt;h3&gt;Not Many People Have This Problem&lt;/h3&gt;
&lt;p&gt;I also realized during my research that not many people run into this specific problem. The few I found who did some real analysis were pointing fingers at Windows updates. And indeed, this particular machine accepts insider builds of Windows. For those not brave enough to use insider builds, I can say that insider updates are both frequent and are similar to playing with commercial grade fireworks. It's fun when they work, but you are only one misfire away from burning down your house.&lt;/p&gt;
&lt;p&gt;I began to suspect my access denied error was a problem with my Windows configuration, and not with the AspNetCore builds or .NET tools. I tried the build for the same commit on &lt;em&gt;two&lt;/em&gt; other machines, and both were successful.&lt;/p&gt;
&lt;p&gt;Two out of three is a consensus in distributed computing, so now I was certain I had a configuration problem, probably caused by Windows Update incorrectly applying inherited security attributes. I started looking for how to fix the problem.&lt;/p&gt;
&lt;h3&gt;The Most Unhelpful Support Document in the History of Microsoft Support Documents&lt;/h3&gt;
&lt;p&gt;In my research I came across the support document titled &amp;quot;&lt;a href="https://support.microsoft.com/en-us/help/278381/default-permissions-for-the-machinekeys-folders"&gt;Default permissions for the MachineKeys folders&lt;/a&gt;&amp;quot; A promising title. There is even some rousing prose in the summary section implying how MachineKeys is a special folder.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The MachineKeys folder stores certificate pair keys for &lt;em&gt;both the computer and users&lt;/em&gt;. ... &lt;em&gt;The default permissions on the folder may be misleading&lt;/em&gt; when you attempt to determine the minimum permissions that are necessary for proper installation and the accessing of certificates.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I was waiting for the document to rattle off the exact permissions setup, but after an auspicious opening the document spirals into darkness with language and terms from Windows Server 2003. It is true that &lt;code&gt;MachineKeys&lt;/code&gt; requires special permissions, but I found it easier to compare settings on two different computers than to translate a 16-year old support document.&lt;/p&gt;
&lt;h3&gt;My Solution&lt;/h3&gt;
&lt;p&gt;On my problem machine, the &lt;code&gt;MachineKeys&lt;/code&gt; folder inherited permissions from the parent folder. These permissions gave &lt;code&gt;Everyone&lt;/code&gt; read permissions on the folder and all files inside, and this is why &lt;code&gt;al.exe&lt;/code&gt; would fail with an access denied error. Given that the folder can contain private keys from different users, these setting are also dangerous. The folder shouldn't inherit permissions, we need to set special permissions.&lt;/p&gt;
&lt;p&gt;The first step in fixing the mess through the GUI is to right-click the folder, go to Properties, go to the Security tab, &lt;em&gt;click Advanced at the bottom of the dialog&lt;/em&gt;, click &amp;quot;Disable inheritance&amp;quot;, and then &amp;quot;Remove all inherited permissions from this object&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/aspnetcorebuild/disableinheritance.jpg" class="image-center" alt="disable inheritance" /&gt;&lt;/p&gt;
&lt;p&gt;Now there is a clean slate to work with. Use the Add button to create a new permissions entry selecting &lt;code&gt;Everyone&lt;/code&gt; as the principal. In the Basic Permissions group, select only Read and Write. Make sure &amp;quot;Applies to&amp;quot; has &amp;quot;This folder only&amp;quot; selected.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/aspnetcorebuild/specialperms.jpg" class="image-center" alt="special permissions" /&gt;&lt;/p&gt;
&lt;p&gt;Also add the Administrators group, and allow Full control for &lt;strong&gt;this folder only&lt;/strong&gt;. You'll know the right settings are in place when the regular security tab shows &amp;quot;Special Permissions&amp;quot; for both the &lt;code&gt;Everyone&lt;/code&gt; group and &lt;code&gt;Administrators&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images3/aspnetcorebuild/special2.jpg" class="image-center" alt="Correctness" /&gt;&lt;/p&gt;
&lt;p&gt;These special permissions allow average users to write into the folder, and then read and delete a file they write. But, they cannot list the contents of the folder or touch files from other users (although admins will have the ability to delete files from other users).&lt;/p&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;Solving security problems requires care and patience. But, I feel better having a working AspNetCore build without compromising the  security settings on my machine.&lt;/p&gt;
</description><pubDate>Sun, 12 Jan 2020 21:12:00 Z</pubDate></item><item><guid isPermaLink="true">http://odetocode.com/blogs/scott/archive/2020/01/09/tracking-down-an-aspnetcore-build-errors.aspx</guid><link>http://odetocode.com/blogs/scott/archive/2020/01/09/tracking-down-an-aspnetcore-build-errors.aspx</link><author>scott@OdeTocode.com</author><title> Tracking Down an AspNetCore Build Error</title><description>&lt;p&gt;Building AspNet Core from source is &lt;a href="https://github.com/aspnet/AspNetCore/blob/master/docs/BuildFromSource.md"&gt;straightforward&lt;/a&gt;, unless, like me, you run into strange errors. My first build compiled over 600 projects successfully, but ended up failing and showing the following message.&lt;/p&gt;
&lt;pre class="brush: text; gutter: false; toolbar: false; "&gt;Build FAILED.

ALINK : error AL1078: Error signing assembly -- Access is denied. [...\LocalizationSample.csproj]
ALINK : error AL1078: Error signing assembly -- Access is denied. [...\LocalizationWebsite.csproj]
ALINK : error AL1078: Error signing assembly -- Access is denied. [...\RazorPagesWebSite.csproj]
ALINK : error AL1078: Error signing assembly -- Access is denied. [...\BasicTestApp.csproj]
    0 Warning(s)
    4 Error(s)
&lt;/pre&gt;
&lt;p&gt;I first noticed that all four failures were because of &lt;code&gt;AL.exe&lt;/code&gt;, a.k.a &lt;a href="https://docs.microsoft.com/en-us/dotnet/framework/tools/al-exe-assembly-linker"&gt;ALINK&lt;/a&gt;, the little known assembly linker that ships with .NET. Compared to a linker in the world of C++, the .NET linker is an obscure tool that is rarely seen unless a build needs to work with satellite assemblies generated from .resx files, or sign an assembly with a public/private key pair.&lt;/p&gt;
&lt;p&gt;The next bit I noticed was that the first two projects involved localization, so I was sure the build errors were some sort of problem with satellite assemblies or resource files. Unfortunately, the error message &lt;code&gt;access is denied&lt;/code&gt; is short on helpful details. I went looking for obvious problems and verified that projects existed in the correct directories, and that no files were missing.&lt;/p&gt;
&lt;h3&gt;Generating a Build Log&lt;/h3&gt;
&lt;p&gt;In need of more information, I decided to create a detailed log of of my AspNetCore build. Building all of AspNetCore requires running a build script, one of &lt;code&gt;build.cmd&lt;/code&gt;, &lt;code&gt;build.ps1&lt;/code&gt;, or &lt;code&gt;build.sh&lt;/code&gt;, depending on your OS and mood. All of these scripts accept parameters. For example, &lt;code&gt;build -BuildNative&lt;/code&gt; will build the native C/C++ projects, which includes the AspNetCoreV2 IIS module. The default is to only &lt;code&gt;-BuildManaged&lt;/code&gt;. You can also thunk MSBuild parameters through the build script, so I used the following to create a binary log of detailed verbosity, and avoided writing to the console to save time:&lt;/p&gt;
&lt;pre class="brush: text; gutter: false; toolbar: false; "&gt;build.cmd -bl -verbosity:detailed -noconsolelogger  
&lt;/pre&gt;
&lt;p&gt;All AspNetCore build output goes into an &lt;code&gt;artifacts&lt;/code&gt; folder, including the binary log. To view the binary log I use &lt;a href="https://github.com/KirillOsenkov/MSBuildStructuredLog/issues"&gt;MSBuildStructureLog&lt;/a&gt;, a tool I've &lt;a href="https://odetocode.com/blogs/scott/archive/2019/03/05/debugging-net-builds-with-the-msbuild-structured-log-viewer.aspx"&gt;written about previously&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A word of warning - AspNetCore build logs are not the kind of logs that you can pack into an overnight bag and sling over your shoulder. If you want to open and view the logs I recommend a machine with at least 16GB of RAM, but 32 is better. The machine needs to handle a process with 10+ GB committed or else be sucking mud.&lt;/p&gt;
&lt;p&gt;&lt;img class="image-center" src="https://odetocode.com/images3/aspnetcorebuild/buildlogcommit.jpg" alt="commit" /&gt;&lt;/p&gt;
&lt;p&gt;Once the build log loads, it is easy to find build errors.&lt;/p&gt;
&lt;p&gt;&lt;img class="image-center" src="https://odetocode.com/images3/aspnetcorebuild/buildlogaspnetcore.jpg" alt="build log aspnetcore" /&gt;&lt;/p&gt;
&lt;p&gt;The command line for each of the 4 build errors looked like the following:&lt;/p&gt;
&lt;pre class="brush: text; gutter: false; toolbar: false; "&gt;CommandLineArguments = ...\al.exe /culture:es-ES /delaysign- /keyfile: and so on...

&lt;/pre&gt;
&lt;p&gt;I tried the command line myself, and sure enough, there is an access denied error.&lt;/p&gt;
&lt;p&gt;&lt;img class="image-center" src="https://odetocode.com/images3/aspnetcorebuild/accessdenied.png" alt="access denied" /&gt;&lt;/p&gt;
&lt;p&gt;The good news, I thought to myself, was that I can easily reproduce the build error without running the entire build. The bad news, I thought to myself, was that I still don't know what resource is denying access. Time to bring in more tools.&lt;/p&gt;
&lt;h3&gt;Tracking Down Access Denied with SysInternals Process Monitor&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://docs.microsoft.com/en-us/sysinternals/downloads/sysinternals-suite"&gt;Sysinternals Suite&lt;/a&gt; has been invaluable for debugging over the years. &lt;a href="https://docs.microsoft.com/en-us/sysinternals/downloads/procmon"&gt;Process Monitor&lt;/a&gt; in particular is the first tool that comes to mind when I need to track down file system activity.&lt;/p&gt;
&lt;p&gt;If you've ever worked with procmon, you'll know the tool can capture an overwhelming amount of data. The first thing to do is to add one or more filters to the capture. In the screen shot below I am only showing file system activity for any process named &lt;code&gt;al.exe&lt;/code&gt;. With the filter in place I can execute the command line, and sure enough, procmon shows the access denied error.&lt;/p&gt;
&lt;p&gt;&lt;img class="image-center" src="https://odetocode.com/images3/aspnetcorebuild/procmonlexe.jpg" alt="Process Monitor" /&gt;&lt;/p&gt;
&lt;p&gt;The access denied message occurs on a file inside the &lt;code&gt;ProgramData\Microsoft\Crypto\RSA\MachineKeys&lt;/code&gt; folder. It appears to me, based on my limited knowledge of the Windows crypto API, that &lt;code&gt;al.exe&lt;/code&gt; is trying to write an ephemeral private key into the machine keys folder, but my user account doesn't have permission to create the file.&lt;/p&gt;
&lt;p&gt;Now I know why the build is failing, but there is no clear solution to fix the problem.&lt;/p&gt;
&lt;h3&gt;What to do?&lt;/h3&gt;
&lt;p&gt;Stay tuned for the next blog post where I will reveal the unexciting solution to this problem in a stupendously boring fashion. &lt;em&gt;Sneak preview&lt;/em&gt;: the solution does &lt;em&gt;not&lt;/em&gt; involve &lt;code&gt;sudo&lt;/code&gt; or changing folder permissions to give my account full control...&lt;/p&gt;</description><pubDate>Thu, 09 Jan 2020 21:12:00 Z</pubDate></item><item><guid isPermaLink="true">http://odetocode.com/blogs/scott/archive/2020/01/08/beautiful-binary-literals.aspx</guid><link>http://odetocode.com/blogs/scott/archive/2020/01/08/beautiful-binary-literals.aspx</link><author>scott@OdeTocode.com</author><title> Beautiful Binary Literals</title><description>&lt;p&gt;Finally! I've used binary literals in real code. This feature, and the digits separator have been good additions to the C# language.&lt;/p&gt;
&lt;pre class="brush: csharp; gutter: false; toolbar: false; "&gt;
public static byte[] BreakIntoTwos(byte value)
{
    var result = new byte[4];

    result[0] = (byte)((value &amp;amp; 0b1100_0000) &amp;gt;&amp;gt; 6);
    result[1] = (byte)((value &amp;amp; 0b0011_0000) &amp;gt;&amp;gt; 4);
    result[2] = (byte)((value &amp;amp; 0b0000_1100) &amp;gt;&amp;gt; 2);
    result[3] = (byte) (value &amp;amp; 0b0000_0011);

    return result;
}
&lt;/pre&gt;
</description><pubDate>Wed, 08 Jan 2020 21:12:00 Z</pubDate></item></channel></rss>