<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Dan Schnau's Blog</title>
  <subtitle>A blog from Dan Schnau.</subtitle>
  <link href="https://danschnau.com/feed" rel="self" />
  <link href="https://www.danschnau.com" />
  <id>urn:uuid:945c0606-0f88-48e8-b9d0-1472e0e37264</id>
  <updated>2026-04-15T19:21:44.287Z</updated>
  <entry>
    <title>Some Mental Health Notes</title>
    <link href="https://danschnau.com/blog/some_mental_health_notes_internal_family_systems" />
    <id>urn:uuid:dbccf2f9-96e6-496e-862e-0e69eeee39d4</id>
    <updated>2026-04-15T19:21:44.287Z</updated>
    <content type="html">&lt;p&gt;My therapist applies &lt;a href=""&gt;Internal Family Systems&lt;/a&gt; as part of her therapy.&lt;/p&gt;
&lt;p&gt;At the end of our discussion, we reviewed the process as she describes it.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Be Curious&lt;/strong&gt; about the part that is causing problems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Communicate&lt;/strong&gt; understanding about that part.&lt;/li&gt;
&lt;li&gt;Show &lt;strong&gt;Appreciation&lt;/strong&gt; for that part. It is in fact trying to help, just in a maladaptive way.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Negotiate&lt;/strong&gt;. Find a way to let the part relax its grip on your psyche by giving it some of what it needs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reassure&lt;/strong&gt; this part. Regularly check in with yourself, and see that you haven't broken your negotiated deal.&lt;/li&gt;
&lt;/ol&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Kagi Search</title>
    <link href="https://danschnau.com/blog/kagi_search" />
    <id>urn:uuid:5ad9f248-a506-45e9-8473-496fa08c2220</id>
    <updated>2026-04-09T19:04:41.98Z</updated>
    <content type="html">&lt;p&gt;A couple days ago I signed up for &lt;a href="https://kagi.com"&gt;Kagi search&lt;/a&gt; on a trial. Today I hit that trials' cap and happily ponied up the $5/month for ad-free search.&lt;/p&gt;
&lt;p&gt;Even with ad-blocker extensions, I found the help from Kagi search to be superior.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/572eff19-8cba-4297-b9db-9f6d99f42b78.png" alt="image.png" /&gt;&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>focus</title>
    <link href="https://danschnau.com/blog/focus" />
    <id>urn:uuid:729062e8-8166-47da-827b-a4df8c3026d0</id>
    <updated>2026-04-08T16:49:00.89Z</updated>
    <content type="html">&lt;p&gt;Man, it's hard to focus in this day and age.&lt;/p&gt;
&lt;p&gt;I noticed yesterday that when I was having a hard time focusing on what I needed to do (some tasks for work and meal planning for the family), I typed out my thinking. As I typed, my thinking became slower and more direct. I got the tasks done and I prepped that day and the following day's meals.&lt;/p&gt;
&lt;p&gt;It's no secret that writing is good for this sort of thing. I'd like to do more of it.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>No, Sam Altman Cannot Be Trusted</title>
    <link href="https://danschnau.com/blog/sam_altman_cannot_be_trusted" />
    <id>urn:uuid:db9b8918-87f0-474b-9de3-d9172af1fef3</id>
    <updated>2026-04-07T18:33:50.55Z</updated>
    <content type="html">&lt;p&gt;I've seen a headline come up recently for a New Yorker article: &lt;a href="https://www.newyorker.com/newsletter/the-daily/can-sam-altman-be-trusted"&gt;Can Sam Altman Be Trusted?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I haven't read the article, but, I can answer the question, and the answer is no.&lt;/p&gt;
&lt;p&gt;This is a guy who justified the energy usage from training Large Language Models &lt;a href="https://www.businessinsider.com/sam-altman-ai-energy-use-training-human-water-chatgpt-2026-2"&gt;by comparing it to the energy needed to raise a human being&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One of the things that is always unfair in this comparison is people talk about how much energy it takes to train an AI model relative to how much it costs a human to do one inference query. But it also takes a lot of energy to train a human.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;It takes a lot of energy to train a human. It takes, like, 20 years of life, and all of the food you eat during that time before you get smart.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You cannot trust a for-profit company to begin with (they're for-profit, not for-people), and you &lt;em&gt;especially&lt;/em&gt; can't trust one with a leader who says fucked up stuff like that.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Trump Needs To Go</title>
    <link href="https://danschnau.com/blog/trump_needs_to_go" />
    <id>urn:uuid:5b130235-1da9-4d08-acf3-a62aed430984</id>
    <updated>2026-04-07T18:14:43.807Z</updated>
    <content type="html">&lt;p&gt;Today The President Of The United States Of America Threatened To commit Genocide. The president &lt;a href="https://truthsocial.com/@realDonaldTrump/posts/116363336033995961"&gt;wrote the following&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A whole civilization will die tonight, never to be brought back again. I don’t want that to happen, but it probably will. However, now that we have Complete and Total Regime Change, where different, smarter, and less radicalized minds prevail, maybe something revolutionarily wonderful can happen, WHO KNOWS? We will find out tonight, one of the most important moments in the long and complex history of the World. 47 years of extortion, corruption, and death, will finally end. God Bless the Great People of Iran!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Twice I've voted against Trump. In 2020 I volunteered my time to work the phones for the democratic party.&lt;/p&gt;
&lt;p&gt;There are a pocket of trumpers that meet on Sunday afternoons near my home and wave their flags. People honk their horns in support.&lt;/p&gt;
&lt;p&gt;I have made it a habit to do my grocery shopping on Sunday afternoons so that I may have a chance to yell expletives at these Trump supporters.&lt;/p&gt;
&lt;p&gt;It's long since past time this pedophile, warmonger, scammer, absolute slime of a human being is removed from power.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Learning some graphics from scratch</title>
    <link href="https://danschnau.com/blog/learning_some_graphics_from_scratch" />
    <id>urn:uuid:4ea3c757-36e7-4848-82da-4a9e70349306</id>
    <updated>2026-03-14T02:19:56.62Z</updated>
    <content type="html">&lt;p&gt;I've taken interest in learning some low-level graphics programming. I did get into software originally because I wanted to make video games. I just got lazy about it in college.&lt;/p&gt;
&lt;p&gt;Anyway. I learned a bit about &lt;a href="https://opengl.org"&gt;OpenGL&lt;/a&gt; &lt;a href="https://vulkan.org"&gt;Vulkan&lt;/a&gt;, and &lt;a href="https://libsdl.org"&gt;SDL&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I've gotten started working through the examples on the LibSDL site.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Re: Service idea: roll-your-own streaming subscription (with artist royalties!)</title>
    <link href="https://danschnau.com/blog/re_spotify_diy" />
    <id>urn:uuid:91664977-d73a-424f-910b-6275ca105565</id>
    <updated>2026-03-13T18:39:14.213Z</updated>
    <content type="html">&lt;p&gt;I came across this post describing a &amp;quot;DIY Spotify&amp;quot; where basically instead of paying Spotify to keep my money and not pay artists, I pay artists directly with my monthly music-spend allocation. It sounds really cool.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://laze.net/2026/03/02/service-idea-rollyourown-streaming-subscription.html"&gt;https://laze.net/2026/03/02/service-idea-rollyourown-streaming-subscription.html&lt;/a&gt;&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Coding With Claude - Install LSP (Language Server Protocol) Support</title>
    <link href="https://danschnau.com/blog/coding_with_claude_install_lsp" />
    <id>urn:uuid:7b9dbc3f-4041-4de2-853a-629ef61bd469</id>
    <updated>2026-02-19T17:47:37.163Z</updated>
    <content type="html">&lt;p&gt;Reading the docs for &lt;a href="https://code.claude.com/docs/en/discover-plugins#code-intelligence"&gt;Code Intelligence&lt;/a&gt; I learned you can make Claude run way faster.&lt;/p&gt;
&lt;p&gt;By default, when Claude is writing code, it's using tools like &lt;code&gt;grep&lt;/code&gt; and running  builds to find errors and explore the codebase.&lt;/p&gt;
&lt;p&gt;But as a human writing code, we don't work like that! Usually, at least. We have the luxury of squiggily lines under errors, and go-to-definition to speed up development. Claude can benefit from the same.&lt;/p&gt;
&lt;p&gt;LSP integrations are implemented as plugins with claude, so installing them is a matter of a &lt;code&gt;/plugin&lt;/code&gt; command.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/plugin install csharp-lsp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/c8cf2bd2-26cb-4f16-aebd-3f54f9c057c2.png" alt="image.png" /&gt;&lt;/p&gt;
&lt;p&gt;Adding LSP support for Claude will both allow Claude to run faster and more token-efficient.&lt;/p&gt;
&lt;p&gt;You can find a list of out-of-the-box supported languages &lt;a href="https://code.claude.com/docs/en/discover-plugins#code-intelligence"&gt;here&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Artificial Intelligence Predictions for 2026</title>
    <link href="https://danschnau.com/blog/artificial_intelligence_predictions_for_2026" />
    <id>urn:uuid:f43013e1-dd9c-4490-9a7d-e7ef30f417b8</id>
    <updated>2025-11-21T00:12:53.1Z</updated>
    <content type="html">&lt;p&gt;My prediction for AI in 2026 is that it's just gonna keep growing. I don't think it's some kind of bubble. Some of the things people are trying with LLMs won't work out, but plenty will.&lt;/p&gt;
&lt;h2&gt;We Are In The Trough Of Disillusionment&lt;/h2&gt;
&lt;p&gt;We are here:
&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/trough.png" alt="trough" /&gt;&lt;/p&gt;
&lt;h2&gt;LLMs Are Not Your Friend&lt;/h2&gt;
&lt;p&gt;LLMs, the tech that is now synonymous with AI, is a technology for figuring out the next best thing to say. I think of it as a digital &amp;quot;yes man&amp;quot;. You can't talk to them like they're your friend and get a real relationship like it's with a human being. The AI Girlfriend apps are way creepy.&lt;/p&gt;
&lt;h2&gt;AI Will Not Replace Software Engineers&lt;/h2&gt;
&lt;p&gt;Have you ever heard the saying that there are two parts to every software project? The first 90%, and the second 90%? AI is great at getting that first 90% done, and also making that second 90% even more arduous.&lt;/p&gt;
&lt;p&gt;I've seen enough AI code slop to be confident that my software engineering skills will be needed for some time, even with lots of fun &amp;quot;vibe coded&amp;quot; software getting churned out. I'm interested to see how much the &amp;quot;get your vibe-coded app ready for production&amp;quot; industry grows.&lt;/p&gt;
&lt;h2&gt;AI Is Really Helpful For Writing Code&lt;/h2&gt;
&lt;p&gt;Having said that ChatGPT isn't going to replace software engineers, I also want to share that I find ChatGPT very useful for software engineering. Recently, I needed to write a function that would return a TimeSpan until the next time a job should run, and there were very specific business rules around it. I detailed those rules to ChatGPT and it gave me a function that worked correctly. After reviewing and testing the code as I would any other software going in to a production system, it became &amp;quot;my code&amp;quot; rather than &amp;quot;vibe code&amp;quot;. And it made my life a bit easier.&lt;/p&gt;
&lt;h2&gt;You Can't Really Trust What AI Business People Or News Articles Say&lt;/h2&gt;
&lt;p&gt;Because there is so damn much money invested in companies with Large Language Model technology, the leaders of those businesses need to keep their investors happy. At least, happy with their investments. As such, any shortcomings or problems will be hidden as much as possible, and any successes will be hyped up. Because this is the next exciting big thing, news articles are going to hype things up as much as they can, in both positive and negative perspectives, because that gets the clicks and the traffic. That's the world we live in.&lt;/p&gt;
&lt;p&gt;There's usually some truth to the matters at hand, but more often than not, things are made out to be more exciting than they really are.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Some News&lt;/h2&gt;
&lt;p&gt;Just for reference of recent events when I look back at this.&lt;/p&gt;
&lt;p&gt;Anthropic, owners of the Claude programming IDE, announced on November 11, 2025 that it would be investing &lt;a href="https://www.anthropic.com/news/anthropic-invests-50-billion-in-american-ai-infrastructure"&gt;50 Billion Dollars&lt;/a&gt; in AI infrastructure.&lt;/p&gt;
&lt;p&gt;Nvidia, a computer chip maker, announced on September 22, 2025 that it would be investing &lt;a href="https://openai.com/index/openai-nvidia-systems-partnership/"&gt;Up To 100 Billion Dollars&lt;/a&gt; in OpenAI.&lt;/p&gt;
&lt;p&gt;OpenAI, the company that owns ChatGPT, has reached a valuation of &lt;a href="https://www.reuters.com/technology/openai-hits-500-billion-valuation-after-share-sale-source-says-2025-10-02/"&gt;500 Billion Dollars&lt;/a&gt; as of this news from October 2, 2025.&lt;/p&gt;
&lt;p&gt;I saw a bunch of headlines about how the Big Short guy had bet against AI markets, but now I can't find any articles with specific numbers. It leads me to think that those headlines were more click-bait than real evidence that we're in an AI bubble where a bunch of money is going to evaporate one day. &lt;a href="https://www.cnbc.com/2025/11/04/karp-big-short-burry-palantir-nvidia.html"&gt;It looks to me more like a couple of people just arguing on the internet&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Gradual Migration: Moving from MVC to Blazor Without Breaking Everything</title>
    <link href="https://danschnau.com/blog/moving_from_mvc_to_blazor_without_breaking_everything" />
    <id>urn:uuid:57443a47-56f2-47c4-9807-c2143cc302de</id>
    <updated>2025-03-26T20:29:49.167Z</updated>
    <content type="html">&lt;h3&gt;Introduction: Why Migrate to Blazor?&lt;/h3&gt;
&lt;p&gt;Blazor brings the power of modern web development to .NET developers, enabling rich interactivity using just C#. But most existing enterprise applications still rely on ASP.NET MVC. Rewriting an entire application overnight isn’t realistic, so this post outlines how to move from MVC to Blazor gradually, without breaking everything in the process.&lt;/p&gt;
&lt;h3&gt;Step-by-Step Migration Strategy&lt;/h3&gt;
&lt;h4&gt;1. Start with Shared Models and Services&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Refactor your business logic into a service:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public interface IProductService
{
    Product GetProductById(int id);
}

public class ProductService : IProductService
{
    public Product GetProductById(int id)
    {
        // Fetch from DB or cache
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then inject and use it in both a controller and a Blazor component:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In MVC Controller:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class ProductController : Controller
{
    private readonly IProductService _productService;

    public ProductController(IProductService productService)
    {
        _productService = productService;
    }

    public IActionResult Details(int id)
    {
        var product = _productService.GetProductById(id);
        return View(product);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;In Blazor Component:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@inject IProductService ProductService

@code {
    [Parameter] public int Id { get; set; }
    private Product product;

    protected override void OnInitialized()
    {
        product = ProductService.GetProductById(Id);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. Use Blazor Components in Razor Views (Where Appropriate)&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Embed a Blazor component into an MVC &lt;code&gt;.cshtml&lt;/code&gt; view:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-html"&gt;&amp;lt;component type=&amp;quot;typeof(MyApp.Components.MyWidget)&amp;quot; render-mode=&amp;quot;ServerPrerendered&amp;quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure the &lt;code&gt;_Imports.razor&lt;/code&gt; and service registration for Blazor Server are correctly configured.&lt;/p&gt;
&lt;h4&gt;3. Isolate Blazor Areas for New Features&lt;/h4&gt;
&lt;p&gt;For any new feature, consider building it in Blazor from the start. You can keep MVC for legacy parts while introducing Blazor for greenfield work. Use consistent routing to make the experience seamless.&lt;/p&gt;
&lt;h4&gt;4. Gradually Replace High-Interaction Pages&lt;/h4&gt;
&lt;p&gt;Pages that involve lots of JavaScript, dynamic forms, or live updates are prime candidates for Blazor. These benefit most from Blazor’s signal-driven updates and component model.&lt;/p&gt;
&lt;h4&gt;5. Introduce a Hybrid Navigation Strategy&lt;/h4&gt;
&lt;p&gt;A quick note: this kind of hybrid approach—mixing Blazor and MVC—is different from &amp;quot;Blazor Hybrid,&amp;quot; which refers to running Blazor components in both client-side and server-side contexts (often used in desktop scenarios with .NET MAUI). Here, we're talking about a web-focused strategy that blends traditional MVC routing with Blazor navigation.&lt;/p&gt;
&lt;p&gt;Mix traditional MVC navigation (&lt;code&gt;RedirectToAction&lt;/code&gt;, &lt;code&gt;&amp;lt;a href&amp;gt;&lt;/code&gt;) with Blazor’s &lt;code&gt;&amp;lt;NavLink&amp;gt;&lt;/code&gt; carefully. Keep navigation consistent with shared layout components and links that bridge both worlds.&lt;/p&gt;
&lt;h4&gt;6. Manage Authentication and Layouts Across Both Frameworks&lt;/h4&gt;
&lt;p&gt;Use shared layout files, partials, or component libraries where possible. For authentication, use the same middleware and claims-based identity so users don’t have to reauthenticate between sections.&lt;/p&gt;
&lt;h3&gt;7. Remove Unused Features&lt;/h3&gt;
&lt;p&gt;Migration is also a great opportunity to clean house. As you assess which parts of your MVC app to rebuild in Blazor, take note of outdated or unused features. If certain pages, reports, or admin tools haven’t been touched in months (or years), consider whether they need to be migrated at all. Trimming unused code keeps your new Blazor app leaner, easier to maintain, and more focused on what users actually need.&lt;/p&gt;
&lt;h3&gt;Final Thoughts: Tips, Pitfalls, and When to Go All-In&lt;/h3&gt;
&lt;p&gt;A full migration to Blazor is tempting—but not always necessary. Start small, prioritize user experience, and lean on shared services to minimize duplication. Avoid trying to copy MVC patterns directly into Blazor; embrace the component model instead. When your team is comfortable and the codebase is ready, a full move may make sense—but until then, hybrid works just fine.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Execute on a simple idea: building LinkDisguiser.com</title>
    <link href="https://danschnau.com/blog/execute_on_a_simple_idea_linkdisguiser_com" />
    <id>urn:uuid:cbd9f6e3-9247-46b6-b6c9-e68289dfd052</id>
    <updated>2025-03-29T22:17:10.123Z</updated>
    <content type="html">&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;LinkDisguiser.com is a project I built to explore the capabilities of &lt;strong&gt;Azure Table Storage&lt;/strong&gt; while solving a practical problem—creating a scalable and cost-efficient way to manage and disguise links. This post will walk through the inspiration behind the project, the technical decisions involved, the challenges faced, and the future enhancements I have planned.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/lkdrxyz_webapi.jpeg" alt="sketch of the lkdr.xyz web api" /&gt;&lt;/p&gt;
&lt;h3&gt;The Inspiration Behind LinkDisguiser.com&lt;/h3&gt;
&lt;p&gt;I wanted to build a project that leveraged the power of &lt;strong&gt;Azure Table Storage&lt;/strong&gt; to handle large-scale data efficiently while remaining cost-effective. The challenge was to design a system that could store and retrieve short links quickly using &lt;strong&gt;partitionKey&lt;/strong&gt; and &lt;strong&gt;rowKey&lt;/strong&gt;, taking advantage of Azure's highly optimized querying. This project was an opportunity to apply and test what I had learned about scalable, cloud-based storage solutions in a real-world application. A similar approach is used by sites like &lt;strong&gt;haveibeenpwned&lt;/strong&gt;, which also rely on fast, partitioned lookups to deliver high-performance data access at scale.&lt;/p&gt;
&lt;h3&gt;Technical Decisions and Architecture&lt;/h3&gt;
&lt;p&gt;The backbone of LinkDisguiser.com is &lt;strong&gt;Azure Table Storage&lt;/strong&gt;, which is used to store link redirects and tracking data. This choice was driven by its low cost, scalability, and fast access when queries are optimized around the &lt;code&gt;partitionKey&lt;/code&gt; and &lt;code&gt;rowKey&lt;/code&gt; structure. These keys allow efficient, targeted lookups that help the system scale with minimal performance trade-offs.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/table_storage.jpeg" alt="sketch of the logo for Azure Table Storage" /&gt;&lt;/p&gt;
&lt;p&gt;In addition to Azure Table Storage, the architecture includes a minimal C# API that powers the short domain &lt;code&gt;lkdr.xyz&lt;/code&gt;, hosted via &lt;strong&gt;Azure App Service&lt;/strong&gt;. This API is responsible for processing and redirecting shortened links. The main frontend site, &lt;code&gt;linkdisguiser.com&lt;/code&gt;, is hosted as an &lt;strong&gt;Azure Static Web App&lt;/strong&gt;, which keeps the overall hosting cost low and simplifies deployment. This combination of services results in a fast, inexpensive solution with very little code to maintain.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/static_web_apps.jpeg" alt="sketch of the logo for azure static web apps" /&gt;&lt;/p&gt;
&lt;p&gt;The redirection mechanism under the hood is simple and lightweight. When a shortened link is accessed via &lt;code&gt;lkdr.xyz&lt;/code&gt;, the C# API queries Azure Table Storage using the optimized keys, finds the target URL, and performs the redirect. This lean setup is highly effective without requiring a heavy backend or database infrastructure.&lt;/p&gt;
&lt;h3&gt;Future Enhancements&lt;/h3&gt;
&lt;p&gt;Looking ahead, several new features are planned to make LinkDisguiser.com more powerful and user-friendly. One major addition will be &lt;strong&gt;link performance tracking&lt;/strong&gt;, allowing users to see how often their links are clicked and from which locations. This will provide valuable insight into engagement and reach.&lt;/p&gt;
&lt;p&gt;Another upcoming feature is &lt;strong&gt;QR code generation&lt;/strong&gt; for each shortened link, making it easier to share links in physical or visual formats like flyers, presentations, or signage.&lt;/p&gt;
&lt;p&gt;Additionally, I'm considering introducing &lt;strong&gt;paid support plans&lt;/strong&gt; for users who want enhanced features, priority assistance, or usage at higher volumes. These enhancements aim to balance usability, scalability, and sustainability as the project grows.&lt;/p&gt;
&lt;h3&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;Building LinkDisguiser.com has been a rewarding experience, combining practical problem-solving with the exploration of scalable cloud technologies. It's a reminder that even simple tools can have impact when built thoughtfully. I hope this project encourages others to start small, experiment, and learn through building. If you have ideas, feedback, or feature requests, I'd love to hear from you.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Link Shortening with LinkDisguiser.com and lkdr.xyz</title>
    <link href="https://danschnau.com/blog/Link_Shortening_with_LinkDisguiser_com_and_lkdr_xyz" />
    <id>urn:uuid:9e4ef6b5-6a0a-422c-ae6c-d7f8db060437</id>
    <updated>2025-03-25T02:06:07.483Z</updated>
    <content type="html">&lt;p&gt;LinkDisguiser.com has been updated to improve link shortening. The service now uses &lt;code&gt;lkdr.xyz&lt;/code&gt; to generate shorter links that are easy to share.&lt;/p&gt;
&lt;h3&gt;What's New?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shorter Links:&lt;/strong&gt; The new domain, &lt;code&gt;lkdr.xyz&lt;/code&gt;, makes shortened links more compact.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Technical Details&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Azure Table Storage:&lt;/strong&gt; Link redirects and click tracking are stored in &lt;strong&gt;Azure Table Storage&lt;/strong&gt; for reliable performance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hosting:&lt;/strong&gt; &lt;code&gt;lkdr.xyz&lt;/code&gt; is hosted on an &lt;strong&gt;Azure App Service&lt;/strong&gt;, while LinkDisguiser.com runs on a &lt;strong&gt;static web app&lt;/strong&gt; for scalability.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Future Enhancements&lt;/h3&gt;
&lt;p&gt;Additional analytics may be introduced to provide insights into link clicks, including geographic data, device types, and referral sources.&lt;/p&gt;
&lt;h3&gt;Why Use LinkDisguiser.com?&lt;/h3&gt;
&lt;p&gt;LinkDisguiser.com provides a simple and effective way to shorten URLs for social media, email campaigns, or general use. The service is free and does not require signups.&lt;/p&gt;
&lt;p&gt;More updates may follow as the project develops.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>How to hide warnings in the dotnet cli</title>
    <link href="https://danschnau.com/blog/how_to_hide_warnings_in_dotnet_cli" />
    <id>urn:uuid:7eb4700c-44d2-4158-85ea-528339b7c8df</id>
    <updated>2025-02-18T22:09:32.3Z</updated>
    <content type="html">&lt;p&gt;To hide warnings with the &lt;code&gt;dotnet&lt;/code&gt; cli in commands such as &lt;code&gt;dotnet build&lt;/code&gt; or &lt;code&gt;dotnet test&lt;/code&gt; - you have to pass a flag to the underlying msbuild system.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dotnet build --property:WarningLevel=0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This doesn't make Warnings not happen - but if you know you're not going to be fixing them, it gets them out of the way of your shell.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Table Capture: A Very Good Browser Extension</title>
    <link href="https://danschnau.com/blog/table_capture_a_good_browser_extension" />
    <id>urn:uuid:bcc9b5be-70f1-4139-8e46-b85f82cf8197</id>
    <updated>2024-11-19T18:16:22.367Z</updated>
    <content type="html">&lt;p&gt;Today I wanted to find a way to extract data from my banking portals faster. Usually, I use the &amp;quot;export to csv&amp;quot; feature, then manually copy/paste that csv data into the &amp;quot;master&amp;quot; spreadsheet. This is how my wife and I keep track of our finances.&lt;/p&gt;
&lt;p&gt;But this process is tedious. I wanted to be able to copy data directly from the html &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; I see when I first log in to my banking portal.&lt;/p&gt;
&lt;p&gt;For this purpose I found the browser extension &lt;a href="https://georgemike.com/tablecapture/"&gt;Table Capture&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This extension allowed me to select any html table on a page and copy/paste directly into my spreadsheet. It's a great tool.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://microsoftedge.microsoft.com/addons/detail/table-capture/cjlemjohnmihejeecaoaglgejaokmclj"&gt;Table Capture Extension for Edge&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://chromewebstore.google.com/detail/table-capture/iebpjdmgckacbodjpijphcplhebcmeop"&gt;Table Capture Extension for Chrome&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/table-capture-firefox/"&gt;Table Capture Extension for Firefox&lt;/a&gt;&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>How to Add a Blazor Server App to an ASP.NET Model/Controller Web API</title>
    <link href="https://danschnau.com/blog/how_to_add_a_Blazor_Server_App_to_an_ASPNET_Controller_Web_API" />
    <id>urn:uuid:9513478d-8c2f-44aa-adf2-f54c0373067e</id>
    <updated>2025-02-18T22:09:03.52Z</updated>
    <content type="html">&lt;p&gt;Blazor is a modern framework for building web applications with C#/.NET. Blazor apps can run &amp;quot;anywhere&amp;quot; in a variety of rendering modes. Blazor supports building pre-rendered static sites, server rendered web applications, and client-rendered web applications. With .NET 8, these rendering modes can be mixed and changed on a component-by-component basis.&lt;/p&gt;
&lt;p&gt;This post focuses on using Blazor Server to render a web application. Blazor Server handles changes on the server then streams DOM updates to the browser via a SignalR connection.&lt;/p&gt;
&lt;p&gt;ASP.NET Web API is a modern, but older and very mature, framework for building Web APIs with C#/.NET.&lt;/p&gt;
&lt;p&gt;You can write a Blazor Server project to retrieve data without using ASP.NET WebAPI, this post will focus on adding a Blazor Server App to an existing Model/Controller WebAPI project.&lt;/p&gt;
&lt;p&gt;We will first set up a sample Web API project with Controllers. Then we will set up a sample Blazor server application.&lt;/p&gt;
&lt;p&gt;Then, we will merge the two together. Finally, we will write the code to invoke the Web API controller from the Blazor Server app.&lt;/p&gt;
&lt;h2&gt;Creating an ASP.NET Controller API&lt;/h2&gt;
&lt;p&gt;First, let us set up a Web API project. With the dotnet cli, this is a one-line command in a terminal.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dotnet new webapi --name add_blazor_server -controllers&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;--name&lt;/code&gt; argument there, &lt;code&gt;add_blazor_server&lt;/code&gt;, can be whatever you want your project to be named. &lt;code&gt;-controllers&lt;/code&gt; specifies to use Controllers for your project rather than a newer &amp;quot;minimal API&amp;quot; project template.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/_new_webapi_controllers.png" alt="New Sample Web API Project" /&gt;&lt;/p&gt;
&lt;p&gt;In that screenshot we can see the contents of the root of the project. A few highlights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Controllers&lt;/code&gt; is the directory for our API Controllers.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;add_blazor_server.csproj&lt;/code&gt; is the project file that specifies things like NuGet package dependencies and which version of .NET to use.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;add_Blazor_server.http&lt;/code&gt; is a file that specifies a request with which you can test the api. This is a convenient tool for those of us used to more heavyweight tools such as Postman.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;appsettings.json&lt;/code&gt; and &lt;code&gt;appsettings.Development.json&lt;/code&gt; are JSON configuration files for application settings. Some things are pre-defined in them.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Program.cs&lt;/code&gt; is the Entry Point of the project.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WeatherForecase.cs&lt;/code&gt; provides a sample Model object for us to work with.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Creating a Blazor Server App&lt;/h2&gt;
&lt;p&gt;Now that we have our API Controllers, we can add a Blazor Server application.&lt;/p&gt;
&lt;p&gt;In order to understand this, lets create a sample Blazor server project and look at a few things.&lt;/p&gt;
&lt;p&gt;Just like the Web API project, we can create a blazor server project with the dotnet cli:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dotnet new blazorserver --name demo_blazorserver&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Inside our demo blazor server project, we have some overlapping files with our WebApi and some new things. Here are a few highlights:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/_new_blazor_server.png" alt="New Sample Blazor Server Project" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Data&lt;/code&gt; provides some sample Data Access features for the blazor server app. Our objective will be to use the Blazor Server app with the API provided by ASP.NET Web Api instead of this.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Pages&lt;/code&gt;, &lt;code&gt;Shared&lt;/code&gt;, and &lt;code&gt;wwwroot&lt;/code&gt; are all for our Blazor Server razor pages UI experience. We'll keep these.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Program.cs&lt;/code&gt; is here as well and we will need to merge what happens in here with our &lt;code&gt;Program.cs&lt;/code&gt; in our Web API project.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Integrating Blazor with ASP.NET Web API&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Adding Blazor to the ASP.NET Project&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now, let's add to the Web API project what we will need to run a blazor server and Web API Controller together in a single project.&lt;/p&gt;
&lt;p&gt;First, we will add what we need in the Web API project's &lt;code&gt;Program.cs&lt;/code&gt;. In the Services setup, we need to invoke a few methods to register Blazor Services.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in that same file we set up middleware, we add Blazor pipelines middlewares. Add them &lt;em&gt;after&lt;/em&gt; the &lt;code&gt;MapControllers()&lt;/code&gt; call so that our API controllers get &amp;quot;first dibs&amp;quot; at an incoming HTTP request.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
   app.UseSwagger();
   app.UseSwaggerUI();
}

app.UseHttpsRedirection();
 
app.UseAuthorization();
 
app.MapControllers();
 
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage(&amp;quot;/_Host&amp;quot;);
 
app.Run();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we can copy some things. Copy &lt;code&gt;App.razor&lt;/code&gt;, &lt;code&gt;_Imports.razor&lt;/code&gt;, &lt;code&gt;wwwroot&lt;/code&gt;, &lt;code&gt;Shared&lt;/code&gt;, and &lt;code&gt;Pages&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Make sure the namespaces are aligned properly in the single project.&lt;/p&gt;
&lt;p&gt;You'll get a few errors related to a missing &lt;code&gt;Data&lt;/code&gt; namespace. Remember, this is the sample data access code from our blazor server app. We're going to change this code to use our Web API controller instead.&lt;/p&gt;
&lt;p&gt;After removing broken dependencies from the &lt;code&gt;FetchData.razor&lt;/code&gt; page, we have an app that runs, but the FetchData page doesn't do anything.&lt;/p&gt;
&lt;p&gt;We can run the app with &lt;code&gt;dotnet run&lt;/code&gt; and give this a test.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/_builds_but_no_data.png" alt="The project runs, but no data screenshot" /&gt;&lt;/p&gt;
&lt;p&gt;But, we can get the data if we point our browser directly at the route for our API controller.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/_data_is_available.png" alt="The API Controller runs, there is data available screenshot" /&gt;&lt;/p&gt;
&lt;p&gt;So, we have our app running both a Web API controller and a Blazor Server app. All that's left is updating some code in our Razor component, so the Blazor Server part of our app can call the Web API controller part of our app.&lt;/p&gt;
&lt;h2&gt;Making WebAPI Controller calls in Blazor Server&lt;/h2&gt;
&lt;p&gt;Let's make an API call in our &lt;code&gt;FetchData.razor&lt;/code&gt; call to replace the WeatherForecastService code we removed.&lt;/p&gt;
&lt;p&gt;First, we set up a new Service in &lt;code&gt;Program.cs&lt;/code&gt;, the HTTP Client. This allows us to inject an HTTPClientFactory into our Razor component so we can make HTTP calls.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;builder.Services.AddHttpClient();&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Then, in &lt;code&gt;FetchData.razor&lt;/code&gt;, we inject two services, &lt;code&gt;NavigationManager&lt;/code&gt; and &lt;code&gt;HttpClientFactory&lt;/code&gt;. We will need the NavigationManager, a class provided by the Blazor framework, so the HTTPClient knows what URI to call.&lt;/p&gt;
&lt;p&gt;While both the web api and the blazor server app are running in the same runtime here, this httpclient could make http calls out to whatever endpoints it has access to.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;@inject IHttpClientFactory HttpClientFactory
@inject NavigationManager NavigationManager
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we can update the &lt;code&gt;OnInitializedAsync()&lt;/code&gt; call of our &lt;code&gt;FetchData&lt;/code&gt; component.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;protected override async Task OnInitializedAsync()
{
    var httpClient = this.HttpClientFactory.CreateClient();
    httpClient.BaseAddress = new Uri(NavigationManager.BaseUri);
    forecasts = await httpClient.GetFromJsonAsync&amp;lt;WeatherForecast[]&amp;gt;(&amp;quot;/weatherforecast&amp;quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we ran run our app one more time with &lt;code&gt;dotnet run&lt;/code&gt; and see that the 'Fetch Data' page is pulling data from our Web API Controller.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/_working.png" alt="The application runs correctly screenshot" /&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion, Review, and Source Code on Github&lt;/h2&gt;
&lt;p&gt;This article provides a step-by-step guide to combining Blazor Server with an ASP.NET Web API, ensuring seamless data integration and a functional web application.&lt;/p&gt;
&lt;p&gt;We first made a sample Web API project, and a sample Blazor Server project. We accomplished this with a few commands from the dotnet cli, a tool that the author is particularly fond of.&lt;/p&gt;
&lt;p&gt;After that, we walked through migrating the Blazor server functionality into the Web API project.&lt;/p&gt;
&lt;p&gt;Once that was running, we programmed a Blazor server razor component to make HTTP calls to our Web API controller in the same project.&lt;/p&gt;
&lt;p&gt;We included a few screenshots of the app along the way. If you would like to see all the code together, you can check it out at &lt;a href="https://github.com/dsschnau/add_blazor_server_app_to_aspnet_web_api"&gt;GitHub here: https://github.com/dsschnau/add_blazor_server_app_to_aspnet_web_api&lt;/a&gt;. Great job, thanks for reading!&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>LinkDisguiser.com, BlankWallpaper.com, and TopTechSites.com</title>
    <link href="https://danschnau.com/blog/linkdisguiserdotcom_blankwallpaperdotcom_toptechsitesdotcom" />
    <id>urn:uuid:2c849247-ef0d-4511-b508-003d6d546d5a</id>
    <updated>2024-11-01T17:53:38.863Z</updated>
    <content type="html">&lt;p&gt;A while back I went domain name shopping and picked up a few cool sounding domains. Using Azure Static Webapps, I built sites for each. Each site is in &amp;quot;alpha&amp;quot; release, and I plan on adding more features and content to these sites over time.&lt;/p&gt;
&lt;h1&gt;LinkDisguiser.com&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/link_disguiser_dot_com.png"&gt;!Home Page for Link Disguiser Dot Com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What better thing to do with the domain &lt;a href="LinkDisguiser.com"&gt;LinkDisguiser.com&lt;/a&gt; than to build a link disguiser, or a link shortener. I consider this first version an 'alpha' version, where links are saved mapped to some random words, and redirects are transparent because disguised links first go to a landing page saying where the disguised link will redirect to. I invite you to give it a try.&lt;/p&gt;
&lt;h1&gt;BlankWallpaper.com&lt;/h1&gt;
&lt;p&gt;BlankWallpaper.com was a foray into generating images with a C# Azure Function. Taking arguments of Color and resolution, this site will generate a blank wallpaper image for the user (or, deliver one that was already generated).&lt;/p&gt;
&lt;h1&gt;TopTechSites.com&lt;/h1&gt;
&lt;p&gt;This domain seemed too good to pass up, but, now I don't have any great ideas for what to do with it. Right now, it's just a page of static links to a big list of tech sites. I'm pretty sure some of them are defunct already.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Walkthrough: Deploy a Blazor WASM app running on a Free Azure Static Webapp instance</title>
    <link href="https://danschnau.com/blog/how_to_create_azure_static_web_app_blazor" />
    <id>urn:uuid:bc30d307-df48-423b-b072-998d4465cf2b</id>
    <updated>2024-05-22T00:00:40.507Z</updated>
    <content type="html">&lt;p&gt;&lt;a href="https://azure.microsoft.com/en-us/products/app-service/static"&gt;Azure Static Web Apps&lt;/a&gt; are one of my new favorite things. Azure static apps are essentially static file servers - there's no compute resources available on them. They're optimized to work for front-end applications e.g. Blazor WASM, Angular, Vue, React, etc. In order to support server-side compute, Azure Static Apps integrate with &lt;a href="https://azure.microsoft.com/en-us/products/functions"&gt;Azure Functions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What I love about this stack is that it's all free for small projects. They provide a free 'hobbyist' tier, and free SSL certificates, so the only thing you need to pay for is a domain (and even then you can skip that and use a generated &lt;code&gt;*.azurestaticapps.net&lt;/code&gt; domain). For Azure functions, the first &lt;em&gt;million requests per month&lt;/em&gt; are free. After that they're still dirt cheap.&lt;/p&gt;
&lt;h2&gt;Create a Blazor WASM Project and Deploy to Azure Static Webapp&lt;/h2&gt;
&lt;p&gt;Let's walk through how to deploy a blazor wasm project to an azure static webapp. I happened to recently buy the domain &lt;code&gt;blankwallpaper.com&lt;/code&gt;, so we will make an app for generating blank wallpapers and get it deployed.&lt;/p&gt;
&lt;p&gt;In order to start, you'll need accounts on Microsoft Azure and Github, and a blank Github repository to work with. In my case it is a private github repo called &lt;code&gt;dsschnau/blankwallpaperdotcom&lt;/code&gt;. If you are reading this and learning to build web applications from scratch, I suggest learning Git and GitHub from another resource.&lt;/p&gt;
&lt;p&gt;Using the dotnet CLI, I can spin up a new blazor wasm application, and push it to my repo in GitHub.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/init_blankwallpaper_1.png" alt="use dotnet cli to init a blazor wasm application" /&gt;
&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/init_blankwallpaper_1.png" alt="use git to push code to github" /&gt;&lt;/p&gt;
&lt;p&gt;From the &lt;a href="https://portal.azure.com"&gt;azure portal&lt;/a&gt;, we first create a &lt;code&gt;Static Web App&lt;/code&gt; resource, and walk through a few bits of configuration.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/create_azure_static_webapp.png" alt="Create Azure Static Web App" /&gt;&lt;/p&gt;
&lt;p&gt;Then we need to configure a few things. GitHub and Azure are tightly integrated (Microsoft owns GitHub after all 😅) so finding my repo is straightforward.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/configure_static_webapp.png" alt="Configure Azure Static Web App" /&gt;&lt;/p&gt;
&lt;p&gt;We can leave the 'Advanced' section by default, so here's the preview before running the deployment&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/create_asw_preview.png" alt="Preview Create Azure Static Web App" /&gt;&lt;/p&gt;
&lt;p&gt;The deployment takes about 20 seconds.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/your_deployment_is_complete.png" alt="deployment complete" /&gt;&lt;/p&gt;
&lt;p&gt;This deployment does two things. First, it creates the resource in Azure. Then it creates a &lt;a href=""&gt;Github Actions Job&lt;/a&gt; and automatically kicks off the first deployment. After about a minute, the first deploy is done, and our &lt;code&gt;hello world&lt;/code&gt; app is deployed. Subsequent commits to &lt;code&gt;main&lt;/code&gt; will trigger new deployments.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/new_asw.png" alt="new azure static webapp" /&gt;
&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/github_action.png" alt="github action" /&gt;
&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/asw_hello_world.png" alt="hello world" /&gt;&lt;/p&gt;
&lt;p&gt;Off to the races! This leaves off configuring Azure Functions as a backend and Custom Domains. I will save those subjects for another day.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Will Blazor Replace MVC? Exploring the Future of Web Development</title>
    <link href="https://danschnau.com/blog/will_blazor_replace_mvc_2024_future_of_web_development" />
    <id>urn:uuid:a961c797-55f4-40e6-8138-265c76b9c14c</id>
    <updated>2024-05-16T02:57:16.793Z</updated>
    <content type="html">&lt;h2&gt;Will Blazor Replace MVC?&lt;/h2&gt;
&lt;p&gt;Blazor and MVC are two distinct technologies in the realm of web development, each with its own set of advantages and use cases. Let's delve into the differences between them to understand their roles in modern web application development.&lt;/p&gt;
&lt;h2&gt;Is Blazor Better than MVC?&lt;/h2&gt;
&lt;p&gt;While Blazor offers a novel approach to building web applications entirely in .NET/C#, it's essential to recognize that it's not intended to replace ASP.NET MVC outright. Blazor is tailored for developing single-page applications (SPAs) or experiences akin to SPAs, where .NET languages like C# are exclusively used. On the other hand, MVC remains a reliable choice for traditional web application development.&lt;/p&gt;
&lt;p&gt;If your objective is to create a dynamic, interactive SPA, Blazor may be the preferable option due to its seamless integration with WebAssembly (WASM), delivering exceptional client-side performance. However, for projects requiring a more conventional web application experience, MVC is likely to be the more suitable choice.&lt;/p&gt;
&lt;h2&gt;Is ASP.NET MVC Still Relevant in 2024?&lt;/h2&gt;
&lt;p&gt;A resounding yes! ASP.NET MVC continues to be widely utilized in web development, including this very blog you're reading. Numerous enterprise-grade applications rely on MVC, benefiting from its robust architecture and steadfast support from Microsoft.&lt;/p&gt;
&lt;p&gt;ASP.NET MVC adheres to the Model-View-Controller pattern, providing a structured approach to building web applications. Requests are handled by controllers, which interact with models and render views for user consumption. With its proven track record, MVC boasts excellent performance and a thriving community of developers and resources.&lt;/p&gt;
&lt;h2&gt;Exploring Blazor Hosting Models&lt;/h2&gt;
&lt;p&gt;Blazor introduces innovative hosting models that distinguish it from MVC. Unlike MVC, where client-side scripting often involves JavaScript, Blazor enables the execution of both server and client code entirely in .NET/C#. This capability is made possible by WebAssembly, which allows .NET code to run directly in web browsers.&lt;/p&gt;
&lt;p&gt;Blazor supports multiple hosting models, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Blazor WebAssembly (WASM):&lt;/strong&gt; Ideal for client-side applications, Blazor WASM leverages WebAssembly to deliver high-performance .NET code directly in the browser.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blazor Server:&lt;/strong&gt; In this model, Blazor applications run on the server, with updates sent to the client via a SignalR pipeline. It's suitable for scenarios where real-time communication and server-side processing are paramount.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blazor Hybrid:&lt;/strong&gt; Combining elements of both Blazor WASM and Blazor Server, the hybrid model allows for a mix of server-side and client-side execution, offering flexibility in application architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While Blazor presents exciting opportunities for modern web development, it's important to recognize that it complements rather than replaces MVC. Both technologies coexist, catering to diverse project requirements and development scenarios.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>There really isn't much use in doubting yourself at work.</title>
    <link href="https://danschnau.com/blog/dont_doubt_yourself" />
    <id>urn:uuid:bf0e8c14-cf34-4e80-9608-5d2914fad4e4</id>
    <updated>2024-03-08T17:21:08.443Z</updated>
    <content type="html">&lt;p&gt;There really isn't much use in doubting yourself at work.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Eloquent JavaScript is a good book</title>
    <link href="https://danschnau.com/blog/eloquent_javascript" />
    <id>urn:uuid:3a3d73a6-b03a-4e14-837f-ed6545ca21e8</id>
    <updated>2024-03-07T17:36:14.653Z</updated>
    <content type="html">&lt;p&gt;I learned from Hacker News today that &lt;a href="https://eloquentjavascript.net/"&gt;Eloquent Javascript is now in it's 4th edition&lt;/a&gt;. About ten years ago, I read this book and worked through every exercise in it. It gave me a deep understanding of JavaScript, and I recommend this book to many people still.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>FunctionInputConverterException: Azure Functions in .NET dotnet-isolated process get a FunctionContext rather than an ExecutionContext</title>
    <link href="https://danschnau.com/blog/FunctionInputConverterException_Could_Not_Populate_The_Value_For_executioncontext_parameter" />
    <id>urn:uuid:f7c801fe-33dd-47ab-9470-9ad8e0ab1167</id>
    <updated>2024-01-25T17:36:18.663Z</updated>
    <content type="html">&lt;p&gt;When you upgrade an Azure Function to .NET 8 &lt;code&gt;dotnet-isolated&lt;/code&gt; model, the &lt;code&gt;Webjobs&lt;/code&gt; namespace goes away, and gets replaced by the &lt;code&gt;Microsoft.Azure.Functions.Worker&lt;/code&gt; namespace.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;Webjobs&lt;/code&gt; namespace, there is a &lt;code&gt;ExceutionContext&lt;/code&gt; that you can inject into your function. Here's a writeup on that from 2020: &lt;a href="https://jurgenonazure.com/2020/06/quick-tip-using-executioncontext-in-azure-functions/"&gt;Quick tip: using ExecutionContext in Azure Functions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is no longer available in the &lt;code&gt;Azure.Functions.Worker&lt;/code&gt; namespace. By the &lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide?tabs=windows#logging"&gt;docs at learn.microsoft.com&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The logger can also be obtained from a FunctionContext object passed to your function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, you can replace any usage of &lt;code&gt;ExecutionContext&lt;/code&gt; with &lt;code&gt;FunctionContext&lt;/code&gt;. But here's the gotcha: there also exists a &lt;code&gt;System.Threading.ExecutionContext&lt;/code&gt; class. Any C# that uses the &lt;code&gt;System.Threading.Task&lt;/code&gt; class involved with the await / async pattern is likely to be using this namespace. The result is that any references to an &lt;code&gt;ExcecutionContext&lt;/code&gt; will happily pass the compiler even with the Webjobs packages uninstalled, and that may lead to cryptic runtime errors.&lt;/p&gt;
&lt;p&gt;So when you migrate to &lt;code&gt;dotnet-isolated&lt;/code&gt; functions, make sure you aren't inadvertently referencing a &lt;code&gt;System.Threading.Execution&lt;/code&gt; context in your Func signature, or you'll get hit with the &lt;code&gt;Microsoft.Azure.Functions.Worker.FunctionInputConverterException&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/functioninputconverterexception.png" alt="FunctionInputConverterException" /&gt;&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Azure Functions Error Debug: Running processor on exception: '/' is invalid after a value. Expected either ',', '}', or ']'</title>
    <link href="https://danschnau.com/blog/Azure_functions_host_json_might_not_like_your_commented_json" />
    <id>urn:uuid:a3f9900a-e448-4004-9f4e-0ceaa6faef4d</id>
    <updated>2024-01-24T16:10:49.1Z</updated>
    <content type="html">&lt;p&gt;As it turns out, Azure Functions might not like &amp;quot;comments&amp;quot; in your &lt;code&gt;host.json&lt;/code&gt;, and it will fail awkwardly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &amp;quot;version&amp;quot;: &amp;quot;2.0&amp;quot;,
  &amp;quot;functions&amp;quot;: [
    &amp;quot;myfunc&amp;quot;
    //&amp;quot;myotherfunc&amp;quot; &amp;lt;--- THIS WILL BREAK THE FUNCTIONS APP
  ],
  &amp;quot;someMoreJsonHere&amp;quot; : { }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Console output:&lt;/p&gt;
&lt;p&gt;Debug: Running processor on exception: '/' is invalid after a value. Expected either ',', '}', or ']'. Path: $.functions | LineNumber: xx | BytePositionInLine: xx.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Use Azure DevOps CLI to Navigate Directly To A Pipeline</title>
    <link href="https://danschnau.com/blog/Use_Azure_DevOps_CLI_To_Naviatge_Directly_To_A_Pipeline" />
    <id>urn:uuid:f5ec838a-3905-4a72-88de-39ca9fca522e</id>
    <updated>2024-01-23T14:58:49.76Z</updated>
    <content type="html">&lt;p&gt;Using the &lt;a href="https://learn.microsoft.com/en-us/azure/devops/cli/?view=azure-devops"&gt;Azure Devops Extension&lt;/a&gt; to to the &lt;a href="https://learn.microsoft.com/en-us/cli/azure/"&gt;Azure CLI&lt;/a&gt;, you can navigate directly to pipelines in your browser without having to click through the Azure Devops UI.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Try the new cross-platform PowerShell https://aka.ms/pscore6

PS C:\Users\danschnau&amp;gt; az pipelines list --name *myproj* --output table
ID    Path     Name               Status    Default Queue
----  -------  -----------------  --------  ---------------
1233  \Build   myproj         enabled   Azure Pipelines
1234  \Deploy  myproj-deploy  enabled   Azure Pipelines

PS C:\Users\schnaud&amp;gt; az pipelines show --id 1233 --open
ID    Path    Name        Status    Default Queue
----  ------  ----------  --------  ---------------
1360  \Build  myproj  enabled   Azure Pipelines
PS C:\Users\danschnau&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first command searches for any pipelines that contain the string &lt;code&gt;myproj&lt;/code&gt; in their name. A list of projects come out with names and ids. The last command will open up the pipeline in your browser, specified by the ID.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Handling HTTP 409 Errors editing files in Azure App Service Editor</title>
    <link href="https://danschnau.com/blog/handling_http_409_error_in_azure_app_service_editor" />
    <id>urn:uuid:712d8250-5430-49e1-a071-91c05ada86c2</id>
    <updated>2024-01-15T18:14:15.417Z</updated>
    <content type="html">&lt;p&gt;I was getting HTTP 409 errors when editing an Azure Function app, I couldn't understand why. A Bing search led to &lt;a href="https://chamindac.blogspot.com/2021/02/resolving-409-access-denied-in-azure.html"&gt;this blog post from Chaminda Chandrasekara&lt;/a&gt; which helped me out. If the configuration &lt;code&gt;WEBSITE_RUN_FROM_PACKAGE&lt;/code&gt; is set to &lt;code&gt;1&lt;/code&gt;, the app service editor returns 409 errors when you try to edit files. I set it to &lt;code&gt;0&lt;/code&gt;, restarted my function app, and I could make changes again.&lt;/p&gt;
&lt;p&gt;When I'm done, I'll set it back to &lt;code&gt;1&lt;/code&gt; for the sake of performance.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/aaaaaaaaa.png" alt="website_run_from_package" /&gt;&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Generate an IEnumerable&lt;int&gt; of a given size, going half "Down", half "Up"</title>
    <link href="https://danschnau.com/blog/generate_ienumerable_int_given_size_half_down_half_up" />
    <id>urn:uuid:d815273e-6f54-45a3-b19b-a4bca09930a6</id>
    <updated>2024-01-10T15:13:20.703Z</updated>
    <content type="html">&lt;p&gt;I sometimes want to generate a list of numbers going &amp;quot;up&amp;quot; and &amp;quot;down&amp;quot; from a given number (usually, the ID of an entry in a database). Sometimes I've done this by hand. But with &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.range?view=net-8.0"&gt;&lt;code&gt;Enumerable.Range&lt;/code&gt;&lt;/a&gt;, generating a list like this makes for a nice snippet of C# code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var startingPoint = 999; // or wherever you want to start
var size = 10;
var list = Enumerable.Range(startingPoint-(size / 2), size);
&lt;/code&gt;&lt;/pre&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>PWAs Seem Really Good</title>
    <link href="https://danschnau.com/blog/pwas_seem_really_good" />
    <id>urn:uuid:2cd55b00-af3b-4e61-ac2b-d01922cb741a</id>
    <updated>2024-01-08T14:43:56.44Z</updated>
    <content type="html">&lt;p&gt;This site was at the front of Hacker News this morning: &lt;a href="https://whatpwacando.today/"&gt;https://whatpwacando.today/&lt;/a&gt;. I think it's rad how much can be built that runs &amp;quot;natively&amp;quot; from a browser. I'd like to explore further what, say, a Blazor app can do running as a PWA.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Experimenting with performances gains from Blazor WASM AOT Compilation 2.0</title>
    <link href="https://danschnau.com/blog/blazor_wasm_aot_compilation_20" />
    <id>urn:uuid:375c8153-5122-4c52-9a63-be4672fabf52</id>
    <updated>2024-01-25T16:20:30.447Z</updated>
    <content type="html">&lt;p&gt;In my previous post I had left open a problem of a 100ms delay that forced the screen to repaint.&lt;/p&gt;
&lt;p&gt;I've been working out what is required to make sure the screen paints between each assignment of a new Base64 to the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag in the DOM.&lt;/p&gt;
&lt;p&gt;After some discussion in Discord with &lt;a href="https://github.com/maraf"&gt;Marek Fišera&lt;/a&gt;, I've gotten the &amp;quot;forced delay&amp;quot; down to a single millisecond:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private async Task DrawStar()
{
    using (Image&amp;lt;Rgba32&amp;gt; image = new(TimesCompleted * 10, 200))
    {
        Star star = new(x: 100.0f, y: 100.0f, prongs: 5, innerRadii: 20.0f, outerRadii: 30.0f);
        image.Mutate(x =&amp;gt; x.Fill(Color.Red, star));
        ImgString = image.ToBase64String(JpegFormat.Instance);
    }
    TimesCompleted++;
    StateHasChanged();
    await Task.Delay(1);
    await Task.Yield();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a result, I get a more accurate picture of JIT vs AOT compilation for this little test case.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;JIT:
&lt;video controls="" width="500"&gt;&lt;/p&gt;
  &lt;source src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/blazor_wasm_test_2.mp4" type="video/mp4"&gt;
&lt;/video&gt;&gt; 
AOT - and take note, AOT compilation takes a long time! This little app took 6 minutes and 14 seconds to compile:
 &lt;video controls="" width="500"&gt;
  &lt;source src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/blazor_wasm_test_3.mp4" type="video/mp4"&gt;
&lt;/video&gt;&gt; 
And now the performance difference is **five-fold**!
&lt;p&gt;Update 1/23/24: while browsing the Fediverse this morning, I found a much more &lt;a href="https://kristoffer-strube.dk/post/a-holistic-comparison-of-blazor-wasm-performance-from-aspnet-core-5-to-8/"&gt;comprehensive investigation of Blazor's WASM performance by one Kristoffer Strube&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Experimenting with performances gains from Blazor WASM AOT Compilation</title>
    <link href="https://danschnau.com/blog/blazor_wasm_aot_compilation" />
    <id>urn:uuid:9c653b71-6d77-4d82-9496-3783b0b255b4</id>
    <updated>2023-12-17T04:38:07.31Z</updated>
    <content type="html">&lt;p&gt;It would appear that AOT compilation gets substantial performance improvements in client-side application performance.&lt;/p&gt;
&lt;p&gt;I put together a tech demo running Blazor client side WASM. The code for the main component is at the bottom of this post.&lt;/p&gt;
&lt;p&gt;The gist is this: draw an image, using the SixLabors ImageSharp library, 20 times. Each image is slightly wider than the previous, and post the time taken.&lt;/p&gt;
&lt;p&gt;In my experiments, it takes about 1/3rd as much time to complete with Ahead-of-time compilation.&lt;/p&gt;
&lt;p&gt;Here's the test without AOT compilation:&lt;/p&gt;
&lt;video controls width="250"&gt;
  &lt;source src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/blazor_wasm_test_no_aot.mp4" type="video/mp4" /&gt;
&lt;/video&gt;
&lt;p&gt;And the same test, compiled with AOT:&lt;/p&gt;
&lt;video controls width="250"&gt;
  &lt;source src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/blazor_wasm_test_with_aot.mp4" type="video/mp4" /&gt;
&lt;/video&gt;
&lt;p&gt;Obviously, this is an anecdotal test, but it would certainly appear to me that AOT compilation can make for some very fast client-side image processing applications.&lt;/p&gt;
&lt;p&gt;Code below:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@page &amp;quot;/&amp;quot;
@using SixLabors.ImageSharp
@using SixLabors.ImageSharp.Drawing
@using SixLabors.ImageSharp.Drawing.Processing
@using SixLabors.ImageSharp.Formats.Jpeg
@using SixLabors.ImageSharp.Formats.Png
@using SixLabors.ImageSharp.PixelFormats
@using SixLabors.ImageSharp.Processing
@using System.Diagnostics

&amp;lt;PageTitle&amp;gt;Home&amp;lt;/PageTitle&amp;gt;

&amp;lt;h1&amp;gt;Blazor WASM AOT performance testing&amp;lt;/h1&amp;gt;

&amp;lt;button @onclick=&amp;quot;_ =&amp;gt; StartWork()&amp;quot; disabled=&amp;quot;@started&amp;quot;&amp;gt;Start&amp;lt;/button&amp;gt;

&amp;lt;p&amp;gt;milliseconds to @TOTAL_ITERATIONS draws: @TimeTaken&amp;lt;/p&amp;gt;

&amp;lt;img src=&amp;quot;@ImgString&amp;quot; /&amp;gt;

@code {
    const int TOTAL_ITERATIONS = 20;
    private string? ImgString { get; set; }
    private long TimeTaken { get; set; }
    private int TimesCompleted { get; set; }
    private bool started { get; set; } = false;

    private Stopwatch Timer { get; set; } = new Stopwatch();

    private async Task StartWork()
    {
        started = true;
        TimesCompleted = 1;
        Timer.Start();

        while(TimesCompleted &amp;lt; TOTAL_ITERATIONS)
        {
            await DrawStar();
        }

        Timer.Stop();

        TimeTaken = Timer.ElapsedMilliseconds;
    }

    private async Task DrawStar()
    {
        using (Image&amp;lt;Rgba32&amp;gt; image = new(TimesCompleted * 10, 200))
        {
            Star star = new(x: 100.0f, y: 100.0f, prongs: 5, innerRadii: 20.0f, outerRadii: 30.0f);
            image.Mutate(x =&amp;gt; x.Fill(Color.Red, star));
            ImgString = image.ToBase64String(JpegFormat.Instance);
        }
        TimesCompleted++;
        StateHasChanged();
        await Task.Delay(100);
        Console.WriteLine(this.ImgString);
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>This Blog Is Migrated to Blazor Static Server Side Rendering</title>
    <link href="https://danschnau.com/blog/migrate-to-blazor-static-server-side-rendering" />
    <id>urn:uuid:0cd0178a-258f-4854-b417-fa6a4e3f8f20</id>
    <updated>2023-12-15T18:00:56.797Z</updated>
    <content type="html">&lt;p&gt;I'm excited to share that https://danschnau.com is now hosted with Blazor Static Server side rendering. Because this site is not a particularly complicated app, it didn't take too much to make this happen.&lt;/p&gt;
&lt;p&gt;Including Razor Components (the stuff of Blazor) services is a one-liner in &lt;code&gt;program.cs&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;builder.Services.AddRazorComponents();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hosting the root Blazor App is also a one liner in &lt;code&gt;program.cs&lt;/code&gt;. There are a few things I want to keep as MVC, for example, support for atom feeds, so I didn't remove the MVC services, and place MVC routing after the blazor components. With this, the blazor app gets &amp;quot;First dibs&amp;quot; at an incoming http request, and if it doesn't want to handle the request, MVC gets it's chance at it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.MapRazorComponents&amp;lt;App&amp;gt;();
app.MapControllerRoute(&amp;quot;default&amp;quot;, &amp;quot;{controller=Home}/{action=Index}/{id?}&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result is that browsing pages is pretty screaming fast.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/dan_schnau_ssr_blazor.png" alt="Dan Schnau dot com blazor SSR Performance" /&gt;&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>"Getting Started With Blazor" at Michigan DevFest 2023</title>
    <link href="https://danschnau.com/blog/blazor_midevfest_2023" />
    <id>urn:uuid:f3b02123-296c-49dc-8f59-c9cc201e62d5</id>
    <updated>2023-12-03T16:45:57.14Z</updated>
    <content type="html">&lt;p&gt;Not long after &lt;a href="https://danschnau.com/blog/i-have-been-laid-off"&gt;I was laid off&lt;/a&gt;, I signed up to give a tech talk at &lt;a href="https://midevfest.com/"&gt;Michigan DevFest 2023&lt;/a&gt;, a tech conference in downtown Detroit.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/dan-talking-at-pizzapizza.jpg" alt="Dan Schnau presenting in the &amp;quot;Pizza Pizza&amp;quot; room at Little Caesars headquarters in downtown Detroit" /&gt;&lt;/p&gt;
&lt;p&gt;I expected that facing the public-speaking fear would keep my skills sharp, help me land a job, and have some fun along the way. &lt;a href="https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor"&gt;Blazor&lt;/a&gt; is a new framework for building web applications from Microsoft, and I've been interested in it for some time, so this was a good opportunity to learn more.&lt;/p&gt;
&lt;p&gt;The conference was set to take place in December, and I submitted an abstract in September.&lt;/p&gt;
&lt;p&gt;In late October, I accepted an offer and began my new job at &lt;a href="https://www.detroittrading.com/"&gt;Detroit Trading Company&lt;/a&gt;. I put all my working energy into my new job, and it paid off well: I've been enjoying where I work! I forgot all about the commitment I had made for December 2nd.&lt;/p&gt;
&lt;p&gt;In November, I began receiving reminders about the talk I signed up to give, just a few weeks out on December 2nd. I procrastinated, and procrastinated, and procrastinated.&lt;/p&gt;
&lt;p&gt;At this point I began using mental health tools I've learned since my visit to the &lt;a href="https://danschnau.com/blog/i_went_to_a_mental_health_hospital"&gt;mental health hospital&lt;/a&gt;. I had to remind myself that public speaking is a terrifying thing for many, but there is no real danger. The fight-or-flight response is simply my body trying to protect me from a non-existent &amp;quot;threat&amp;quot;. I also may be &lt;a href="https://danschnau.com/blog/10_years_an_impostor"&gt;an impostor&lt;/a&gt;, but whether I am or not isn't even relevant. I'm a person who made a commitment to do something, and to remain in integrity with myself, I must take that on with my best effort. The results of the effort are completely out of my hands.&lt;/p&gt;
&lt;p&gt;To push myself to prepare, I scheduled a lunch-and-learn session for the Thursday preceding the conference. I also set aside time at home with my partner to practice the talk.&lt;/p&gt;
&lt;p&gt;I prepared, somewhat, and both practice rounds went poorly. But my partner is a wonderful supportive woman, and my coworkers are wondferful supportive people. I received advice, ideas, and encouragement from all and I felt confident I could put something together that was better for Saturday.&lt;/p&gt;
&lt;p&gt;And so, the night before the conference, I worked like a caffiene-addled college student to re-work the whole structure of my presentation. I wrote it all out on paper several times and talked to the wall at my desk with my phone's stopwatch running to work out timings. I went to bed around 2am that night, which was exceptionally late for me.&lt;/p&gt;
&lt;p&gt;The morning of the conference I ported my notes on paper into a brief slide deck. In my opinion, staring at slides is boring: if someone wants to read a bunch of text to learn something, a book or a web page is a better option than slides. I would rely on my charming looks and personality to teach and entertain my attendees.&lt;/p&gt;
&lt;p&gt;My talk was at the end of the day so I spent the rest of the conference enjoying talks from other presenters, chatting with other attendees and spending time with my friends and partner who came with.&lt;/p&gt;
&lt;p&gt;I gave my talk, I had a lot of fun with it, and received a lot of positive feedback. I'm glad I did this and even moreso I'm thankful I had the opportunity.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Installing a GraphQL Service on dan schnau dot com</title>
    <link href="https://danschnau.com/blog/graphql_for_funsies" />
    <id>urn:uuid:c2092674-9828-4dc9-8bc3-22c6897ca496</id>
    <updated>2023-10-20T14:53:05.743Z</updated>
    <content type="html">&lt;p&gt;As part of an upcoming project, I'll be working with GraphQL. So before starting, I decided to set up a GraphQL API on danschnau.com first.&lt;/p&gt;
&lt;p&gt;To accomplish this, I installed the project&lt;a href="https://github.com/graphql-dotnet/graphql-dotnet"&gt;graphql-dotnet&lt;/a&gt; and configured it against the danschnau.com blog database.&lt;/p&gt;
&lt;p&gt;Thanks to the &lt;a href="https://www.nuget.org/packages/GraphQL.MicrosoftDI"&gt;GraphQL.MicrosoftDI&lt;/a&gt; package, as part of the graphql-dotnet project, setting up an API was a matter of one change in the application's ServiceCollection and one in the Application builder.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public void ConfigureServices(IServiceCollection services)
{
           // other services configured....
            services.AddGraphQL(b =&amp;gt;
            {
                b.Services.Register&amp;lt;GraphQLHttpMiddleware&amp;lt;ISchema&amp;gt;, GraphQLHttpMiddleware&amp;lt;ISchema&amp;gt;&amp;gt;(GraphQL.DI.ServiceLifetime.Singleton);
                b.AddSystemTextJson()
                 .AddErrorInfoProvider(opt =&amp;gt; opt.ExposeExceptionDetails = true) // set to false for production
                 .AddSchema&amp;lt;BlogSchema&amp;gt;()
                 .AddGraphTypes(typeof(BlogSchema).Assembly);
            });
           // other services configured....
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
            // other stuff....
            app.UseGraphQL&amp;lt;ISchema&amp;gt;();
            app.UseGraphQLPlayground();
            // other stuff....
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Configuring the GraphQL Schema is a matter of mapping the concepts in GraphQL to the application. GraphQL queries are always rooted in a &amp;quot;Query&amp;quot; or a &amp;quot;Mutation&amp;quot; - I think of them as GET and POST in http. In my case, I did not need a &lt;code&gt;Mutation&lt;/code&gt; in my Schema, but only a simple &lt;code&gt;Query&lt;/code&gt; for the purpose.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using GraphQL.Instrumentation;
using GraphQL.Types;

    public class BlogSchema : Schema
    {
        public BlogSchema(IServiceProvider serviceProvider) : base(serviceProvider)
        {
            // Query = ....
            Query = (BlogQuery)serviceProvider.GetService(typeof(BlogQuery)) ??
                throw new InvalidOperationException();

            // no mutations.
            // Mutation = ....
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;BlogQuery&lt;/code&gt; defines a graphQL query that can be made, and instructions for the rest of the application to execute, or &lt;code&gt;resolve&lt;/code&gt;, the query.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-chsarp"&gt;using GraphQL.Types;

    public class BlogQuery : ObjectGraphType&amp;lt;object&amp;gt;
    {
        public BlogQuery()
        {
            FieldAsync&amp;lt;ListGraphType&amp;lt;BlogType&amp;gt;&amp;gt;(&amp;quot;blogs&amp;quot;, &amp;quot;get all blogs&amp;quot;, resolve:
                async context =&amp;gt; await blogRepo.GetBlogsAsync());
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lastly, the GraphQL package must learn about the types needed. In my case, a &lt;code&gt;BlogType&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-chsarp"&gt;using GraphQL.Types;

    public class BlogType : ObjectGraphType&amp;lt;Blog&amp;gt;
    {
        public BlogType() 
        {
            Name = &amp;quot;Blog&amp;quot;;
            Field(b =&amp;gt; b.BlogId).Description(&amp;quot;The id of the blog.&amp;quot;);
            Field(b =&amp;gt; b.Published).Description(&amp;quot;When the blog was first published.&amp;quot;);
            Field(b =&amp;gt; b.LastUpdated, nullable: true).Description(&amp;quot;When the blog was last updated. Null if never.&amp;quot;);
            Field(b =&amp;gt; b.Title).Description(&amp;quot;The Title of the Blog.&amp;quot;);
            Field(b =&amp;gt; b.Slug).Description(&amp;quot;The slug for the blog, used in URL generation.&amp;quot;);
            Field(b =&amp;gt; b.Content).Description(&amp;quot;The Content of the blog, serialized into HTML.&amp;quot;);
            Field(name: &amp;quot;URL&amp;quot;, expression: b =&amp;gt; $&amp;quot;https://danschnau.com/blog/{b.Slug}&amp;quot;).Description(&amp;quot;The URL for the blog.&amp;quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result is that if you visit &lt;a href="https://danschnau.com/ui/playground"&gt;https://danschnau.com/ui/playground&lt;/a&gt;, you get a sample client which can be used to make GraphQL queries for this blog.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/graphql.jpg" alt="demo of graphql working for danschnau.com" /&gt;&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Upload and render image in Blazor WASM client-side application</title>
    <link href="https://danschnau.com/blog/upload_and_render_image_blazor_wasm_client_side" />
    <id>urn:uuid:c093750b-172a-45ca-9742-e1403cf34914</id>
    <updated>2023-10-07T13:51:59.317Z</updated>
    <content type="html">&lt;p&gt;Here's some sample code for a blazor client-side component that allows uploading and rending an image. The cool thing about this is that it all happens client-side and allows working with the image in .NET. All that is needed is some interop javascript code, borrowed from this document: &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/images?view=aspnetcore-7.0#stream-image-data"&gt;https://learn.microsoft.com/en-us/aspnet/core/blazor/images?view=aspnetcore-7.0#stream-image-data&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@page &amp;quot;/&amp;quot;
@using Microsoft.AspNetCore.Components.Forms
@inject IHttpClientFactory HttpClientFactory
@inject IJSRuntime JS

&amp;lt;InputFile OnChange=&amp;quot;@Upload_And_Render_Image&amp;quot; /&amp;gt;

&amp;lt;img id=&amp;quot;myImage&amp;quot; /&amp;gt;

&amp;lt;script&amp;gt;
  window.setImage = async (imageElementId, imageStream) =&amp;gt; {
    const arrayBuffer = await imageStream.arrayBuffer();
    const blob = new Blob([arrayBuffer]);
    const url = URL.createObjectURL(blob);
    const image = document.getElementById(imageElementId);
    image.onload = () =&amp;gt; {
      URL.revokeObjectURL(url);
    }
    image.src = url;
  }
&amp;lt;/script&amp;gt;

@code {

    public async Task Upload_And_Render_Image(InputFileChangeEventArgs e)
    {
        Stream ms = new MemoryStream();
        await e.File.OpenReadStream().CopyToAsync(ms);
        ms.Position = 0;
        var dotnetImageStream = new DotNetStreamReference(ms);
        await JS.InvokeVoidAsync(&amp;quot;setImage&amp;quot;, &amp;quot;myImage&amp;quot;, dotnetImageStream);
    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;video controls src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/blazor_image_demo.mp4" muted="false"&gt;&lt;/video&gt;&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>How To Add Controllers To A Blazor Server App</title>
    <link href="https://danschnau.com/blog/how_to_add_controllers_to_a_blazor_server_app" />
    <id>urn:uuid:4cecb7b3-0d61-4d93-a249-2c253730e496</id>
    <updated>2025-03-29T22:02:06.773Z</updated>
    <content type="html">&lt;p&gt;&lt;strong&gt;Update November 2024&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I wrote a much more detailed post about this subject here: &lt;a href="https://danschnau.com/blog/how_to_add_a_Blazor_Server_App_to_an_ASPNET_Controller_Web_API"&gt;https://danschnau.com/blog/how_to_add_a_Blazor_Server_App_to_an_ASPNET_Controller_Web_API&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/blazor_plus_mvc.jpeg" alt="blazor plus mvc" /&gt;&lt;/p&gt;
&lt;p&gt;I spun up a new Blazor Server App and wanted to add an API controller, but out of the box it didn't work. It's funny, sometimes the things that you take for granted end up being hard to get past when they don't work how you expect.&lt;/p&gt;
&lt;p&gt;I had to do these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;In &lt;code&gt;Program.cs&lt;/code&gt;, configure Controller routes. This is the &amp;quot;canonical&amp;quot; default:&lt;/p&gt;
&lt;p&gt;app.MapControllerRoute(&amp;quot;default&amp;quot;, &amp;quot;{controller=Home}/{action=Index}/{id?}&amp;quot;);&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;That's basically it. Add a Controllers directory to the Server project, and instantiate an API controller. For example:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;    using Microsoft.AspNetCore.Mvc;
    
    namespace MyApp.Server
    {
        [Route(&amp;quot;/&amp;quot;)]
        [ApiController]
        public class TestController : ControllerBase
        {
            [HttpGet(&amp;quot;getdata&amp;quot;)]
            public IActionResult GetData()
            {
                return Ok(new { data = new { foo = &amp;quot;bar&amp;quot; } });
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that an HTTP GET to /getdata would return the hard-coded object there.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>A List Of Free Programming Challenges You Can Take On Today To Develop Your Skills</title>
    <link href="https://danschnau.com/blog/I_do_not_know_what_to_code" />
    <id>urn:uuid:536c5df5-d06e-4ffa-825d-a4562d283d44</id>
    <updated>2023-09-25T19:09:54.83Z</updated>
    <content type="html">&lt;h2&gt;I Know How To Code But I Don't Know What To Code&lt;/h2&gt;
&lt;p&gt;With some extra &lt;a href="/blog/i-have-been-laid-off"&gt;time on my hands&lt;/a&gt; these days I am looking for things to code. I notice there are others with similar problems. This is a compiled list of free programming challenges you can start taking on today.&lt;/p&gt;
&lt;h3&gt;Codewars&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.codewars.com/"&gt;Codewars&lt;/a&gt; is a site where users both create and solve problems. Solutions are shared and discussed in the app. User solutions are developed, submitted, and analyzed for correctness. Multiple languages are supported. I liked to use Codewars several years ago to test my skills. It can be a great way to make sure your chops with different languages are where you want them to be. They have their own community including a Discord server and discussions board. Codewars is a for-profit adventure, working with companies and educators to provide solutions for developing and recruiting talent.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/codewarscom.png" alt="Codewars screenshot" /&gt;&lt;/p&gt;
&lt;h3&gt;Topcoder&lt;/h3&gt;
&lt;p&gt;In my mind, &lt;a href="https://www.topcoder.com/"&gt;Topcoder&lt;/a&gt; is the original programming challenge website. I believe this is the site I tried to access through my America Online Account when I was in middle school. The platform runs on competition, where challenges are posted, and prize money is awarded to developers with winning solutions.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/topcodercom.png" alt="Topcoder screenshot" /&gt;&lt;/p&gt;
&lt;h3&gt;LeetCode&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://leetcode.com/"&gt;LeetCode&lt;/a&gt; is similar to Codewars with the way it presents problems. On LeetCode, &amp;quot;you can use tutorials and study plans to learn fundamental and advanced data structures and algorithms, compete in contests, use guides to prepare for top companies, exercise your problem solving skills by solving real interview questions, and much more.&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/lettcodecom.png" alt="LeetCode screenshot" /&gt;&lt;/p&gt;
&lt;h3&gt;Project Euler&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://projecteuler.net/"&gt;Project Euler&lt;/a&gt; &amp;quot;is a series of challenging mathematical/computer programming problems that will require more than just mathematical insights to solve.&amp;quot;&lt;/p&gt;
&lt;p&gt;Project Euler is under a Creative Commons license and not-for-profit. It's a huge list of math-oriented programming problems. It's good practice for any 'math person'. I remember taking a swing at these problems in college and finding them very challenging.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/projecteulernet.png" alt="Project Euler screenshot" /&gt;&lt;/p&gt;
&lt;h3&gt;Edabit&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://edabit.com/challenges#!"&gt;Edabit&lt;/a&gt; is more straight-to-the-punch coding challenges that run in the browser, similar to Codewars.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/edabitcom.png" alt="Edabit screenshot" /&gt;&lt;/p&gt;
&lt;h3&gt;Exercism&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://exercism.org/"&gt;Exercism&lt;/a&gt; is a open-source and not-for-profit code learning platform, where users can either mentor or take on challenges over a large range of programming languages.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/exercismorg.png" alt="exercism.org screenshot" /&gt;&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Use A RegEx to get the Word Count out of a string in JavaScript</title>
    <link href="https://danschnau.com/blog/javascript_regex_word_count" />
    <id>urn:uuid:6df4c796-e1d5-4282-8793-84953a828402</id>
    <updated>2023-09-25T15:04:35.687Z</updated>
    <content type="html">&lt;p&gt;I added a 'word count' feature to my blog editor this morning. This is the regular expression to match spaces and line breaks:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-regex"&gt;/\s/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And in JavaScript:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;const count = mystring.split(/\s+/).length;
&lt;/code&gt;&lt;/pre&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Will Blazor Replace MVC?</title>
    <link href="https://danschnau.com/blog/will_blazor_replace_mvc" />
    <id>urn:uuid:61a01c48-c1c6-42a4-9088-73126a3ceb43</id>
    <updated>2023-09-23T18:40:38.83Z</updated>
    <content type="html">&lt;p&gt;Blazor will not simply replace MVC. Blazor is a new technology with its own advantages and disadvantages, while MVC is a tried-and-true technology for building web applications with ASP.NET.&lt;/p&gt;
&lt;h2&gt;Is Blazor Better than MVC?&lt;/h2&gt;
&lt;p&gt;The short answer is no, Blazor is not Better than MVC. It's a new technology for a new type of application, and not intended to replace ASP.NET MVC. If you want to build a single-page-application or SPA like experience, and only write with .NET languages like C#, then Blazor is the technology you need. If you want to build a more classic web application experience, then ASP.NET MVC will suit your needs better.&lt;/p&gt;
&lt;p&gt;The result of this is that if you want to build a single-page application, Blazor is likely a better choice than MVC for your needs. Blazor WebAssembly, or Blazor WASM, offers spectacular performance gains on client-side applications thanks to taking advantage of WebAssembly.&lt;/p&gt;
&lt;p&gt;If you want to build a more classic web application experience, MVC is likely a better choice than Blazor. Blazor is a newer technology but offers potential and has been growing in popularity since 2019.&lt;/p&gt;
&lt;h2&gt;Is ASP.NET MVC still used in 2023?&lt;/h2&gt;
&lt;p&gt;It sure is! The blog you're reading this on is powered by MVC. Many enterprise applications run on MVC and they will continue to do so for the foreseeable future. MVC is a time-tested technology that has full-throated support from Microsoft.&lt;/p&gt;
&lt;p&gt;MVC is an acronym for &lt;strong&gt;Model, View, Controller&lt;/strong&gt; and it is a way of building out web applications. In short, An HTTP request comes in to the server. The request lands on a Controller, which handles the request and generates a Model. The Model is then sent to a View, which is returned by the server and rendered on the user's page. The View for this page you're reading right now is of type &lt;code&gt;Blog&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;MVC is a proven architecture with excellent performance and a large, healthy ecosystem of developers and software packages.&lt;/p&gt;
&lt;h3&gt;Blazor Hosting Models&lt;/h3&gt;
&lt;p&gt;Blazor is a technology built for running web applications written &lt;em&gt;entirely&lt;/em&gt; in .NET/C#. That is. This is the key difference: in MVC, the web server is running .NET, but you're still running JavaScript in the browser for anything done client-side. &lt;strong&gt;With Blazor, everything is run in .NET, both server and client code.&lt;/strong&gt; Blazor runs .NET code on the browser thanks to WebAssembly, or WASM. Essentially, a tiny version of the .NET framework is sent to the browser, then compiled C# code is sent along with it. This Blazor model is called Blazor WASM.&lt;/p&gt;
&lt;p&gt;Blazor can also run entirely server side, with page updates sent to the browser via a SignalR pipeline. This model is called Blazor Server.&lt;/p&gt;
&lt;p&gt;In combination with Blazor WASM, and Blazor Server, some code can run on the server, and other code can run on the browser in a hosting model called Blazor Hybrid.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Learn SQL In One Day: The Fastest SQL Bootcamp In History</title>
    <link href="https://danschnau.com/blog/sql_in_one_day" />
    <id>urn:uuid:9d6a33fd-9532-4ee3-a93b-728aca789afc</id>
    <updated>2023-09-22T19:32:27.313Z</updated>
    <content type="html">&lt;p&gt;Here we go. I'm going to explain SQL in one sitting. I'm writing this without doing any research, just from my own experience, so do check the facts yourself, and I welcome feedback and will happily make edits. If it doesn't make much sense, well I'm injured and on painkillers today so whatever.&lt;/p&gt;
&lt;p&gt;SQL stands for Structured Query Language. It is a long running standard that has been around for decades, long before the internet at least. It's used to query databases. Because it's a standard, there's a lot of overlapping things between different database providers that you can use the same SQL for.&lt;/p&gt;
&lt;h2&gt;Database Providers&lt;/h2&gt;
&lt;p&gt;Like Microsoft's SQL Server, Oracle's MySQL, The open source PostgreSQL, open source SQLite, all database providers. They have lots of different characteristics around deploying them and what they cost and who is responsible for them, but they all understand Structured Query Language, SQL.&lt;/p&gt;
&lt;h2&gt;Tables&lt;/h2&gt;
&lt;p&gt;SQL is a language primarily for querying Tables of data in databases. Tables are similar to spreadsheets in Excel. As a matter of fact, a lot of database implementations come in to play because an Excel spreadsheet got too big. I've seen it happen first-hand several times in my career. If you have a big spreadsheet and need help making it into a database and a web app, you can &lt;a href="/hireme"&gt;hire me&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Anyway, tables are based in Relational Algebra, which is a &amp;quot;math thing&amp;quot; that isn't particularly useful for applications, but if you're making your own database provider, you had better know it well. Data is stored in rows and columns on long-term storage like a hard-disk drive.&lt;/p&gt;
&lt;p&gt;Data is (hopefully) normalized over several tables. So, let's make an example table &amp;quot;customer&amp;quot;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-sql"&gt;CREATE TABLE customer (name nvarchar(255), address nvarchar(255))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can think of this table as an excel spreadsheet with two columns: one for name, and one for address. Well what if you need to associate more than one address with one customer? The not-so-great way is to make two rows of data in this table for the same customer. A better way is to &amp;quot;normalize&amp;quot; the data and make an Addresses table, and associate the two together. It looks like this when creating the tables:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-sql"&gt;CREATE TABLE customer (customerid uniqueidentifier, name nvarchar(255))
CREATE TABLE address (addressid uniqueidentifier, customerid uniqueidentifier, address nvarchar(255))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So now you have two tables: &lt;code&gt;customer&lt;/code&gt; and &lt;code&gt;address&lt;/code&gt;. Customers with one address will have one row in each table. Customers with no address will have only one row in the customer table, and customers with two or more addresses will have a row in the customer table and two or more rows in the address table.&lt;/p&gt;
&lt;h2&gt;INSERT&lt;/h2&gt;
&lt;p&gt;How, might you ask, do you insert data into a sql table? Well, you do that with &lt;code&gt;INSERT&lt;/code&gt; statements. Let's insert a customer into our tables created above. We can use the &lt;code&gt;newid()&lt;/code&gt; SQL method to generate an id, or you can be cheeky and grab one from &lt;a href="https://danschnau.com/Home/INeedAGuid"&gt;I Need A GUID!&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-sql"&gt;INSERT INTO customer (newid(), 'app devs r us')
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;then what if we need to include an address to that customer?&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-sql"&gt;INSERT INTO address(newid(), (SELECT top 1 customerid from customer where name = 'app devs r us'), '123 main street)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will shove a row into the address table and the associated ids will match up. I had to include a select statement in there to make this example make sense. Bear with me.&lt;/p&gt;
&lt;h2&gt;SELECT&lt;/h2&gt;
&lt;p&gt;Retrieving data from SQL databases is what comes up a lot and I feel like I've helped a lot of folks over the years get up and running with this. Allow me to continue with my run down.&lt;/p&gt;
&lt;h3&gt;get all data from a SQL table&lt;/h3&gt;
&lt;p&gt;This is the simplest SELECT statement there is.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT * FROM customer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This retrieves all the data in the table&lt;/p&gt;
&lt;h3&gt;get only one column from a SQL table&lt;/h3&gt;
&lt;p&gt;We did this in our INSERT example.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT customerid FROM customer 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will return only the &lt;code&gt;customerid&lt;/code&gt; row. You could think of it like going into Excel and only copying column 'A' from a spreadsheet.&lt;/p&gt;
&lt;h3&gt;Get Data Across Tables&lt;/h3&gt;
&lt;p&gt;This is the real magic of SQL. JOIN. JOINs get crazy complicated but there's one JOIN that has been my bread and butter over the years. The LEFT JOIN. It ties together data so you can see it in one big view, even if it's across tables. Let's use JOIN to get some data from our customers and addresses.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT * from customers
LEFT JOIN addresses
ON customers.customerid = addresses.customerid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will dump both tables into one view. If a customer has multiple addresses, they'll show up in multiple rows with each address. If a customer has no address, they won't show up in this query at all.&lt;/p&gt;
&lt;h2&gt;In Conclusion&lt;/h2&gt;
&lt;p&gt;SQL is very detailed and complicated but if you're just starting to pick it up, hopefully this helps you get started learning SQL.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Syntax to Sawdust: Lessons from Middle School Woodshop</title>
    <link href="https://danschnau.com/blog/syntax_to_sawdust" />
    <id>urn:uuid:1c0135bb-f5af-493d-94e7-028a6e0d9916</id>
    <updated>2023-09-20T12:27:57.827Z</updated>
    <content type="html">&lt;p&gt;My fiancé's job is to provide middle schoolers with hacksaws, orbital sanders, drill presses, solvents, paints, and lumber, and then guide them in the safe usage of these tools and materials.&lt;/p&gt;
&lt;p&gt;With my recent &lt;a href="/hireme"&gt;funemployment&lt;/a&gt;, I've been spending time helping her woodshop students. I learned a lot along the way.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/dan_mitre_saw.jpg" alt="Me, with a mitre saw" /&gt;.&lt;/p&gt;
&lt;h2&gt;Working In Person Makes A Difference&lt;/h2&gt;
&lt;p&gt;I've worked remote for half a decade. Going into the woodshop and working with students at first felt like meeting concrete face-first. By the end of the first class, I was excited and exhilarated from communication and human connection. I have to admit that this, delightfully, surprised me.&lt;/p&gt;
&lt;p&gt;Being on my feet, moving around, shouting, using body language. It is great fun compared to sitting in the same chair talking flatly into the same headset. My health app on my watch was happy about it too.&lt;/p&gt;
&lt;h2&gt;Not Everyone Is At 100% All The Time&lt;/h2&gt;
&lt;p&gt;Some students showed up and worked hard and were at their best one day, and the next they were quiet and lethargic. It became apparent that, just like adults, kids have good days and bad days, and that's nothing to be ashamed about.&lt;/p&gt;
&lt;h2&gt;Making Something Just Because It's Cool Is Fine&lt;/h2&gt;
&lt;p&gt;I worked with the kids sanding wood, applying stain, and pounding nails. I began a side project - sanding blocks of wood to be made into bathroom passes - and I'm excited about it. Slinging code is fun and rewarding, but sanding blocks of wood so students can go use the bathroom during class in an organized fashion is as well.&lt;/p&gt;
&lt;h2&gt;Respect Goes A Very Long Way&lt;/h2&gt;
&lt;p&gt;I noticed that the students would initially misbehave to test my response. When I could respectfuly correct a student, I observed them putting in more effort in class and showed more respect to both myself and their teacher.&lt;/p&gt;
&lt;p&gt;Students who chose not to show respect, including those with prior woodworking experience, were not allowed to participate as their behavior disrupted the rest of the group. It reminded me of the stereotypical 'jerk' ace programmer who looks down on the rest of their team.&lt;/p&gt;
&lt;h2&gt;A Group Of People Can Get A Lot Done Quickly&lt;/h2&gt;
&lt;p&gt;In the mornings before classes began, I would prepare work pieces for children (using the mitre saw, pictured above). I'd need to prepare about 150 pieces, and it would take an hour or two. Last night, my fiancé and I wound 150 spools of thread with a power drill and it took more time than the length of the film &lt;a href="https://www.imdb.com/title/tt0357413/"&gt;Anchorman&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But during class, many hands made for very short work. While it takes me time to do the same thing 150 times, 150 students can do it in 20 minutes. It was easy to see that given a large enough group, well divided work, and strong leadership for that group, A lot can get done quickly.&lt;/p&gt;
&lt;p&gt;Thank goodness I didn't need to sand all 150 pieces of wood by myself.&lt;/p&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: While I would love to share photos of the students working on their projects, I will not, in the interest of respecting their privacy.&lt;/p&gt;
&lt;/blockquote&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Work Is A Gas</title>
    <link href="https://danschnau.com/blog/work_is_a_gas" />
    <id>urn:uuid:eee9a2a4-4501-4850-b552-a42f51407ba5</id>
    <updated>2023-09-15T16:41:06.44Z</updated>
    <content type="html">&lt;p&gt;During the postwar expansion of the United Kingdom, a Naval Historian wrote about a phenomenon that explained why governments move at a glacial pace and at the same time, why it can take months to get a Pull Request merged.&lt;/p&gt;
&lt;p&gt;In tying to yesterdy's article, &lt;a href="https://danschnau.com/blog/localhost_doesnt_need_to_die"&gt;&amp;quot;Localhost Doesn't Need To Die, Except Maybe With Venture Capital&amp;quot;&lt;/a&gt;, I would like to expand on &lt;a href="https://en.wikipedia.org/wiki/Parkinson%27s_law"&gt;Parkinson's Law&lt;/a&gt; and state that &lt;strong&gt;work complicates to fill the available time &lt;strong&gt;and money&lt;/strong&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The three states of matter (I've heard there's also something called 'plasma', but we're going off my high school physics class today) are solid, liquid, and gas. Solids retain their shape, liquids take the shape of their container, and gases &lt;em&gt;expand&lt;/em&gt; and take the shape of their container.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Knowledge work is like a gas.&lt;/strong&gt; it expands to take the shape of its container. It has to be. If it was solid, then knowledge workers would just be on the assembly line. If it was liquid, it would at least be easy to estimate the size of work. But knowledge work has been in the domain of Parkinson's Law since long before it was &lt;a href="https://en.wikipedia.org/wiki/C._Northcote_Parkinson#cite_note-4"&gt;written about in The Economist in 1955&lt;/a&gt;: it takes up everything you give it.&lt;/p&gt;
&lt;p&gt;Knowing this, we can rethink how work gets done. Given some arbitrary objective, and one day to do it, I will likely get it done in one day. If I give myself a week, I will take that week. If I give myself a month and a budget of five thousand dollars, I'll take a month and I'll be five grand poorer.&lt;/p&gt;
&lt;p&gt;Of course, it is possible it can't get done in one day. The &lt;a href="https://en.wikipedia.org/wiki/Ideal_gas_law"&gt;Ideal Gas Law, &lt;code&gt;pV = nRT&lt;/code&gt;&lt;/a&gt; applies to gases. If I squeeze too much in to too small a space, the amount of pressure and temperature may become too much, and I will burn out.&lt;/p&gt;
&lt;p&gt;Since Work is a gas, I think we might all benefit from squeezing work down as tight as we can, and using &lt;a href="https://en.wikipedia.org/wiki/Mindfulness"&gt;mindfulness techniques&lt;/a&gt; to be aware of the rising pressure and temperature, and use that information to blow off steam while the work gets done in the fastest, smallest container that is practical.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Localhost doesn't need to die, except maybe with venture capital</title>
    <link href="https://danschnau.com/blog/localhost_doesnt_need_to_die" />
    <id>urn:uuid:e5f516e0-a3bf-431d-8014-e65a2ee31473</id>
    <updated>2023-09-14T19:23:16.893Z</updated>
    <content type="html">&lt;p&gt;This post is a response to &lt;a href="https://lucasfcosta.com/2023/09/11/localhost-will-die.html?utm_source=danschnaudotcom"&gt;Lucas F. Costa's blog &amp;quot;Localhost will inevitably die&amp;quot;&lt;/a&gt;, which I enjoyed. I don't think localhost has to inevitably die, and I think it's worth preserving.&lt;/p&gt;
&lt;p&gt;In 2008, I ran software from &lt;code&gt;localhost&lt;/code&gt; for the first time. I was in the computer lab at my university, a room full of computers and computer nerds. As hard as the custodians worked, the stale smell of nerd sweat never went away. It was a lab full of not-so-powerful desktop computers running Fedora Linux. I was struggling as a computer science student to draw a line between algorithms and data structures, and making anything interesting on the computer happen, like how Facebook was taking off in popularity, having been written with something called &lt;code&gt;php&lt;/code&gt; and I guess that guy was getting rich slinging code in college the same as me, except I was &lt;a href="/Home/QuitVaping"&gt;losing weight from spending all my money on cigarettes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That night in the fall of 2008 I wrote an application in PHP supported by a MySQL database. It had no styles, just a table of information I could plug in with the HTML &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; I managed to slap together. I understood the &lt;a href="https://en.wikipedia.org/wiki/Relational_algebra"&gt;relational algebra&lt;/a&gt; that powered the database in theory, but this was the first time I could put some of that power into practice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I pointed my browser to http://localhost and I believe an epiphany happened. It was the day I became able to create web applications.&lt;/strong&gt; I could run an app, serve it to my own browser, and make changes to the full stack: database, application, and front end. There was a rush of excitement and I walked home that night through the snow listening to &lt;a href="https://youtu.be/n8suXz-tqT0"&gt;Jay Z and Linkin Park&lt;/a&gt;. I had finally learned a practical skill in college.&lt;/p&gt;
&lt;h2&gt;Keeping localhost alive&lt;/h2&gt;
&lt;p&gt;I have experienced this problem in the past, where you can no longer run your company or team's application on your laptop anymore. Someone I once worked with put it nicely, saying &amp;quot;I should be able to get on an airplane, crack open my laptop, press a button, and run the whole thing&amp;quot;. This was a few years ago, when Wi-Fi wasn't readily available on commercial flights.&lt;/p&gt;
&lt;p&gt;As Lucas wrote, localhost dies as applications become bloated and complex. A huge chunk of code becomes a series of microservices, or someone pitches a CQRS implementation to the manager in a place where it doesn't belong. It's not long until technologies like Azure, Kubernetes, and Terraform enter the discussion.&lt;/p&gt;
&lt;p&gt;That happens when two things happen: A sufficient, but small, number of technical problems happens with running on localhost, and the company has a huge pile of cash to burn.&lt;/p&gt;
&lt;p&gt;If &lt;a href="https://stackexchange.com/performance"&gt;Stack Exchange can do what it does with four SQL servers and 9 web servers&lt;/a&gt;, then most any software can run without using cloud tech to rain down a freshly Terraformed copy of the production Kubernetes cluster every Monday morning. But it happens anyway. I've watched the whole thing happen. I've also watched SQL servers chug on and on in a rock-solid application for years and years.&lt;/p&gt;
&lt;p&gt;I think it has to do with venture capital funding a bunch of developers without much else to do, or an attempt to just scale as big as possible. I'm not really sure. But in the companies I've worked for that didn't take venture capital, the boring software just chugged on and on. And in the companies that I've worked for that did, localhost did indeed die a painful death.&lt;/p&gt;
&lt;h2&gt;Boring tech&lt;/h2&gt;
&lt;p&gt;I'm motivated these days to use the boring tech. If you have a software project that might need to be built good, fast, and cheap, without flashy cutting edge tech, you may want to &lt;a href="/hireme"&gt;hire me&lt;/a&gt; to make it happen.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Time is of the Essence: Crafting an Image Management Solution in One Hour</title>
    <link href="https://danschnau.com/blog/using_azure_storage_and_front_door_for_image_management" />
    <id>urn:uuid:f3140a25-176e-4a9f-80c1-c7412c0bd36e</id>
    <updated>2023-09-12T19:09:15.913Z</updated>
    <content type="html">&lt;p&gt;Today I put together an image management solution for Dan Schnau dot com in about an hour.&lt;/p&gt;
&lt;p&gt;Before today, my image management workflow was as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add the image to the &lt;code&gt;wwwroot/img&lt;/code&gt; directory in source code.&lt;/li&gt;
&lt;li&gt;Make a commit and re-deploy the DanSchnauDotCom app.&lt;/li&gt;
&lt;li&gt;Reference the image in markdown.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Committing and re-deploying with every image became intolerable. It took too long. It wouldn't scale beyond one post per month. These images belong in long-term storage, not in source code.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/Screenshot%202023-09-12%20144707.png" alt="These images have no place in source code, yet here they are." /&gt;&lt;/p&gt;
&lt;h2&gt;The solution: Azure Storage and Azure Front Door&lt;/h2&gt;
&lt;p&gt;First, I created an &lt;a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-account-overview"&gt;Azure Storage Account&lt;/a&gt;. This took five minutes to create and configure. In the Azure portal, you can drag and drop images. That means I can take screenshots and load them into Azure immediately.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/danschnaustorage.png" alt="My new Azure Storage account." /&gt;&lt;/p&gt;
&lt;p&gt;This would have been good enough of a solution, but &lt;a href="https://learn.microsoft.com/en-us/azure/storage/blobs/anonymous-read-access-prevent?tabs=portal"&gt;Microsoft recommends against anonymous public read access to blob data&lt;/a&gt;. It's recommended to use &lt;a href="https://learn.microsoft.com/en-us/azure/frontdoor/front-door-overview"&gt;Azure Front Door&lt;/a&gt;, Microsoft's recommended CDN product, for both security and performance benefits.&lt;/p&gt;
&lt;p&gt;I created a Front Door config and tied it to my storage account. Now images are loaded through a cached CDN, and I can use the generated URLs while typing markdown posts, like the one you've read here, and no commits are needed.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://danschnaudotcom-images-cdf9eudmcea0hcgx.z01.azurefd.net/danschnaudotcom-images/danschnau_frontdoor.png" alt="My Azure Front Door configuration" /&gt;&lt;/p&gt;
&lt;p&gt;I put together an image management solution for my own needs in about an hour. &lt;a href="/hireme"&gt;I can help you with yours as well&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>A Simple Application of OAuth: Mastodon's API</title>
    <link href="https://danschnau.com/blog/this_blog_automatically_posts_to_the_fediverse" />
    <id>urn:uuid:88973885-b8a5-432b-b70f-b3e9443e3b14</id>
    <updated>2023-09-12T15:46:14.337Z</updated>
    <content type="html">&lt;p&gt;A &lt;a href="https://danschnau.com/blog/invest_in_good_user_stories"&gt;User Story&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;As Dan Schnau,
When I publish a new article on Dan Schnau Dot Com,
I want a 'toot' with the title and a link to the article to be posted automatically,
So that followers of the feed can go read the article.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When this post is published, the DanSchnauDotCom application will publish a 'toot' to my &lt;a href="https://toot.cafe/@danschnau"&gt;profile on toot.cafe&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this article are the steps I took to accomplish this goal. If you are looking for someone to implement modern, safe, and secure authentication and authorization flows, you can &lt;a href="https://danschnau.com/hireme"&gt;hire me&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Step One: Register an Application&lt;/h2&gt;
&lt;p&gt;In order to do so, I first created an app in the &lt;code&gt;development&lt;/code&gt; portion of my account at toot.cafe.&lt;/p&gt;
&lt;p&gt;&lt;img src="/img/mastodon_new_app.png" alt="Mastodon New App" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/img/mastodon_app_danschnaudotcom.png" alt="Mastodon App DanSchnauDotCom" /&gt;&lt;/p&gt;
&lt;h2&gt;Step Two: Get User Authorization&lt;/h2&gt;
&lt;p&gt;Then, I could use the &lt;a href="https://docs.joinmastodon.org/spec/oauth/#authorization"&gt;authorization endpoint&lt;/a&gt; to authorize the app with my account at &lt;code&gt;toot.cafe&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The authorization endpoint requires several parameters, which are serialized into the requests query string.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;client_id&lt;/code&gt;: my application Client Key from step one.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;response_type&lt;/code&gt;: &lt;code&gt;code&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scope&lt;/code&gt;: &lt;code&gt;write:statuses&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;redirect_uri&lt;/code&gt;: &lt;code&gt;urn:ietf:wg:oauth:2.0:oob&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;code&gt;client id&lt;/code&gt; is the important part for our user story: it informs the API which application, amongst all that might be running on &lt;code&gt;toot.cafe&lt;/code&gt;, is asking for authorization. The rest are literals related to OAuth and out of scope for this article.&lt;/p&gt;
&lt;p&gt;The serialized request is a big URL:
&lt;code&gt;https://toot.cafe/oauth/authorize?response_type=code&amp;amp;client_id=mz3T_application_client_key_goes_here_nnJk&amp;amp;redirect_uri=urn:ietf:wg:oauth:2.0:oob&amp;amp;scope=write:statuses&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And opening that URL in a browser logged in to toot.cafe and authorizing the app will spit out an authorization code.&lt;/p&gt;
&lt;p&gt;&lt;img src="/img/mastodon_got_authorization_code.png" alt="Mastodon Authorization Code" /&gt;&lt;/p&gt;
&lt;h2&gt;Step Three: Use Authorization Code To Generate A Bearer Token&lt;/h2&gt;
&lt;p&gt;With this authorization code, we can now make an HTTP POST call to get a bearer token.&lt;/p&gt;
&lt;p&gt;We need the &lt;code&gt;client_id&lt;/code&gt; and &lt;code&gt;client_secret&lt;/code&gt; from step one and the Authorization &lt;code&gt;code&lt;/code&gt; from step two.&lt;/p&gt;
&lt;p&gt;The request is an HTTP POST to &lt;code&gt;https://toot.cafe/oauth/token&lt;/code&gt; with &lt;code&gt;form-data&lt;/code&gt; in the request body.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP POST https://toot.cafe/oauth/token
content-type: multipart/form-data

code:KXEcode_from_step_three_goes_hereadk41rtB4
client_id:mz3T4application_client_key_goes_herewnnJk
client_secret:Ul6japplication_client_secret_goes_here3TAILc4Ric
redirect_uri:urn:ietf:wg:oauth:2.0:oob
grant_type:authorization_code
scope:write:statuses

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The response:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP 200 OK
{
    &amp;quot;access_token&amp;quot;: &amp;quot;oaQC_____________________Crc&amp;quot;,
    &amp;quot;token_type&amp;quot;: &amp;quot;Bearer&amp;quot;,
    &amp;quot;scope&amp;quot;: &amp;quot;write:statuses&amp;quot;,
    &amp;quot;created_at&amp;quot;: 1694018755
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;access_token&lt;/code&gt; in this response is the special secret needed to start making HTTP POST requests to write status updates.&lt;/p&gt;
&lt;h2&gt;Step Four: Write Some Code To Make the Post with the Access Token&lt;/h2&gt;
&lt;p&gt;I implemented a new function that would make the http request using the generated authorization code.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;    public class SocialService
    {
        private readonly IHttpClientFactory _httpClientFactory;

        public SocialService(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

        /*
        * Make an HTTP request to toot.cafe with an authorization code to write a status to toot.cafe/@danschnau about the new blog post.
        * 
        * This is a &amp;quot;happy path&amp;quot; implementation with a hard-coded secret, no logging, and no error handling.
        * In &amp;quot;big kid&amp;quot; applications logging, error handling, and secret management must be implemented.
        */
        public async Task PostToMastodonAsync(Blog blog)
        {
            using HttpClient client = _httpClientFactory.CreateClient();

            client.DefaultRequestHeaders.Add(&amp;quot;Authorization&amp;quot;, &amp;quot;Bearer oaQ____________________________iCrc&amp;quot;);

            // Make HTTP Post Request
            var url = &amp;quot;https://toot.cafe/api/v1/statuses&amp;quot;;
            var content = new
            {
                status = $&amp;quot;New Post: {blog.Title} - https://danschnau.com/blog/{blog.Slug}&amp;quot;
            };

            await client.PostAsync(url, JsonContent.Create(content));
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;In Conlusion&lt;/h2&gt;
&lt;p&gt;It took more time to write this blog post than it did to implement the functionality discussed in this blog post. The usage of OAuth in this article is narrow in scope, and has much more to do with the protocol than it does with toot.cafe, Mastodon, or the fediverse. I can quickly, safely, and securely implement Authorization and Authentication flows for your business applications using some of the skills I've demonstrated here and you can &lt;a href="https://danschnau.com/hireme"&gt;hire me&lt;/a&gt; to do so.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>I have been laid off.</title>
    <link href="https://danschnau.com/blog/i-have-been-laid-off" />
    <id>urn:uuid:1d739db7-865c-4035-93ea-3722dc30a922</id>
    <updated>2023-09-12T15:43:11.77Z</updated>
    <content type="html">&lt;p&gt;I have been laid off from my job.&lt;/p&gt;
&lt;p&gt;I learned that unexpectedly getting laid off from your job feels a lot like a relationship ending. I experienced a lot of difficult feelings in a short amount of time.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.ibtimes.com/software-startup-skykick-cuts-181-jobs-after-march-layoffs-3710220"&gt;SkyKick laid off 181 employees&lt;/a&gt;, about two-thirds of its workforce. My job at SkyKick was among the layoffs.&lt;/p&gt;
&lt;p&gt;Instead of immediately taking  another full-time job, I will work independently - seeking short-term contracts and building projects of my own. &lt;a href="mailto:dsschnau@gmail.com"&gt;Contact me&lt;/a&gt; if you or someone you know may be interested in my services.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Intermittent SSL errors pushing and pulling code</title>
    <link href="https://danschnau.com/blog/why_do_i_have_to_pull_so_much" />
    <id>urn:uuid:23c3505f-d9ae-4f86-a684-8e7d05a86cec</id>
    <updated>2023-05-30T13:07:25.04Z</updated>
    <content type="html">&lt;p&gt;For about a year, I've had a problem where &lt;code&gt;git push&lt;/code&gt; and &lt;code&gt;git pull&lt;/code&gt; from &lt;code&gt;dev.azure.com&lt;/code&gt; would intermittently fail with an error like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cmd"&gt;~#@❯ git pull                                                                                               ❮  
fatal: unable to access 'https://dev.azure.com/dsschnau/_git/codecodecode/': OpenSSL SSL_connect: Connection was reset in connection to dev.azure.com:443
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then I'd have to re-try, sometimes about a dozen times.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cmd"&gt;~#@❯ git pull                                                                                               ❮  
fatal: unable to access 'https://dev.azure.com/dsschnau/_git/codecodecode/': OpenSSL SSL_connect: Connection was reset in connection to dev.azure.com:443
~#@❯ git pull                                                                                               ❮  
fatal: unable to access 'https://dev.azure.com/dsschnau/_git/codecodecode/': OpenSSL SSL_connect: Connection was reset in connection to dev.azure.com:443
~#@❯ git pull                                                                                               ❮  
fatal: unable to access 'https://dev.azure.com/dsschnau/_git/codecodecode/': OpenSSL SSL_connect: Connection was reset in connection to dev.azure.com:443
~#@❯ git pull                                                                                               ❮  
fatal: unable to access 'https://dev.azure.com/dsschnau/_git/codecodecode/': OpenSSL SSL_connect: Connection was reset in connection to dev.azure.com:443
~#@❯ git pull                                                                                               ❮  
fatal: unable to access 'https://dev.azure.com/dsschnau/_git/codecodecode/': OpenSSL SSL_connect: Connection was reset in connection to dev.azure.com:443
~#@❯ git pull                                                                                               ❮  
fatal: unable to access 'https://dev.azure.com/dsschnau/_git/codecodecode/': OpenSSL SSL_connect: Connection was reset in connection to dev.azure.com:443
~#@❯ git pull                                                                                               ❮  
fatal: unable to access 'https://dev.azure.com/dsschnau/_git/codecodecode/': OpenSSL SSL_connect: Connection was reset in connection to dev.azure.com:443
~#@❯ git pull                                                                                               ❮  
fatal: unable to access 'https://dev.azure.com/dsschnau/_git/codecodecode/': OpenSSL SSL_connect: Connection was reset in connection to dev.azure.com:443
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But eventually it would work.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cmd"&gt;~#@❯ git pull                                                                                              ❮   1
Already up to date.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I searched the web and found a StackOverflow question that suggested a workaround: add a HOSTS file entry to force &lt;code&gt;dev.azure.com&lt;/code&gt; to use an ipv4 address. That suggests the intermittent error has to do with ipv6.&lt;/p&gt;
&lt;p&gt;I added this entry, and now my console pushes and pulls without SSL failures.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &amp;quot;13.107.42.20 dev.azure.com&amp;quot; &amp;gt; C:/Windows/System32/drivers/etc/HOSTS
&lt;/code&gt;&lt;/pre&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>The Magic P Words</title>
    <link href="https://danschnau.com/blog/The_Magic_P_Words" />
    <id>urn:uuid:f404ce94-7f39-42e2-8a02-d8e8fc39f48c</id>
    <updated>2023-05-05T16:37:55.517Z</updated>
    <content type="html">&lt;p&gt;When I offer my kids advice, I'm really just giving myself advice. Here are the &amp;quot;Magic P Words&amp;quot; I use.&lt;/p&gt;
&lt;h2&gt;Planning&lt;/h2&gt;
&lt;p&gt;I think I offer this Magic P word to myself more than my children. Planning is so important for achieving anything, especially new things.&lt;/p&gt;
&lt;p&gt;Sure, Mike Tyson said &lt;a href="https://twitter.com/MikeTyson/status/1052665864401633299?s=20"&gt;&amp;quot;Everyone has a plan until they get punched in the mouth.&amp;quot;&lt;/a&gt;. But it also holds true that &lt;a href="https://military-history.fandom.com/wiki/7_Ps_(military_adage)"&gt;Proper Planning and Preparation Prevents Piss Poor Performance&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Please&lt;/h2&gt;
&lt;p&gt;We all need periodic reminders to use our manners.&lt;/p&gt;
&lt;h2&gt;Patience&lt;/h2&gt;
&lt;p&gt;Good things come to those who wait.&lt;/p&gt;
&lt;h2&gt;Practice&lt;/h2&gt;
&lt;p&gt;Good things come to those who practice. In practice, it is really the only way to get better at something. The rest is just theory.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>I Got an Invitiation to The AI-Powered Microsoft Designer. It's Rad.</title>
    <link href="https://danschnau.com/blog/microsoft_designer_is_cool_ai" />
    <id>urn:uuid:5ec458d9-49d6-4b30-98ae-3e58b29aaf34</id>
    <updated>2023-04-21T23:45:20.193Z</updated>
    <content type="html">&lt;p&gt;I got an invitation to &lt;a href="https://designer.microsoft.com/"&gt;Microsoft Designer, an AI-Powered design tool&lt;/a&gt;. I tried it out by sampling some save-the-date invitations. The results are really cool.&lt;/p&gt;
&lt;p&gt;From a simple prompt, I got some great options to start.&lt;/p&gt;
&lt;img src="/img/save_the_date_1.jpg" alt="Microsoft Designer made some samples."&gt;
&lt;p&gt;Then Microsoft Designer lets you drill into the image and customize it with a bunch of tools.&lt;/p&gt;
&lt;img src="/img/save_the_date_2.jpg" alt="Microsoft Designer includes cool tools."&gt;
&lt;p&gt;Microsoft Designer looks like a killer application of artificial intelligence.&lt;/p&gt;
&lt;p&gt;Just a few ideas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Wedding/Party invitations&lt;/li&gt;
&lt;li&gt;School/Church fliers&lt;/li&gt;
&lt;li&gt;Application Announcement emails&lt;/li&gt;
&lt;li&gt;Classroom posters&lt;/li&gt;
&lt;li&gt;Brochure designs&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>.NET 8 Preview 3: Simplified Output Path format for Builds</title>
    <link href="https://danschnau.com/blog/net-8-artifacts-directory" />
    <id>urn:uuid:c259946c-2b71-421c-b58b-c16b57dc4621</id>
    <updated>2023-04-20T19:31:26.33Z</updated>
    <content type="html">&lt;p&gt;If you're like me, you're used to finding your .NET build artifacts in &lt;code&gt;/bin&lt;/code&gt; and &lt;code&gt;/obj&lt;/code&gt; directories inside your source projects. It's been this way my whole career.&lt;/p&gt;
&lt;p&gt;In .NET 8 Preview 3, this can be changed to dump the &lt;code&gt;/bin&lt;/code&gt; and &lt;code&gt;/obj&lt;/code&gt; artifacts into an &lt;code&gt;.artifacts&lt;/code&gt; directory instead of the source root.&lt;/p&gt;
&lt;p&gt;.NET Manager Chet Husk Writes about it &lt;a href="https://github.com/dotnet/core/issues/8135#issuecomment-1483357684"&gt;in the comments of 'What's new in .NET 8 Preview 3' discussion&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I'd like to test this little feature out. First, I needed to install &lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/8.0"&gt;.NET 8 Preview 3&lt;/a&gt;. It required a system restart.&lt;/p&gt;
&lt;p&gt;First, a new console app in a directory &lt;code&gt;testartifactsoutput&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-powershell"&gt;~#@❯ dotnet new console                                                                                                     ❮  

Welcome to .NET 8.0!
---------------------
SDK Version: 8.0.100-preview.3.23178.7

Telemetry
---------
The .NET tools collect usage data in order to help us improve your experience..... *SNIP*

Read more about .NET CLI Tools telemetry: https://aka.ms/dotnet-cli-telemetry

----------------
Installed an ASP.NET Core HTTPS development certificate.
To trust the certificate run 'dotnet dev-certs https --trust' (Windows and macOS only).
Learn about HTTPS: https://aka.ms/dotnet-https
----------------
Write your first app: https://aka.ms/dotnet-hello-world
Find out what's new: https://aka.ms/dotnet-whats-new
Explore documentation: https://aka.ms/dotnet-docs
Report issues and find source on GitHub: https://github.com/dotnet/core
Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli
--------------------------------------------------------------------------------------
The template &amp;quot;Console App&amp;quot; was created successfully.

Processing post-creation actions...
Restoring C:\danschnauprojects\testartifactsoutput\testartifactsoutput.csproj:
  Determining projects to restore...
  Restored C:\danschnauprojects\testartifactsoutput\testartifactsoutput.csproj (in 76 ms).
Restore succeeded.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The default behavior is still to build to the &lt;code&gt;/bin&lt;/code&gt; directory.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-powershell"&gt;~#@❯ dotnet build                                                                                                           ❮  
MSBuild version 17.6.0-preview-23174-01+e7de13307 for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
C:\Program Files\dotnet\sdk\8.0.100-preview.3.23178.7\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.RuntimeIdentifierInference.targ
ets(287,5): message NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy [C:\danschnaupr
ojects\testartifactsoutput\testartifactsoutput.csproj]
  testartifactsoutput -&amp;gt; C:\danschnauprojects\testartifactsoutput\bin\Debug\net8.0\testartifactsoutput.dll
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we create a &lt;code&gt;Directory.Build.Props&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-powershell"&gt;~#@❯ dotnet new buildprops                                                                                                  ❮  
The template &amp;quot;MSBuild Directory.Build.props file&amp;quot; was created successfully.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The buildprops file is pretty sparse.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-xml"&gt;&amp;lt;Project&amp;gt;
  &amp;lt;!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build --&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;

  &amp;lt;/PropertyGroup&amp;gt;
&amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We add the &lt;code&gt;UseArtifactsOutput&lt;/code&gt; flag to the file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-xml"&gt;&amp;lt;Project&amp;gt;
  &amp;lt;!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build --&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;UseArtifactsOutput&amp;gt;true&amp;lt;/UseArtifactsOutput&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
&amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I cleaned up the build by deleting the &lt;code&gt;/bin&lt;/code&gt; and &lt;code&gt;obj&lt;/code&gt; directories.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-powershell"&gt;~#@❯ ls                                                                                                                     ❮  


    Directory: C:\danschnauprojects\testartifactsoutput


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         4/20/2023   3:25 PM            212 Directory.Build.props
-a----         4/20/2023   3:20 PM            105 Program.cs
-a----         4/20/2023   3:20 PM            252 testartifactsoutput.csproj
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now, a build:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-powershell"&gt;~#@❯ dotnet build                                                                                                           ❮  
MSBuild version 17.6.0-preview-23174-01+e7de13307 for .NET
  Determining projects to restore...
  Restored C:\danschnauprojects\testartifactsoutput\testartifactsoutput.csproj (in 64 ms).
C:\Program Files\dotnet\sdk\8.0.100-preview.3.23178.7\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.RuntimeIdentifierInference.targ
ets(287,5): message NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy [C:\danschnaupr
ojects\testartifactsoutput\testartifactsoutput.csproj]
  testartifactsoutput -&amp;gt; C:\danschnauprojects\testartifactsoutput\.artifacts\bin\testartifactsoutput\debug\testartifactsoutput.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:01.20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ooh, I see the &lt;code&gt;.artifacts&lt;/code&gt; directory in the output there.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;/bin&lt;/code&gt; and &lt;code&gt;/obj&lt;/code&gt; directories are now in their own &lt;code&gt;/.artifacts&lt;/code&gt; folder. Sweet.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-powershell"&gt;~#@❯ ls                                                                                                         ❮  1s 452ms   


    Directory: C:\danschnauprojects\testartifactsoutput


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         4/20/2023   3:26 PM                .artifacts
-a----         4/20/2023   3:25 PM            212 Directory.Build.props
-a----         4/20/2023   3:20 PM            105 Program.cs
-a----         4/20/2023   3:20 PM            252 testartifactsoutput.csproj

~#@❯ ls .\.artifacts\                                                                                                       ❮  


    Directory: C:\danschnauprojects\testartifactsoutput\.artifacts


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         4/20/2023   3:26 PM                bin
d-----         4/20/2023   3:26 PM                obj
&lt;/code&gt;&lt;/pre&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Identity Protocol improvements in ASP.NET Core 8</title>
    <link href="https://danschnau.com/blog/asp-net-core-8-identity-1" />
    <id>urn:uuid:a56b2bf1-3981-4cff-8d39-43398f9c2501</id>
    <updated>2023-04-10T22:56:48.923Z</updated>
    <content type="html">&lt;p&gt;Last week, &lt;a href="https://devblogs.microsoft.com/dotnet/improvements-auth-identity-aspnetcore-8/"&gt;Jeremy Likness discussed improvements to Authentication and Identity features in ASP.NET Core 8&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I wanted to try some of it out, so I first went to install &lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/8.0"&gt;dotnet 8.0.0-preview.2&lt;/a&gt;. I think the downloads page looks slick.&lt;/p&gt;
&lt;h2&gt;OpenID Connect, IdentityServer, and Duende&lt;/h2&gt;
&lt;p&gt;Earlier this week, I talked about the terms &lt;a href="https://danschnau.com/blog/authentication-vs-authorization"&gt;Authentication vs Authorization&lt;/a&gt;. Now I'll use this chance to tie the terms to more technical details.&lt;/p&gt;
&lt;p&gt;ASP.NET Core, in the past, used a third-party implementation of OpenID Connect called IdentityServer to provide authentication and authorization services. IdentityServer is an implementation of the OpenId Conect protocol, which itself is a layer over the OAuth 2.0 protocol that provides Authentication, which is a protocol for Authorization.&lt;/p&gt;
&lt;p&gt;The Authorization story here starts with the &lt;a href="https://www.rfc-editor.org/rfc/rfc6749"&gt;OAuth 2.0 Authorization Framework&lt;/a&gt;. Oauth 2.0 is a framework for dealing with limitations of client-server authentication. There are a lot of good reasons to not implement client-server authentication. From this developer's perspective, the most attractive reason is that with OAuth, I as a software engineer, don't need to spend the time and money on implementing password security. This is because the OAuth protocol specifies an authorization layer that separates the client and server, enabling third parties to provide Authorization services.&lt;/p&gt;
&lt;p&gt;OAuth 2.0 provides an &lt;em&gt;authorization&lt;/em&gt; implementation. OpenID Connect provides an &lt;em&gt;authentication&lt;/em&gt; implementation. And both are just protocols. That is - protocols that are instructions for software to implement to work with other software that implements the same protocols.&lt;/p&gt;
&lt;p&gt;Back to the beginning. Until version 8, ASP.NET Core used a third party implementation of OpenId Connect called &lt;a href="https://identityserver4.readthedocs.io/en/latest/"&gt;IdentityServer&lt;/a&gt;. In version 8, this dependency is removed, and developers will be able to pick from IdentityServer or any other number of OpenId Connect and OAuth 2.0 implementations.&lt;/p&gt;
&lt;p&gt;Ostensibly, these changes will make it easier to create and ship &amp;quot;simple&amp;quot; auth systems that do things like use social logins - and I look forward to trying them out as they become available.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Authentication vs Authorization</title>
    <link href="https://danschnau.com/blog/authentication-vs-authorization" />
    <id>urn:uuid:bceaf811-70ec-43bb-b8a2-45a51074bcc7</id>
    <updated>2023-04-10T14:14:51.513Z</updated>
    <content type="html">&lt;p&gt;Authentication and Authorization (AuthN and AuthZ) are terms that refer to discrete, specific concepts that can easily be confused.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authentication&lt;/strong&gt; is the process of proving that someone is who they claim to be.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authorization&lt;/strong&gt; is the process of granting access to a resource.&lt;/p&gt;
&lt;p&gt;For instance, to buy alcohol, I must be at least 21 years old, as per legal requirements. When I try to make the purchase, the sales clerk will likely request my driver's license. By examining the photo on the license and confirming it to be of me, the sales clerk is performing &lt;strong&gt;Authentication&lt;/strong&gt;. I have presented evidence that I am Dan Schnau, and provided a government-issued document with a photograph and other relevant facts, verifying my identity.&lt;/p&gt;
&lt;p&gt;However, at this moment, the sales clerk has not allowed me access to the resource- alcohol. After proving my identity, the sales clerk will perform &lt;strong&gt;Authorization.&lt;/strong&gt; The clerk will verify my date of birth on the driver's license, which will enable them to grant access, knowing that I have already proved who I am (via photo ID) and that I am authorized based on my age.&lt;/p&gt;
&lt;p&gt;In summary, Authentication verifies identities, and Authorization grants access to resources based on those identities. It is critical to implement both measures for safe systems.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>I asked ChatGPT to write 500 words about the software industry.</title>
    <link href="https://danschnau.com/blog/chatgpt-writes-software-industry" />
    <id>urn:uuid:8ac453e3-1ac4-46a7-99d3-ea05571cfc87</id>
    <updated>2023-03-31T19:21:05.35Z</updated>
    <content type="html">&lt;p&gt;As an AI language model, it is difficult for me to express opinions or personal bias since my responses are generated entirely based on statistical patterns from the dataset I was trained on. However, I can provide a general overview of the current state of the software industry.&lt;/p&gt;
&lt;p&gt;The software industry is one of the fastest-growing industries in the world. It has transformed almost every aspect of daily life, from how we communicate with each other to how we conduct business. The industry has seen exponential growth in the past few decades, with the increasing availability of processing power, deep learning techniques, and cloud computing. Software development has become easier, faster, and more affordable than ever before.&lt;/p&gt;
&lt;p&gt;One of the key drivers of the industry's growth has been the rise of mobile devices. With more than 3 billion smartphones in use globally, software developers have had to adapt to the rapidly changing behavior and needs of consumers. This has led to a surge in the development of mobile applications, which have become indispensable for both business and personal use.&lt;/p&gt;
&lt;p&gt;Another key driver of the software industry's growth has been the shift to cloud computing. Cloud computing has enabled businesses of all sizes to access affordable computing power and storage without having to set up their own IT infrastructure. This has democratized technology, making it accessible to small and medium-sized enterprises that previously did not have the resources to invest in IT infrastructure.&lt;/p&gt;
&lt;p&gt;The software industry has also seen a shift in the way software is developed. The traditional model of developing software has been replaced by more agile approaches such as DevOps, which emphasizes collaboration, agility, and rapid iterations. These approaches have enabled businesses to be more responsive to market demands and to deliver software products faster.&lt;/p&gt;
&lt;p&gt;However, the increasing reliance on software has also led to concerns around security and privacy. The rise of cybercrime has highlighted the need for secure software development practices, and companies are increasingly investing in cybersecurity measures to protect their assets and customers' information.&lt;/p&gt;
&lt;p&gt;The software industry has also seen a shift in the business model, with more companies embracing open-source software. Open-source software allows businesses to access the source code of software products, customize them to meet their needs, and contribute back to the community. This has led to a flourishing ecosystem of software developers who share their knowledge and collaborate on projects.&lt;/p&gt;
&lt;p&gt;Finally, the software industry is undergoing a shift towards automation and artificial intelligence (AI). AI has the potential to transform the way we develop software, making it more efficient and effective. AI-powered tools can help identify bugs and optimize code, freeing up developers' time to focus on more complex tasks. However, there are concerns around the impact of automation on jobs, and the industry must find a way to balance the benefits of automation with the need to protect jobs and skills.&lt;/p&gt;
&lt;p&gt;In conclusion, the software industry is in a state of constant evolution. As technology continues to advance and consumer behavior changes, software developers must adapt to stay ahead of the curve. The industry's growth and increasing importance in almost every aspect of daily life are indicative of the potential it holds for the future. However, the industry must also address the challenges it faces, such as cybersecurity and the impact of automation on jobs. Overall, the software industry is a vital and exciting industry, and its future is full of promise.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Rules for Overcoming Worry</title>
    <link href="https://danschnau.com/blog/rules_for_covercoming_worry" />
    <id>urn:uuid:db7e577a-5200-4756-bba1-a0d7f502f256</id>
    <updated>2023-01-25T16:52:32.023Z</updated>
    <content type="html">&lt;p&gt;A few years ago I visited a &lt;a href="https://www.twicesoldtales.com/"&gt;used book store in Seattle&lt;/a&gt;. It had several cats hanging around with the customers, which was pretty cool.&lt;/p&gt;
&lt;p&gt;I found a copy of &lt;a href="https://www.amazon.com/How-Stop-Worrying-Start-Living/dp/0671733354?&amp;amp;_encoding=UTF8&amp;amp;tag=danschnaudotc-20&amp;amp;linkCode=ur2&amp;amp;linkId=1dbe2f10c8989ac29a769902ed4204cd&amp;amp;camp=1789&amp;amp;creative=9325"&gt;How To Stop Worrying and Start Living&lt;/a&gt; by Dale Carnegie, of &amp;quot;How to Win Friends and Influence People&amp;quot; fame.&lt;/p&gt;
&lt;p&gt;I bought it, and it's been a great book to keep around when I get stressed out.&lt;/p&gt;
&lt;p&gt;One of the exercises in the book suggests creating your own rules for overcoming worry. I wrote my answers down and have copied them here.&lt;/p&gt;
&lt;h2&gt;Rules for Overcoming Worry&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Remember that every moment is new. Past performance is not indicative of future results.&lt;/li&gt;
&lt;li&gt;Meditate. Breathe relaxation and loving-kindness into your muscles, from head to toe.&lt;/li&gt;
&lt;li&gt;Remember what your mother told you when you were a stressed out child (and has been so right about): &amp;quot;Most of the things you worry about will never even happen&amp;quot;.&lt;/li&gt;
&lt;li&gt;Focus on the present day in front of you. The distant future is of no concern. As Apollo Creed told Rocky Balboa as he trained to fight Clubber Lang: &amp;quot;There is no tomorrow!&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I put a lot of energy into stayling relaxed these days. I know that being stressed out will not only not help but rather it will harm me.&lt;/p&gt;
&lt;h4&gt;Affiliate notice&lt;/h4&gt;
&lt;p&gt;This page contains Amazon affiliate links.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Good Software Engineers are Business Analysts Too</title>
    <link href="https://danschnau.com/blog/engineers_find_specifications_as_business_analysts" />
    <id>urn:uuid:b5a5198e-2718-4351-a478-2c6a3f1f9616</id>
    <updated>2023-01-20T18:31:35.77Z</updated>
    <content type="html">&lt;p&gt;Good software engineers have to understand the business they're working in.&lt;/p&gt;
&lt;p&gt;If they don't, they're at the mercy of business analysts and project managers to explain what to do.&lt;/p&gt;
&lt;p&gt;Some of my most successful moments in my career have been when I've taken on a role of business analyst and gone directly to the source for information on what needs to be built for the business.&lt;/p&gt;
&lt;p&gt;In other words:&lt;/p&gt;
&lt;img src="/img/specifications.png" alt="specifications"&gt;
&lt;p&gt;You gotta write code, and be this person too.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>ChatGPT Is at the Peak of the Hype Cycle</title>
    <link href="https://danschnau.com/blog/ChatGPT_peak_of_hype" />
    <id>urn:uuid:a0bea4a4-40c0-4e7a-a5f5-9cd502183190</id>
    <updated>2023-01-19T16:01:39.7Z</updated>
    <content type="html">&lt;p&gt;ChatGPT is at the peak of the hype cycle.&lt;/p&gt;
&lt;img src="/img/peak_of_expectations.png" alt="peak of expectations" /&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Yes, let's deploy to production.</title>
    <link href="https://danschnau.com/blog/yes-lets-deploy-to-production" />
    <id>urn:uuid:f6c3f5d9-e6c9-420e-86da-030da8018b37</id>
    <updated>2022-12-18T01:35:51.223Z</updated>
    <content type="html">&lt;p&gt;A friend (a geniuine friend, who I truly respect, both personally and professionally) shared this video with me the other day.&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/5p8wTOr8AbU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;The video is pretty funny. But it touches on something I'd like to see change.&lt;/p&gt;
&lt;p&gt;If I'm a paying customer for some software in development, I'd much rather have some partially working software that I paid for than &lt;em&gt;nothing&lt;/em&gt;. So management will push to ship something.&lt;/p&gt;
&lt;p&gt;If I'm a manager, I want to show my customer that we're working for them by delivering &lt;em&gt;deliverables&lt;/em&gt; on a schedule.&lt;/p&gt;
&lt;p&gt;If I'm a software engineer, and I shit on those trying to buy and sell software just because the software isn't &lt;em&gt;perfect&lt;/em&gt;, then I'm not doing my job.&lt;/p&gt;
&lt;p&gt;Perfection is an illusion. Perfectionism is a mental health issue.&lt;/p&gt;
&lt;p&gt;Yes, let's deploy to production. Deploy it and watch where things go wrong. Improve from there. &lt;a href="https://danschnau.com/blog/agile_is_meaningless"&gt;You'll never do agile right&lt;/a&gt;, but you can &lt;em&gt;be&lt;/em&gt; agile. Without deploying to production, and failing, there won't ever be any real success.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Find your Application Insights node from the Instrumentation Key with Azure CLI</title>
    <link href="https://danschnau.com/blog/get_application_insights_from_instrumentation_key" />
    <id>urn:uuid:e5947f38-99a7-4ef8-bed7-63f12cde3101</id>
    <updated>2022-11-30T14:06:54.787Z</updated>
    <content type="html">&lt;p&gt;I had an instrumentation key but I didn't know what instance of application insights telemetry was getting pumped into.&lt;/p&gt;
&lt;p&gt;After some fiddling I got a one-line command in Azure CLI to get an Application Insights instance from an instrumentation key.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;az graph query -q &amp;quot;Resources | where properties.InstrumentationKey =~ '00000000-0000-0000-0000-000000000000'&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The command spits out a JSON blob of information about any instances that match the query.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>You'll Never Do Agile Right</title>
    <link href="https://danschnau.com/blog/agile_is_meaningless" />
    <id>urn:uuid:f0b9153e-9031-49bd-95ac-3da4c2bb7d6a</id>
    <updated>2022-11-23T19:39:27.31Z</updated>
    <content type="html">&lt;p&gt;In the article for &lt;a href="https://en.wikipedia.org/wiki/Agile_software_development"&gt;Agile Software Development&lt;/a&gt;, there are six thousand, eight hundred words and over 100 references.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://agilemanifesto.org/"&gt;Agile Mainfesto&lt;/a&gt; is 73 words. Gobs and gobs of software has been written to &amp;quot;do agile&amp;quot;. Every software shop I've encountered in the last decade has told me they use an agile software development process. Being an &amp;quot;Agile coach&amp;quot; is an entire career.&lt;/p&gt;
&lt;p&gt;But the whole manifesto is just not all that.&lt;/p&gt;
&lt;p&gt;The entire body reads as follows:&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;We are uncovering better ways of developing
software by doing it and helping others do it.
Through this work we have come to value:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Individuals and interactions&lt;/strong&gt; over processes and tools&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Working software&lt;/strong&gt; over comprehensive documentation&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Customer collaboration&lt;/strong&gt; over contract negotiation&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Responding to change&lt;/strong&gt; over following a plan&lt;/p&gt;
&lt;p&gt;That is, while there is value in the items on
the right, we value the items on the left more.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;That's it. Focus on relationships with people, and actually solving problems.&lt;/p&gt;
&lt;p&gt;Moreover, the Manifesto ends with a sentence to weasel out of any actual firm stance. &lt;em&gt;The other stuff is also valuable&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You'll never do Agile right, because there's no way to prove that you're doing it right. Don't make user stories and have retrospective meetings just because it says to do so in some book or by some coach.&lt;/p&gt;
&lt;p&gt;Do what works for whatever your situation. Just make sure your values and priorities are in order. That's the whole manifesto.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>INVEST in Good User Stories</title>
    <link href="https://danschnau.com/blog/invest_in_good_user_stories" />
    <id>urn:uuid:60646a12-1509-4f2c-972c-9926bdc0dc7f</id>
    <updated>2022-04-20T18:24:44.183Z</updated>
    <content type="html">&lt;p&gt;Software isn't always complicated.&lt;/p&gt;
&lt;p&gt;But when you work on big, complicated software, with lots of people, it does get complicated.&lt;/p&gt;
&lt;p&gt;It's easy to get stuck in the mud with big software and lots of people working remotely.&lt;/p&gt;
&lt;p&gt;So communication is very important. Planning your work is very important.&lt;/p&gt;
&lt;p&gt;I think it's very corny. But I have to admit that writing good user stories is important if you want yourself and your team to be effective.&lt;/p&gt;
&lt;p&gt;Assuming you're a software engineer.&lt;/p&gt;
&lt;p&gt;I asked SkyKick's resident expert on the subject: &amp;quot;What Makes a good User Story?&amp;quot; and I got back the INVEST criteria.&lt;/p&gt;
&lt;p&gt;Independent. The story doesn't have dependencies or prerequisites.&lt;/p&gt;
&lt;p&gt;Negotiable. The story isn't so rigid as to be brittle. Details can change and things can be rewritten. The important part of the story - the actual value of it - remains.&lt;/p&gt;
&lt;p&gt;Valuable. Your Tech Debt Story Doesn't Matter If It Isn't Valuable To The Stakeholders. Stories completed need to be delivering value. We aren't getting these fat paychecks for nothing.&lt;/p&gt;
&lt;p&gt;Estimable. If you have no idea how much effort it will take to get the work done, not even a reasonable guess, the story is worthless. We're trying to plan our Big Kid work at our Big Kid Company.&lt;/p&gt;
&lt;p&gt;Small. If it's so big you can't get it done in a couple of days, it's not a story. It's an Epic, and multiple smaller stories need to be created under that epic.&lt;/p&gt;
&lt;p&gt;Testable. You gotta be able to prove if it worked or not. Full stop.&lt;/p&gt;
&lt;p&gt;If you gloss over these and write crappy user stories at work, you're going to have a bad time.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>I Went to a Mental Health Hospital</title>
    <link href="https://danschnau.com/blog/i_went_to_a_mental_health_hospital" />
    <id>urn:uuid:1dcdc5eb-c901-42c7-ac49-34df5231d0dd</id>
    <updated>2021-07-03T21:13:00.163Z</updated>
    <content type="html">&lt;p&gt;Shortly after I wrote the &lt;a href="https://danschnau.com/blog/suicidal_ideation"&gt;&amp;quot;Suicidal Ideation&amp;quot;&lt;/a&gt; post, I had a really really bad day.&lt;/p&gt;
&lt;p&gt;I had told my therapist that I was thinking about suicide.&lt;/p&gt;
&lt;p&gt;I had told my therapist that I had a specific plan.&lt;/p&gt;
&lt;p&gt;My therapist asked for my wife's phone number to contact in case of emergency.&lt;/p&gt;
&lt;p&gt;That afternoon in February, I locked myself in the bedroom.&lt;/p&gt;
&lt;p&gt;It was enough to call it an emergency.&lt;/p&gt;
&lt;p&gt;Police entered my home and took me away in handcuffs to a hospital of my choosing.&lt;/p&gt;
&lt;p&gt;I spent two nights on suicide watch in a hospital gown in a stripped-bare room.&lt;/p&gt;
&lt;p&gt;I was then taken via ambulance to a mental health hospital. I spent another five nights there.&lt;/p&gt;
&lt;p&gt;At the mental health hospital, I began taking antidepressants and participating in group therapies.&lt;/p&gt;
&lt;p&gt;I made a few friends there.&lt;/p&gt;
&lt;p&gt;After I left the mental hospital, I began telling friends and family what happened. &lt;strong&gt;Everyone was supportive.&lt;/strong&gt; My relationships with my friends and family strengthened.&lt;/p&gt;
&lt;p&gt;The antidepressants have been effective for me. I consult with a psychiatrist regularly about adjusting the medications.&lt;/p&gt;
&lt;p&gt;I still meet with my therapist regularly.&lt;/p&gt;
&lt;p&gt;I'm glad I'm here today.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Two Rules of Thumb for Day-Job Programming</title>
    <link href="https://danschnau.com/blog/two_rules_of_thumb" />
    <id>urn:uuid:345788c0-c311-4fa2-a7c4-951b73c93d86</id>
    <updated>2021-06-24T19:26:42.62Z</updated>
    <content type="html">&lt;p&gt;Rule #1: Never commit code you haven't run &lt;em&gt;at least once&lt;/em&gt;, no matter how small or simple.&lt;/p&gt;
&lt;p&gt;Rule #2: Have a plan, and execute that plan, for verifying changes and fixes when they go into production.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>npm errors with node-sass, node-sass deprecated, drop-in replacement available</title>
    <link href="https://danschnau.com/blog/node-sass-deprecated" />
    <id>urn:uuid:da2f301e-5f29-45ad-8f98-a4c8779aeb43</id>
    <updated>2021-06-23T15:17:59.633Z</updated>
    <content type="html">&lt;p&gt;The &lt;code&gt;node-sass&lt;/code&gt; npm package has given me problems on various projects at various companies over the years. It's a js wrapper around a python implementation of Sass, a CSS pre-processor. Or maybe the python is there to run a ruby script, or something like that. I'm not really sure, I just want the package I &lt;code&gt;npm install&lt;/code&gt;'d to work.&lt;/p&gt;
&lt;p&gt;Errors usually look something like this: &lt;code&gt;Error: node-sass@4.13.0 postinstall: node scripts/build.js&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The first thing that usually happens is that it works on my machine but not my build machine, or vice-versa. That's usually resolved by making sure versions of &lt;code&gt;node&lt;/code&gt; and &lt;code&gt;npm&lt;/code&gt; are up to date on all invested parties.&lt;/p&gt;
&lt;p&gt;Then I usually get the error locally complaining about Python not being available/not in the right place.&lt;/p&gt;
&lt;p&gt;But today, I found out that &lt;a href="https://sass-lang.com/blog/libsass-is-deprecated"&gt;node-sass is deprecated&lt;/a&gt;. The replacement is just &lt;a href="https://www.npmjs.com/package/sass"&gt;sass&lt;/a&gt;, which is a Dart implementation of sass.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm uninstall node-sass&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm install sass&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Done. A quick smoke test and ready to deploy.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Truthiness and Falsiness in JavaScript</title>
    <link href="https://danschnau.com/blog/js_truthy_falsy" />
    <id>urn:uuid:ee6a9fce-7f12-4db0-badd-27a3f44d66a6</id>
    <updated>2021-06-19T23:41:08.313Z</updated>
    <content type="html">
&lt;p&gt;JavaScript has ‘truthiness’ and ‘falsiness’.&lt;/p&gt;
&lt;p&gt;Things that are “falsey”:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;null&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;false&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Things that are “truthy”&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;function() {}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;''&lt;/code&gt; (empty string)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;'any other string'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;true&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1&lt;/code&gt; (or any number, apart from 0)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[]&lt;/code&gt; (empty array)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{}&lt;/code&gt; (new object)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Object&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Suicidal Ideation</title>
    <link href="https://danschnau.com/blog/suicidal_ideation" />
    <id>urn:uuid:10988e5a-ef0d-44db-9608-bbe1f5a5805e</id>
    <updated>2021-02-23T22:00:32.31Z</updated>
    <content type="html">
&lt;p&gt;&lt;em&gt;This is not a cry for help. I’m getting help and I’ve been getting help.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I have a depressive mind. When I woke up last Saturday, horrible thoughts were gnawing at me. I scribbled them into my planner.&lt;/p&gt;
&lt;p&gt;
&lt;img src="/img/suicidal_ideation.png" alt="Image of a hand-written note of suicidal ideation"&gt;
&lt;/p&gt;
&lt;p&gt;The note reads as follows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There’s no good reason for me to exist. If I died, everyone would be better off.&lt;/p&gt;
&lt;p&gt;The kids would no longer have a crazy Dad. Heather wouldn’t have to deal with me and she’d get lots of insurance $.&lt;/p&gt;
&lt;p&gt;My friends would be fine. I’m a drag on them anyway. Work… it’s laughable to think anyone would even notice.&lt;/p&gt;
&lt;p&gt;I’m a drag on the world and it’s in everyone’s best interest if I killed myself.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is not my real mind. At least, it is not my whole mind. It is a part of it. It is a part of my mind that is trying to protect me, but it is attacking the wrong things, like an autoimmune disease.&lt;/p&gt;
&lt;p&gt;Poor mental health is something I cope with along with &lt;a href="https://www.cdc.gov/nchs/fastats/mental-health.htm"&gt;roughly 1 in 10 of other American Adults&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>The Relationship Between Docker and Kubernetes</title>
    <link href="https://danschnau.com/blog/the_relationship_between_docker_and_kubernetes" />
    <id>urn:uuid:cd37c892-f834-4ab7-87c1-7a06e34332e8</id>
    <updated>2021-01-28T03:18:46.487Z</updated>
    <content type="html">&lt;p&gt;I’m learning container tech on the fly at work.&lt;/p&gt;
&lt;p&gt;This is how I understand the relationship between Docker and Kubernetes. If I’m wrong, or learn more, I’ll write an update post.&lt;/p&gt;
&lt;p&gt;Docker is the tech that makes ‘containers’ like a shipping container. It’s a popular one, but not the only one. The containers fit a specification so they’re interoperable.&lt;/p&gt;
&lt;p&gt;Just like shipping containers, Docker containers are portable, small, and work anywhere. On a boat, on a truck, in a loading dock, on a crane.&lt;/p&gt;
&lt;p&gt;You don’t need Kubernetes to run a container. You might be a truck with a single Docker container on your bed.&lt;/p&gt;
&lt;p&gt;But if you’re a cargo ship, with a huge pile of containers, you need Kubernetes. It’s software for managing a constantly changing pile of containers.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>How To Reference and Focus a DOM Element from Blazor Component Code-Behind</title>
    <link href="https://danschnau.com/blog/blazor_how_to_focus_dom_element" />
    <id>urn:uuid:6f01c10a-a340-494a-a737-131f94eab95d</id>
    <updated>2021-01-23T13:43:36.17Z</updated>
    <content type="html">
&lt;p&gt;The &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-5.0#ref"&gt;Direcive Attribute &lt;code&gt;@ref&lt;/code&gt;&lt;/a&gt; can be used to refer to a specific DOM element.&lt;/p&gt;
&lt;p&gt;There’s also a &lt;a href="https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-5-preview-8/#set-ui-focus-in-blazor-apps"&gt;convenience method &lt;code&gt;FocusAsync&lt;/code&gt; for focusing an &lt;code&gt;input&lt;/code&gt; element&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In my Blazor Markup:&lt;/p&gt;
&lt;pre class="razor"&gt;&lt;code&gt;    &amp;lt;input @ref=&amp;quot;addNameInput&amp;quot; type=&amp;quot;text&amp;quot; placeholder=&amp;quot;Enter your name here&amp;quot; /&amp;gt;
    &amp;lt;button class=&amp;quot;btn btn-primary&amp;quot; @onclick=&amp;quot;AddNameAsync&amp;quot;&amp;gt;Add your name&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And in my code-behind:&lt;/p&gt;
&lt;div class="sourceCode" id="cb2"&gt;&lt;pre class="sourceCode csharp"&gt;&lt;code class="sourceCode cs"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;protected&lt;/span&gt; ElementReference addNameInput { &lt;span class="kw"&gt;get&lt;/span&gt;; &lt;span class="kw"&gt;set&lt;/span&gt;; }&lt;/span&gt;
&lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;protected&lt;/span&gt; async Task &lt;span class="fu"&gt;AddNameAsync&lt;/span&gt;()&lt;/span&gt;
&lt;span id="cb2-3"&gt;&lt;a href="#cb2-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;{&lt;/span&gt;
&lt;span id="cb2-4"&gt;&lt;a href="#cb2-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="co"&gt;// focus the input and return if addNameInput is empty&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-5"&gt;&lt;a href="#cb2-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="kw"&gt;if&lt;/span&gt;(&lt;span class="dt"&gt;string&lt;/span&gt;.&lt;span class="fu"&gt;IsNullOrWhiteSpace&lt;/span&gt;(_nameToAdd))&lt;/span&gt;
&lt;span id="cb2-6"&gt;&lt;a href="#cb2-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    {&lt;/span&gt;
&lt;span id="cb2-7"&gt;&lt;a href="#cb2-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        await addNameInput.&lt;span class="fu"&gt;FocusAsync&lt;/span&gt;();&lt;/span&gt;
&lt;span id="cb2-8"&gt;&lt;a href="#cb2-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="kw"&gt;return&lt;/span&gt;;&lt;/span&gt;
&lt;span id="cb2-9"&gt;&lt;a href="#cb2-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    }&lt;/span&gt;
&lt;span id="cb2-10"&gt;&lt;a href="#cb2-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>How To Move Blazor Component Code into a Code Behind</title>
    <link href="https://danschnau.com/blog/how_to_move_blazor_component_code_into_a_code_behind" />
    <id>urn:uuid:8852a7d6-5655-450b-a2b2-58d6811db543</id>
    <updated>2021-01-23T13:07:43.6Z</updated>
    <content type="html">
&lt;p&gt;Here’s the steps to getting a Blazor Component’s code out of a &lt;code&gt;@Code&lt;/code&gt; block and into it’s own file.&lt;/p&gt;
&lt;p&gt;It doesn’t take much to make a one-file blazor component hard to work with since there’s markup and logic in the same file.&lt;/p&gt;
&lt;p&gt;Let’s call our example component &lt;code&gt;ExampleComponent.razor&lt;/code&gt;.&lt;/p&gt;
&lt;ol type="1"&gt;
&lt;li&gt;In the same directory as &lt;code&gt;ExampleComponent.razor&lt;/code&gt;, add a file &lt;code&gt;ExampleComponent.razor.cs&lt;/code&gt;. Visual Studio should nest it appropriately.&lt;/li&gt;
&lt;li&gt;Change the new class name from &lt;code&gt;ExampleComponent&lt;/code&gt; to &lt;code&gt;ExampleComponentBase&lt;/code&gt; and have that class inherit from &lt;code&gt;Microsoft.AspNetCore.Components.ComponentBase&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Cut/Paste everything from the &lt;code&gt;@code&lt;/code&gt; block into the body of the &lt;code&gt;ExampleComponentBase&lt;/code&gt; class.&lt;/li&gt;
&lt;li&gt;Tell the Page about it’s new base class. At the top of &lt;code&gt;ExampleComponent.razor&lt;/code&gt;, add &lt;code&gt;@inherits ExampleComponentBase&lt;/code&gt; before the &lt;code&gt;@page&lt;/code&gt; directive.&lt;/li&gt;
&lt;li&gt;Fix Dependency Injection. Instead of using the &lt;code&gt;@inherits&lt;/code&gt; directive in a Razor page, add an &lt;code&gt;[Inject]&lt;/code&gt; Attribute and a &lt;code&gt;{get;set;}&lt;/code&gt; to injected dependencies. For example, &lt;code&gt;@inject NavigationManager NavigationManager&lt;/code&gt; in a Razor Component would become &lt;code&gt;[Inject] NavigationManager NavigationManager { get; set; }&lt;/code&gt; in a code-behind.&lt;/li&gt;
&lt;li&gt;Change any &lt;code&gt;private&lt;/code&gt; fields accessed by markup to &lt;code&gt;protected&lt;/code&gt;. They will need to be &lt;code&gt;protected&lt;/code&gt; instead of &lt;code&gt;private&lt;/code&gt; since the Razor Component markup is now a sub-class of &lt;code&gt;ExampleComponentBase&lt;/code&gt; and thus cannot access private fields.&lt;/li&gt;
&lt;/ol&gt;

</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>10 Years as an Impostor</title>
    <link href="https://danschnau.com/blog/10_years_an_impostor" />
    <id>urn:uuid:98679708-7060-4a32-afa8-173d3c7c79ec</id>
    <updated>2021-01-23T11:54:59.673Z</updated>
    <content type="html">
&lt;p&gt;For me, this February will mark 10 years of collecting a steady paycheck in software engineering working on “boring business software”.&lt;/p&gt;
&lt;p&gt;I’m in my 30s. I have a four-year degree in Software Engineering from a university that shows up on &lt;a href="https://drafty.cs.brown.edu/csopenrankings/"&gt;this list&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ten years ago, my salary was $42,000. I’ve tripled that salary and then some.&lt;/p&gt;
&lt;p&gt;Several times I’ve quit one job for a better one. I’ve never been fired from a job.&lt;/p&gt;
&lt;p&gt;I’m a cis white male. I live in the Midwest USA.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If anyone working in our industry might &lt;em&gt;not&lt;/em&gt; feel like an impostor I imagine they might look a lot like me.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But I feel like a fake, a fraud, just waiting to be found out I don’t hold all the cards, frequently.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I don’t have anything new to say about impostor syndrome. I just won’t spend another decade being ashamed of it.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>I set up a Pi-Hole because of the Pandemic</title>
    <link href="https://danschnau.com/blog/i-set-up-a-pi-hole" />
    <id>urn:uuid:192ca182-a7eb-431e-adaa-afa3d1b95275</id>
    <updated>2020-05-29T23:06:40.197Z</updated>
    <content type="html">&lt;p&gt;Because of the pandemic, my sons have been using laptops a lot for gaming during the day. Their machines don''t have adblockers. I figured this was a good time to set up &lt;a href="https://pi-hole.net/"&gt;pi-hole&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I used &lt;a href="https://www.raspberrypi.org/downloads/"&gt;Raspian imager&lt;/a&gt; to image an SD card. I then plugged in the SD card, hooked up the raspberry pi to my monitor/mouse/keyboard at my desk and ran through the Raspbian setup wizard.&lt;/p&gt;
&lt;p&gt;Then I powered down the Pi and moved it to the entertainment center and hooked it up to the router and (temporarily) TV and a mouse/keyboard.&lt;/p&gt;
&lt;p&gt;Pi-Hole provides a ''one-command'' installer script:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;curl -sSL https://install.pi-hole.net | bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There were a few prompts in the install script, I accepted all the defaults. One thing I wonder about is using an ''upstream'' DNS server apart from Google. I left it at Google, I''ll look into the other DNS servers another time.&lt;/p&gt;
&lt;p&gt;That was up and running and I thought I was ''done'' so I tested loading &lt;code&gt;people.com&lt;/code&gt; on my phone (while connected to Wi-Fi) and ads still showed up. I realized I forgot to configure the router to use the new DNS server.&lt;/p&gt;
&lt;p&gt;So I looked up instructions for how to do that on my router and did so, it was pretty easy.&lt;/p&gt;
&lt;p&gt;Pi-Hole provides a web interface, which worked without issues, but I wanted to be able to &lt;code&gt;ssh&lt;/code&gt; into the pi-hole in the future so I don''t need to connect it to the TV. I found out that Raspbian lets you do this with a few points-and-clicks. Then I could ssh from any laptop in the house on the wifi. I took the HDMI cable, mouse and keyboard out of the pi and the project was done.&lt;/p&gt;
&lt;p&gt;Setting up Pi-Hole was really easy. The whole bit was one in about an hour and the kids were back to gaming in no time.&lt;/p&gt;</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Discovering nuget.exe Versions with tools.json</title>
    <link href="https://danschnau.com/blog/nuget_tools_json" />
    <id>urn:uuid:7aaad5f4-bc61-4eb6-8697-f8a53c18361d</id>
    <updated>2020-04-13T20:38:59.36Z</updated>
    <content type="html">&lt;p&gt;While updating my build failed with an upgrade from ASP.NET Core from 2.2 to 3.1. I was using Azure DevOps for my build.&lt;/p&gt;
&lt;p&gt;I was getting a funny error:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-log"&gt;##[error]The nuget command failed with exit code(1) and error(C:\Program Files\dotnet\sdk\2.2.110\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(137,5): error NETSDK1045: The current .NET SDK does not support targeting .NET Core 3.1.  Either target .NET Core 2.2 or lower, or use a version of the .NET SDK that supports .NET Core 3.1.)
##[error]Packages failed to restore
Finishing: NuGet restore
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It turned out my build was set to an old version of NuGet, which has seen several updates for .NET Core.&lt;/p&gt;
&lt;p&gt;I also learned Microsoft''s NuGet team publishes a json file detailing release history and preview releases at &lt;a href="https://dist.nuget.org/tools.json"&gt;https://dist.nuget.org/tools.json&lt;/a&gt;. More detail on the contents of the file at &lt;a href="https://docs.microsoft.com/en-us/nuget/api/tools-json"&gt;docs.microsoft.com&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Implementing Single-User Administrator Authorization with  Azure AD in ASP.Net Core 3.1</title>
    <link href="https://danschnau.com/blog/implementing_single_user_admin" />
    <id>urn:uuid:5eb8c8ca-af7d-4e19-9067-9cd92cc102f0</id>
    <updated>2020-04-12T04:24:55.413Z</updated>
    <content type="html">&lt;p&gt;Until now, this blog was entirely a read-only site.
Content is stored in a database, and the app reads from that DB, but there was no code for writing to the DB. In order to update content, I''d manually write SQL queries. I needed to be able to secure some features of the site so I could get away from that practice.&lt;/p&gt;
&lt;p&gt;Here were my goals for this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Save no user data in the app.&lt;/li&gt;
&lt;li&gt;Use my Mirosoft Identity (the same one I use on my Xbox) to log in.&lt;/li&gt;
&lt;li&gt;Only allow me to Authorize.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I worked off &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-aspnet-core-webapp?view=aspnetcore-3.1"&gt;this helpdoc at docs.microsoft.com&lt;/a&gt;. It links to a sample project I could use as a reference, which was useful while troubleshooting.&lt;/p&gt;
&lt;h2&gt;Install Dependencies&lt;/h2&gt;
&lt;p&gt;Step 1 was to install Nuget package &lt;a href="https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.AzureAD.UI/5.0.0-preview.2.20167.3"&gt;&lt;strong&gt;Microsoft.AspNet.Core.Authentication.AzureAD.UI&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Create new Azure AD App registration&lt;/h2&gt;
&lt;p&gt;To use Azure AD auth, you need to create an App Registration. I did mine point-and-click in the Azure Portal.&lt;/p&gt;
&lt;p&gt;More info on how to do this at &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-aspnet-core-webapp?view=aspnetcore-3.1#option-2-register-and-manually-configure-your-application-and-code-sample"&gt;docs.microsoft.com&lt;/a&gt;. By the end of it, I had a &lt;code&gt;TenantId&lt;/code&gt; and a &lt;code&gt;ClientId&lt;/code&gt;. I also had to make sure my redirect urls were correct - I needed &lt;code&gt;danschnau.com/oidc-login&lt;/code&gt; for prod and &lt;code&gt;localhost:8888/oidc-login&lt;/code&gt; for local development.&lt;/p&gt;
&lt;h2&gt;Configure Things in your app&lt;/h2&gt;
&lt;p&gt;In my ASP.NET Core 3.1 app, I plugged in a few settings into my &lt;code&gt;appsettings.json&lt;/code&gt;. These were copied from the sample project.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-json"&gt;&amp;quot;AzureAd&amp;quot;: {
   &amp;quot;Instance&amp;quot;: &amp;quot;https://login.microsoftonline.com/&amp;quot;,
   &amp;quot;Domain&amp;quot;: &amp;quot;[Enter the domain of your tenant, e.g. contoso.onmicrosoft.com]&amp;quot;,
   &amp;quot;TenantId&amp;quot;: &amp;quot;TENANT_ID_GOES_HERE&amp;quot;,
   &amp;quot;ClientId&amp;quot;: &amp;quot;CLIENT_ID_GOES_HERE&amp;quot;,
   &amp;quot;CallbackPath&amp;quot;: &amp;quot;/signin-oidc&amp;quot;
},
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Update &lt;code&gt;Startup.cs&lt;/code&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;Configure&lt;/code&gt; Changes&lt;/h3&gt;
&lt;p&gt;In &lt;code&gt;Configure&lt;/code&gt;, I added &lt;code&gt;app.UseAuthorization()&lt;/code&gt; and &lt;code&gt;app.UseAuthentication()&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;// This method gets called by the runtime. Use this method to configure 
// the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // snip

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();
    app.UseCookiePolicy();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =&amp;gt; { /* snip */ });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;ConfigureServices&lt;/code&gt; Changes&lt;/h3&gt;
&lt;p&gt;In &lt;code&gt;ConfigureServices&lt;/code&gt;, I added a few things.&lt;/p&gt;
&lt;p&gt;I changed &lt;code&gt;services.ConfigureMvc()&lt;/code&gt; to instead to a bit of Authorization configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;services.AddMvc(options =&amp;gt;
{
    var policy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
    options.Filters.Add(new AuthorizeFilter(policy));
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also had to call &lt;code&gt;services.AddAuthentication()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
    .AddAzureAD(options =&amp;gt; Configuration.Bind(&amp;quot;AzureAd&amp;quot;, options));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I had to call &lt;code&gt;services.AddAuthorization&lt;/code&gt;, specifying that the only way to be authorized is to have a claim of my email address.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;services.AddAuthorization(options =&amp;gt;
{
    options.AddPolicy(&amp;quot;adminpolicy&amp;quot;, o =&amp;gt;
    {
        o.RequireAssertion(authorizationHandlerContext =&amp;gt;
            authorizationHandlerContext.User
            .HasClaim(&amp;quot;http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress&amp;quot;, &amp;quot;dsschnau@gmail.com&amp;quot;));
    });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It took a bit of trial and error to get that working.&lt;/p&gt;
&lt;h2&gt;Add &lt;code&gt;[AllowAnonymous]&lt;/code&gt; To Public Controllers&lt;/h2&gt;
&lt;p&gt;This blog is a pretty vanilla MVC app. All the Controllers that service public-facing pages got the &lt;code&gt;[AllowAnonmyous]&lt;/code&gt; tag put on their clases.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[AllowAnonymous]
public class HomeController : Controller
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Secure A Controller&lt;/h2&gt;
&lt;p&gt;Then I could force a Controller to lock down access to only me:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;    [Authorize(&amp;quot;adminpolicy&amp;quot;)]
    public class AdminController : Controller
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That was it! All done.&lt;/p&gt;
&lt;h2&gt;Closing Thoughts&lt;/h2&gt;
&lt;p&gt;This all worked well, taking 2-3 hours to implement this. Having examples, well-written documentation, and helpful error messages, I was never stuck troubleshooting for too long.&lt;/p&gt;
&lt;p&gt;There''s more I'd like to do around Authentication/Authorization on Dan Schnau Dot Com in the future - adding the ability to leave comments comes to mind.&lt;/p&gt;
&lt;p&gt;If you have feedback, questions or advice - email me at dsschnau@gmail.com.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Running NUnit 2 Tests in Visual Studio 2019</title>
    <link href="https://danschnau.com/blog/running_nunit2_tests_requires_a_nuget_package" />
    <id>urn:uuid:135c9a26-3b60-4ef8-bb1f-b27619a2c09d</id>
    <updated>2020-01-02T00:54:39.927Z</updated>
    <content type="html">
&lt;p&gt;I encountered an issue the other day where I was unable to run unit tests in the Visual Studio 2019 Test Explorer.&lt;/p&gt;
&lt;p&gt;The tests would just show up as 'skipped' in the Test Explorer UI, and the Test output window would say something to the effect of:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-Console"&gt;[Informational] Executing test method 'MyProject.UnitTest1.Main'
[Informational] ------ Run test started ------
[Warning] No test matches the given testcase filter `FullyQualifiedName=MyProject.UnitTest1.Main`...
[Informational] ========== Run test finished: 0 run (0:00:03.6212841) ==========
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The test project used NUnit 2 for it's testing framework.&lt;/p&gt;
&lt;p&gt;After searching the web, I found out I could add a different NuGet package that enabled running the tests from the Test Explorer.&lt;/p&gt;
&lt;p&gt;It's called &lt;a href="https://www.nuget.org/packages/NUnitTestAdapter/"&gt;NUnitTestAdapter&lt;/a&gt;. After installing that package and rebuilding the project, I was able to run my tests. Though I haven't tried it, it looks like there's a separate package for running NUnit 3 tests: &lt;a href="https://www.nuget.org/packages/NUnit3TestAdapter/"&gt;NUnit3TestAdapter&lt;/a&gt;&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Promise.all()</title>
    <link href="https://danschnau.com/blog/javascript_promise_all" />
    <id>urn:uuid:a4a11cb4-96f8-4324-9efa-f9be85cddf85</id>
    <updated>2019-08-17T19:06:29.67Z</updated>
    <content type="html">
&lt;p&gt;In JavaScript, you can use &lt;code&gt;Promise.all()&lt;/code&gt; if you need to wait for a few promises to complete
before moving forward in a routine.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var p1 = getData(); // getData returns a promise with some data when it completes.
var p2 = 42;
var p3 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 100, 'a fake promise')
});

Promise.all([p1, p2, p3]).then(function(promiseResults) {
    console.log(promiseResults);
    // promiseResults will contain an in-order array of the results of `getData()`, 42, and 'a fake promise' 
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For more information, check out the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all"&gt;Mozilla Developer Network&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>An Atom feed on DanSchnau dot com</title>
    <link href="https://danschnau.com/blog/an_atom_feed_on_danschnau_dot_com" />
    <id>urn:uuid:56a754da-a7d0-4200-bcff-f2686e6bb754</id>
    <updated>2019-07-29T04:01:40.067Z</updated>
    <content type="html">
&lt;p&gt;As of yesterday this blog supports syndication by Atom feed. You can &lt;a href="https://danschnau.com/feed"&gt;use this
link to connect the feed to a reader&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I took joy in writing the code for this feed. Coding by requirements of a web
standard is a much different experience than that of business-oriented User Stories.&lt;/p&gt;
&lt;p&gt;Atom is a 'publishing format' which is a contract that says &amp;quot;I'll supply my
information in a specific form, so external applications will know how to read
it.&amp;quot;&lt;/p&gt;
&lt;p&gt;It's not common for applications to interoperate well anymore. Wouldn't it be
nice if your Facebook feed also could read your Twitter feed?&lt;/p&gt;
&lt;p&gt;Anyway to publish with this format, you publish an &lt;code&gt;xml&lt;/code&gt; file formatted a
specific way, then feed readers can read it. That's it.&lt;/p&gt;
&lt;p&gt;My implementation isn't much more than a bunch of &lt;code&gt;XDocument&lt;/code&gt; calls. Here's the
gist:&lt;/p&gt;
&lt;p&gt;
&lt;script src="https://gist.github.com/dsschnau/6de34b68a899a4355f816f0900068516.js"&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p&gt;I used some random samples around the web to get started, including &lt;a href="https://en.wikipedia.org/wiki/Atom_(Web_standard)#Example_of_an_Atom_1.0_feed"&gt;this one on
Wikipedia&lt;/a&gt;.
I found that &lt;a href="https://validator.w3.org/feed/"&gt;w3.org hosts a validator for validating RSS and Atom
feeds&lt;/a&gt;. That was very useful.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>How to Change which Identity Visual Studio uses to Authenticate with Azure</title>
    <link href="https://danschnau.com/blog/change_visual_studio_azure_identity" />
    <id>urn:uuid:aaaac036-7a4d-451f-895d-4e92a30fbcdb</id>
    <updated>2019-07-21T22:05:12.593Z</updated>
    <content type="html">
&lt;p&gt;I was developing an app locally on a different laptop than usual. When it
started it would fall over returning &lt;code&gt;502&lt;/code&gt; errors. I saw in Visual Studio's
debug output this error:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Exception thrown: 'Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException' in System.Private.CoreLib.dll&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The error was caused because my app uses a Key Vault instance on a different
subscription. I had already signed into Visual Studio with the correct identity,
but there's another step to tell Visual Studio 2019 which identity to use when
authenticating.&lt;/p&gt;
&lt;p&gt;As I found in The &lt;a href="https://docs.microsoft.com/en-us/azure/key-vault/service-to-service-authentication#authenticating-with-visual-studio"&gt;Azure/Key Vault
docs&lt;/a&gt;,
The menu is under &lt;strong&gt;Tools &amp;gt; Options &amp;gt; Azure Service Authentication&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="/img/vs_azure_service_authentication.png" alt="Azure Service Authentication Options"&gt;&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Working Remote is Awesome</title>
    <link href="https://danschnau.com/blog/working_remote_is_awesome" />
    <id>urn:uuid:267ead31-5f74-4c42-abaf-8a7dde3c5270</id>
    <updated>2019-07-13T11:51:35.787Z</updated>
    <content type="html">
&lt;p&gt;This March I began a new job at &lt;a href="https://www.skykick.com/"&gt;SkyKick&lt;/a&gt;. SkyKick is
a world-class startup and I couldn't be more excited to be a part of their team,
and that's in no small part due to that they've trusted me to work remotely from
my house in metro Detroit.&lt;/p&gt;
&lt;p&gt;At SkyKick, I spent my first two weeks at the office in Seattle, to make
onboarding go smoothly. Then I flew back home to DTW with my new work
laptop in hand.&lt;/p&gt;
&lt;p&gt;Working remote took some getting used to. I was uncomfortable without having
anywhere to go on my first remote day so I took a picture of my desk with a
&amp;quot;thumbs-up&amp;quot; in front of my phone and posted it to Slack.&lt;/p&gt;
&lt;p&gt;Three months in, I'm emphatically happy with this working arrangement.&lt;/p&gt;
&lt;p&gt;I believe that as it becomes more 'normal' to work from wherever, the stigma around
it will fade away.&lt;/p&gt;
&lt;p&gt;The rest of this post details some of my remote experience so far.&lt;/p&gt;
&lt;h2&gt;Be intentional about talking to people.&lt;/h2&gt;
&lt;p&gt;Just like back in the days of &lt;a href="https://wowwiki.fandom.com/wiki/Barrens_chat"&gt;Barrens
chat&lt;/a&gt;, my communications are now
entirely through typed text, some colorful avatars, and occasional voice chat.&lt;/p&gt;
&lt;p&gt;That means if you want to talk to someone, &lt;em&gt;you gotta talk to them!&lt;/em&gt; You can't
idly hang out in the kitchen drinking coffee hoping that the person you wanted
to speak with strolls by, just so you don't have to feel like you're bothering
them at their desk.&lt;/p&gt;
&lt;p&gt;Send messages! Ask people how they are! Schedule 1-on-1s! Put the onus of &amp;quot;Can't
talk right now, I'm busy&amp;quot; on the other person - you have no idea how busy they
are because you can't look over their shoulder.&lt;/p&gt;
&lt;h2&gt;Distractions&lt;/h2&gt;
&lt;p&gt;I live with my wife and three children. my school-age kids are home for the
summer. The house is a bit chaotic day-to-day, and programming is a job where
you frequently need to quietly focus on something.&lt;/p&gt;
&lt;p&gt;Fortunately I'm not the first to encounter this issue, and strategies for
handling work-at-home have been well discussed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have a specific room that you work in&lt;/li&gt;
&lt;li&gt;Set 'office hours'&lt;/li&gt;
&lt;li&gt;Set clear boundaries with family members or co-habitants&lt;/li&gt;
&lt;li&gt;Be transparent with your team&lt;/li&gt;
&lt;li&gt;Use your webcam liberally&lt;/li&gt;
&lt;li&gt;Invest in a good headset with a good microphone&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most important to me has been the headset headphones. I bought a (used)
&lt;a href="https://steelseries.com/gaming-headsets/arctis-pro-gamedac"&gt;Steelseries Arctis Pro with
GameDAC&lt;/a&gt; and I
couldn't be happier with the purchase.&lt;/p&gt;
&lt;h2&gt;Writing skills&lt;/h2&gt;
&lt;p&gt;Most of my working communication is now written. I think it's just as important
for a software engineer working remote to practice writing along with
programming.&lt;/p&gt;
&lt;p&gt;My favorite book on the subject of writing is &lt;a href="https://www.goodreads.com/book/show/53343.On_Writing_Well"&gt;&lt;strong&gt;On Writing Well&lt;/strong&gt; By William
Zinsser&lt;/a&gt;. It's a
great book both for technical advice and inspiration to write well. I'm not
setting out to be a writer by trade, but I came out with a greater respect for
the craft after reading this book.&lt;/p&gt;
&lt;h2&gt;Links talking about Remote Work&lt;/h2&gt;
&lt;p&gt;People are talking lots about remote work. Here's a list of links I collected
over the last few months:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.fastcompany.com/90318974/the-rise-of-remote-working-will-continue"&gt;Jessica Stevens @ Fast Company: Why Remote Work Isn't Going Away Anytime
soon&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://8thlight.com/blog/sarah-sunday/2019/05/28/remote-team-workflows.html"&gt;Sarah Sunday @ 8thlight: Remote Team Workflows&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.quuu.co/lessons-worth-learning-from-remote-workers/"&gt;Quuu: Remote working – Bringing Sanity to Mind &amp;amp; Lessons Worth Learning &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://upside.fm/the-future-of-work-is-here-so-why-arent-more-companies-remote-first/"&gt;Upside.fm: The “Future of Work” is Here… So Why Aren’t More Companies Remote-first?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This blog is a nice love letter from a guy working successfully in a co-working
space: &lt;a href="https://chrissalzman.com/blog/workantileisten/"&gt;Chris Salzman: On Workantile's 10th
Birthday&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Julia Evans has written several posts on the subject of working Remotely for Stripe:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jvns.ca/blog/2014/03/29/reports-from-remote-land-remote-pairing-works-great/"&gt;remote pairing, two weeks in&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jvns.ca/blog/2014/06/06/working-remote/"&gt;three months&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jvns.ca/blog/2014/10/22/working-remote-8-months-in/"&gt;eight months&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jvns.ca/blog/2015/12/31/surviving-meetings-while-remote/"&gt;meetings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jvns.ca/blog/2018/02/18/working-remotely--4-years-in/"&gt;four years&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Google shouldn't Be A Verb</title>
    <link href="https://danschnau.com/blog/google_shouldnt_be_a_verb" />
    <id>urn:uuid:7b5fb537-accf-4ba9-aba5-15cc9e2f715a</id>
    <updated>2019-04-02T14:46:47.503Z</updated>
    <content type="html"> &lt;p&gt;&amp;quot;Google&amp;quot; is a company built on a search engine. It isn't a verb.&lt;/p&gt;  &lt;p&gt;In 1998 Larry Page wrote in an email &amp;quot;Have fun and keep googling!&amp;quot;.  It is the first recorded usage. Of course &lt;em&gt;he&lt;/em&gt; would would want you to use  it as a verb.&lt;/p&gt;  &lt;p&gt;But when you use &amp;quot;Google&amp;quot; as a verb for &amp;quot;search the web&amp;quot;, there is a  problem. It builds into language an assumption that Google is the &lt;strong&gt;One True Search  Engine&lt;/strong&gt;. There can be no other web search engine, because the word for &amp;quot;search  the web&amp;quot; is Google.&lt;/p&gt;  &lt;p&gt;That isn't true. Google is a good search engine, sure. But it isn't perfect  for every search every time.&lt;/p&gt;  &lt;p&gt;I use &lt;a href="https://duckduckgo.com/"&gt;DuckDuckGo&lt;/a&gt;. It has a better privacy story and  &lt;a href="https://duckduckgo.com/bang"&gt;tooling&lt;/a&gt;.  &lt;a href="https://www.bing.com/"&gt;Microsoft Bing&lt;/a&gt; has a &lt;a href="https://www.microsoft.com/en-us/rewards"&gt;rewards program&lt;/a&gt;.  &lt;a href="https://wiby.me/"&gt;Wiby&lt;/a&gt; Is a search engine for &amp;quot;classic&amp;quot; websites.  &lt;a href="https://www.wolframalpha.com/"&gt;Wolfram Alpha&lt;/a&gt; will  give precise information that you won't find on Google.&lt;/p&gt;  &lt;p&gt;Search engines are amazing modern technologies we didn't have two decades ago.  They shouldn't be shouldered out by language. So the next time you tell your  friend something you looked up with Google, let them know you searched the  web for it.&lt;/p&gt; </content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>LocalStorage, The  unary operator and strict equality in JavaScript</title>
    <link href="https://danschnau.com/blog/localstorage_strict_equality_unary_operator_javascript" />
    <id>urn:uuid:af0bb65b-0f9f-40c9-8fa5-628418bec2e8</id>
    <updated>2019-03-16T02:45:27.02Z</updated>
    <content type="html">&lt;p&gt;My stance on JavaScript strict vs. type-converting comparison (double vs.
triple-equals) is to &lt;em&gt;always&lt;/em&gt; use strict equality. But the other day this
tripped me up.&lt;/p&gt;
&lt;p&gt;I had a number stored in local storage and I wanted to check it against a
constant number.&lt;/p&gt;
&lt;p&gt;But, the check failed even when the numbers were the same. I then learned
that &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#Basic_concepts"&gt;everything stored in local storage is stored as as string&lt;/a&gt;.
your data).&lt;/p&gt;
&lt;p&gt;I put the unary operator &lt;code&gt;+&lt;/code&gt; in front of the value read out of local storage,
and the problem was resolved.&lt;/p&gt;
</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>gotcha: Compiler error CS0656 when using Dynamics in a .NET Standard project</title>
    <link href="https://danschnau.com/blog/Compiler_Error_CS0656_Using_Dynamics" />
    <id>urn:uuid:f9c8ddfe-99b5-4f20-af2c-0dc987dab722</id>
    <updated>2019-03-02T00:10:00Z</updated>
    <content type="html">&lt;p&gt;This afternoon I'm experimenting with &lt;a href="https://stackexchange.github.io/Dapper/"&gt;Dapper&lt;/a&gt;, a micro-ORM used by Stack Overflow. The most straightforward way to query with dapper is to get a list of Dynamic objects. But when I went to read some data out of a table I got this compiler error.&lt;/p&gt;        &lt;img src="~/img/dapper_dynamic_compiler_error.png" alt="Screenshot of some code querying with dapper that returned an enumerable dynamic." /&gt;        &lt;code&gt;CS0656 Missing compiler required member 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'&lt;/code&gt;        &lt;p&gt;I had to search the web for the cause. It turns out you need to install two NuGet packages to work with Dynamics:&lt;/p&gt;        &lt;ul&gt;          &lt;li&gt;&lt;a href="https://www.nuget.org/packages/System.Dynamic.Runtime/"&gt;System.Dynamic.Runtime&lt;/a&gt;&lt;/li&gt;          &lt;li&gt;&lt;a href="https://www.nuget.org/packages/Microsoft.CSharp/"&gt;Microsoft.CSharp&lt;/a&gt;&lt;/li&gt;      &lt;/ul&gt;        &lt;p&gt;I got tripped up on this because I thought Dynamics were just part of the features of C#, so I wouldn't expect to require a nuget package to use them. But then things like System.Linq are their own packages, so I guess that's the way things are here.&lt;/p&gt;</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
  <entry>
    <title>Four Pillars Of Object Oriented Programming</title>
    <link href="https://danschnau.com/blog/Four_Pillars_Of_Object_Oriented_Programming" />
    <id>urn:uuid:3c406e4e-ce55-46fd-8b36-76fbe2d2f7ac</id>
    <updated>2019-03-02T00:00:00Z</updated>
    <content type="html">    &lt;aside&gt;
        Or: "Four Strategies you can apply to work effectively with an Object Oriented Programming Language"
    &lt;/aside&gt;


    &lt;ul&gt;
        &lt;li&gt;&lt;a href="#Abstraction"&gt;Abstraction&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href="#Encapsulation"&gt;Encapsulation&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href="#Inheritance"&gt;Inheritance&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href="#Polymorphism"&gt;Polymorphism&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;
        When I was in college, and for my first few years of Seriour Business Programming Jobs, these four concepts seemed Big And Scary, Like I Would Never Understand Them.
    &lt;/p&gt;
    &lt;p&gt;
        If you work with an object-oriented language, it's worth knowing them intentionally. You can be more effective if you're able to distinguish and apply these with explicit reasons. 
    &lt;/p&gt;
    &lt;p&gt;
        It's possible that you use them without realizing it.
    &lt;/p&gt;

    &lt;h2 id="Abstraction"&gt;Abstraction&lt;/h2&gt;
    &lt;p&gt;
        Here's when you need an abstraction: If you ever find yourself thinking "I'm writing the same code over and over with only small changes for different types. It would be cool if I could write this routine once and use a more generalized type rather than a {String, Int, Object}|{Cat, Dog, Mouse}{XmlDoc, JSONDoc, BinaryStream}."
    &lt;/p&gt;
    &lt;p&gt;
        When you do so, you're using an &lt;strong&gt;Abstraction.&lt;/strong&gt;
    &lt;/p&gt;

    &lt;h2 id="Encapsulation"&gt;Encapsulation&lt;/h2&gt;
    &lt;aside&gt;
        For the longest time, I thought "keep your privates private" was a software security thing. It kind is, kind of, but I've found myself thinking about it much more as making it easier to write and read your code.
    &lt;/aside&gt;
    &lt;img src="/img/encapsulation.jpg" alt="'Hey, I need to know your Name.' 'Sure, do you also want to know the last time I used the bathroom?''No. Keep that private.''" /&gt;
    &lt;p&gt;
        Most objects are complicated enough that you should hide details that the object needs in order to work correctly, but no other objects should ever need. Hide those fields and methods with Encapsulation!
    &lt;/p&gt;

    &lt;h2 id="Inheritance"&gt;Inheritance&lt;/h2&gt;
    &lt;p&gt;
        Inheritance is closely related to the concept of Abstraction. It amounts to pushing common features of a class into a base class. Writing the same code over and over again across classes? Make the method once in an Abstract class.
        Then, in the places where you were repeating code, you can inherit from the base class, getting all the functionality with none of the repetition.
    &lt;/p&gt;

    &lt;h2 id="Polymorphism"&gt;Polymorphism&lt;/h2&gt;
    &lt;aside&gt;
        Polymorphism is the one of these four I use every day but always indirectly. I've never thought to myself "I know, I'll use &lt;strong&gt;Polymorphism&lt;/strong&gt; to solve this problem!
    &lt;/aside&gt;
    &lt;p&gt;
        Ever notice how you can add `ints`, `floats`, and `doubles` together? That's polymorphism at work. Polymorphism is the act of different types in a language working with each other because they have a common interface, such as the '+' operator in our example.
    &lt;/p&gt;</content>
    <author>
      <name>Dan Schnau</name>
      <uri>https://danschnau.com</uri>
    </author>
  </entry>
</feed>