<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Ed Andersen]]></title><description><![CDATA[Software Engineer in Japan. Subscribe for blog posts and the occasional analysis of the important developer news.]]></description><link>https://www.edandersen.com</link><image><url>https://substackcdn.com/image/fetch/$s_!8VmJ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c2f46f2-1699-40b5-9851-b9670a4403d7_330x330.png</url><title>Ed Andersen</title><link>https://www.edandersen.com</link></image><generator>Substack</generator><lastBuildDate>Wed, 29 Apr 2026 11:22:45 GMT</lastBuildDate><atom:link href="https://www.edandersen.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Ed Andersen]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[edandersen@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[edandersen@substack.com]]></itunes:email><itunes:name><![CDATA[Ed Andersen]]></itunes:name></itunes:owner><itunes:author><![CDATA[Ed Andersen]]></itunes:author><googleplay:owner><![CDATA[edandersen@substack.com]]></googleplay:owner><googleplay:email><![CDATA[edandersen@substack.com]]></googleplay:email><googleplay:author><![CDATA[Ed Andersen]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[C# 14's best new features]]></title><description><![CDATA[This stuff you might actually use]]></description><link>https://www.edandersen.com/p/c-14s-best-new-features</link><guid isPermaLink="false">https://www.edandersen.com/p/c-14s-best-new-features</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Sun, 21 Sep 2025 04:26:59 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/6898d3d0-10d2-403a-b84c-58e0eb3cf469_840x600.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi all. Been a while since I&#8217;ve done one of these. Thanks for continuing to subscribe! And if you&#8217;d like this post in cringe Youtube video form, check out:</p><div id="youtube2-RTFdUBKQres" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;RTFdUBKQres&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/RTFdUBKQres?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div><hr></div><h3>1. Null-Conditional Assignment (<code>?=</code>)</h3><p>We've all written this boilerplate code a thousand times. You have a nullable object, and you only want to assign a value to one of its properties if the object itself isn't <code>null</code>.</p><p>In C# 13, you had to perform an explicit null check.</p><pre><code><code>public class Channel
{
    public int Subscribers { get; set; }
    public int Members { get; set; }
}

// ---

Channel? myChannel = null; // or new Channel();

// We have to check for null before assigning
if (myChannel is not null)
{
    myChannel.Subscribers = 1000;
}
</code></code></pre><p><strong>After: C# 14</strong></p><p>With the new null-conditional assignment operator (<code>?=</code>), you can do this in a single, safe, and readable line.</p><pre><code><code>Channel? myChannel = null;

// This will only assign 1000 if myChannel is not null
myChannel?.Subscribers = 1000;
</code></code></pre><p>It's a small change, but a very welcome one!</p><div><hr></div><h3>2. The <code>field</code> Keyword for Semi-Auto-Properties</h3><p>Auto-properties are great, but as soon as you need to add logic to your getter or setter (like validation), you have to convert it to a full property with an explicit private backing field.</p><p><strong>Before: C# 13</strong></p><p>This required creating a separate field (<code>_subscribers</code>) just to add a check in the setter.</p><pre><code><code>public class Channel
{
    private int _subscribers;
    public int Subscribers
    {
        get =&gt; _subscribers;
        set
        {
            if (value &lt; 1000)
            {
                throw new ArgumentException("Not enough subs!");
            }
            _subscribers = value;
        }
    }
}
</code></code></pre><p><strong>After: C# 14</strong></p><p>The new <code>field</code> keyword gives you access to the implicit, compiler-generated backing field of an auto-property. This means you can add logic without the boilerplate!</p><pre><code><code>public class Channel
{
    public int Subscribers
    {
        get =&gt; field; // 'field' refers to the implicit backing field
        set
        {
            if (value &lt; 1000)
            {
                throw new ArgumentException("Not enough subs!");
            }
            field = value; // Assign to the implicit backing field
        }
    }
}
</code></code></pre><p>This keeps the code much cleaner and more concise.</p><div><hr></div><h3>3. User-Defined Compound Operators</h3><p>This one is really cool. Have you ever wanted to define what a compound operator like <code>++</code> or <code>+=</code> does for your own custom type? Now you can!</p><p>Imagine we want the <code>++</code> operator to increment both the <code>Subscribers</code> and <code>Members</code> properties of our <code>Channel</code> class at the same time.</p><p><strong>Before: C# 13</strong></p><p>You'd have to increment each property manually.</p><pre><code><code>var myChannel = new Channel();
myChannel.Subscribers++;
myChannel.Members++;
</code></code></pre><p><strong>After: C# 14</strong></p><p>You can now overload the compound operator directly in your class definition.</p><pre><code><code>public class Channel
{
    public int Subscribers { get; set; }
    public int Members { get; set; }

    public void operator ++()
    {
        Subscribers++;
        Members++;
    }

    public void operator +=(int amount)
    {
        Subscribers += amount;
        Members += amount;
    }
}

// ---

var myChannel = new Channel();
myChannel++; // This now increments both Subscribers and Members!

Console.WriteLine($"Subs: {myChannel.Subscribers}, Members: {myChannel.Members}");
// Output: Subs: 1, Members: 1
</code></code></pre><p>Not sure where I&#8217;d use this but hey ho!</p><div><hr></div><h3>4. The <code>extension</code> Keyword</h3><p>Extension methods have always required a <code>public static</code> class and <code>public static</code> methods using the <code>this</code> keyword. C# 14 introduces a new, more explicit syntax.</p><p><strong>Before: C# 13</strong></p><p>The classic syntax we all know.</p><pre><code><code>public static class ChannelExtensions
{
    public static void PrintDetails(this Channel c)
    {
        Console.WriteLine($"Subs: {c.Subscribers}, Members: {c.Members}");
    }
}
</code></code></pre><p><strong>After: C# 14</strong></p><p>You can now use the <code>extension</code> keyword to create an "extension type." The syntax is cleaner and makes the intent clearer.</p><pre><code><code>public extension ChannelExtensions
{
    public void PrintDetails(this Channel c)
    {
        Console.WriteLine($"Subs: {c.Subscribers}, Members: {c.Members}");
    }
}
</code></code></pre><p>Personally, I'm not totally sold on this one yet, as it just creates another way to do the same thing. You can now use extension methods to add Properties, but still&#8230;</p><div><hr></div><h3>5. <code>nameof</code> Improvements for Generics</h3><p>The <code>nameof</code> operator is a handy tool for getting the string name of a variable, type, or member. However, it had a limitation with generic types.</p><p><strong>Before: C# 13</strong></p><p><code>nameof</code> worked on a constructed generic type, but not on an "unbound" or open generic type.</p><p>C#</p><pre><code><code>public class Channel&lt;T&gt; { }
public class YouTube { }

// This worked fine
Console.WriteLine(nameof(Channel&lt;YouTube&gt;)); // Output: Channel

// This caused a compile error
// Console.WriteLine(nameof(Channel&lt;&gt;));
</code></code></pre><p><strong>After: C# 14</strong></p><p>This limitation has been removed. You can now use <code>nameof</code> directly on an unbound generic type.</p><p>C#</p><pre><code><code>// Now this works perfectly!
Console.WriteLine(nameof(Channel&lt;&gt;)); // Output: Channel
</code></code></pre><p>This is a nice little fix for those edge cases where you need the name of the generic type definition itself.</p><div><hr></div><h3>My thoughts on C# 14</h3><p>Actually they&#8217;ve been quite restrained this year. No crazy new ways to instantiate lists or classes that conceptually forks C#. I do get alot of comments on my channel saying that C# is getting too complicated, so glad they have shown restraint. Well done chaps!</p>]]></content:encoded></item><item><title><![CDATA[The state of WPF in 2025]]></title><description><![CDATA[Reports of WPF's death are exaggerated]]></description><link>https://www.edandersen.com/p/the-state-of-wpf-in-2025</link><guid isPermaLink="false">https://www.edandersen.com/p/the-state-of-wpf-in-2025</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Fri, 20 Jun 2025 03:17:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!zLYg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Deep dive this week into an important question that Redditors get in a panic about  quite often - is WPF Dead? </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zLYg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zLYg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 424w, https://substackcdn.com/image/fetch/$s_!zLYg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 848w, https://substackcdn.com/image/fetch/$s_!zLYg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 1272w, https://substackcdn.com/image/fetch/$s_!zLYg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zLYg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png" width="1456" height="727" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:727,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:833795,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/166371771?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zLYg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 424w, https://substackcdn.com/image/fetch/$s_!zLYg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 848w, https://substackcdn.com/image/fetch/$s_!zLYg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 1272w, https://substackcdn.com/image/fetch/$s_!zLYg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><br>Let's look at the full state of WPF today.</p><h3>Alive and Well in the Enterprise and &#8220;boring&#8221; stuff</h3><p>First things first: Microsoft is still actively working on WPF. It was made part of the open-source .NET Foundation and continues to receive meaningful updates. In .NET 8, we got hardware acceleration for RDP and a new folder dialog. For .NET 9, Microsoft is adding a Fluent theme to make WPF apps look more like modern Windows 11 applications, a feature many thought was reserved for newer frameworks. The official roadmap even shows plans for .NET 10. This isn't just life support; it's active development. This predictability is precisely what enterprises value, which is why you'll find WPF powering critical line-of-business applications on oil rigs, in maritime shipping, and across the manufacturing industry&#8212;the so-called "dark matter developers" who don't always show up in surveys but build the backbone of industry.</p><ul><li><p><a href="https://www.google.com/search?q=https://devblogs.microsoft.com/dotnet/whats-new-for-wpf-in-dotnet-8/">What's new for WPF in .NET 8 - Microsoft Dev Blogs</a></p></li><li><p><a href="https://github.com/dotnet/wpf/blob/main/roadmap.md">WPF Roadmap 2024 - GitHub</a></p></li><li><p><a href="https://www.google.com/search?q=https://learn.microsoft.com/en-us/dotnet/desktop/wpf/introduction/wpf-overview%3Fview%3Dnetdesktop-9.0">Windows Presentation Foundation - Microsoft Docs</a></p></li></ul><h3>Third parties make bank on WPF</h3><p>WPF&#8217;s longevity isn&#8217;t just thanks to Microsoft. I&#8217;d wager that third parties make more money from WPF than MS does themselves and are incentivized to keep it that way.<br><br>A massive ecosystem of vendors and open-source projects keeps it vibrant. Companies like Telerik and DevExpress continue to sell and support extensive component libraries, investing thousands to ensure developers can build complex, feature-rich applications quickly. At the same time, the open-source community is keeping things fresh. Before MS added the Fluent theme to WPF, popular projects like MahApps.Metro give WPF apps a modern Windows look and feel like Windows 10 rather than the classic WebForms inspired take, while Material Design in XAML allows you to build a Windows app that looks like an Android app. This combination of corporate and community support is a self perpetuating machine.<br></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gz1Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gz1Q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 424w, https://substackcdn.com/image/fetch/$s_!gz1Q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 848w, https://substackcdn.com/image/fetch/$s_!gz1Q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 1272w, https://substackcdn.com/image/fetch/$s_!gz1Q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gz1Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png" width="991" height="778" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:778,&quot;width&quot;:991,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:666608,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/166371771?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gz1Q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 424w, https://substackcdn.com/image/fetch/$s_!gz1Q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 848w, https://substackcdn.com/image/fetch/$s_!gz1Q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 1272w, https://substackcdn.com/image/fetch/$s_!gz1Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Pay up lads</figcaption></figure></div><ul><li><p><a href="https://www.telerik.com/blogs/wpf-net-maui-how-choose">WPF vs. .NET MAUI: How to Choose in 2024 - Telerik</a></p></li><li><p><a href="https://www.devexpress.com/wpf/">WPF UI Controls - DevExpress</a></p></li><li><p><a href="https://www.google.com/search?q=https://github.com/MaterialDesignInXAML/MaterialDesignInXAML">Material Design in XAML Toolkit - GitHub</a></p></li><li><p><a href="https://github.com/MahApps/MahApps.Metro">MahApps.Metro - GitHub</a></p></li></ul><h3>The Geographic Divide and the Ghost of WinUI</h3><p>So if it's not dead, where is it hiding? I think the debate is still a thing because  because WPF's popularity is highly regional. <a href="https://www.ziprecruiter.com/Salaries/WPF-Developer-Salary">In the United States, the average salary for a WPF developer is a chonky $130,000</a>, suggesting strong demand. But cross the pond to the UK, and the number of job postings mentioning WPF is vanishingly small. In Japan, they're practically non-existent outside of niche finance roles. Surprisingly, Google Trends data shows a huge resurgence of interest in China and South Korea. Meanwhile, Microsoft's intended successor, WinUI, has seen development activity fall off a cliff since late 2021, with its GitHub commit history looking like a ghost town compared to the flurry of activity around .NET MAUI, Avalonia, and even WPF itself.</p><p>I miss WinUI btw. Seriously. WinUI 2.x was fast, compiled native and created apps indistinguishable from the Windows 10 and Windows 11 shells. In my view if you cannot create the native Settings app pixel for pixel perfect then its not a &#8220;native&#8221; app toolkit.</p><ul><li><p><a href="https://www.google.com/search?q=https://www.scichart.com/the-future-of-wpf-is-wpf-dead/">The Future of WPF: Is WPF Dead? - SciChart</a></p></li><li><p><a href="https://github.com/microsoft/microsoft-ui-xaml">microsoft-ui-xaml (WinUI) Repository - GitHub</a></p></li></ul><h3>The Spirit Lives On: Avalonia and OpenSilver</h3><p>While WPF remains a Windows-only affair, its XAML-based spirit is finding new life in cross-platform frameworks. <a href="https://avaloniaui.net/">Avalonia</a> proudly calls itself the open-source WPF successor and even offers a product, XPF, designed to take your existing WPF applications and run them on macOS and Linux in minutes. It&#8217;s what Microsoft tried&#8212;and failed&#8212;to do with "WPF Everywhere" back in 2006, a project that morphed into the ill-fated Silverlight. And just like WPF, Silverlight's spirit has been resurrected by the community in the form of OpenSilver, which is picking up where Microsoft left off. For developers who honed their XAML skills on WPF, these frameworks offer an exciting path forward without abandoning their expertise.<br></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oLBF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oLBF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 424w, https://substackcdn.com/image/fetch/$s_!oLBF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 848w, https://substackcdn.com/image/fetch/$s_!oLBF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 1272w, https://substackcdn.com/image/fetch/$s_!oLBF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oLBF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png" width="1242" height="857" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:857,&quot;width&quot;:1242,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:341676,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/166371771?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oLBF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 424w, https://substackcdn.com/image/fetch/$s_!oLBF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 848w, https://substackcdn.com/image/fetch/$s_!oLBF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 1272w, https://substackcdn.com/image/fetch/$s_!oLBF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Avalonia keeping the dream alive</figcaption></figure></div><ul><li><p><a href="https://opensilver.net/">OpenSilver - A modern, open-source reimplementation of Silverlight</a></p></li><li><p><a href="https://www.google.com/search?q=https://learn.microsoft.com/en-us/xamarin/xamarin-forms/platform/wpf">Xamarin.Forms Platform Support - Microsoft Docs (Historical)</a></p></li></ul><h3>The Verdict</h3><p>So, is WPF dead? <strong>No.</strong> But it has probably flatlined.<br><br>Its spirit, however, has broken free from its Windows-only confines and lives on in modern, cross-platform frameworks. The transferrable design patterns, XAML, MVVM etc are reusable across Uno and Avalonia today, even as .NET MAUI sails off into the sunset.</p><div><hr></div><p><br>View this post in mega cringe video form:<br></p><div id="youtube2-aaKgNKLpeGA" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;aaKgNKLpeGA&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/aaKgNKLpeGA?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p><br><br></p>]]></content:encoded></item><item><title><![CDATA[JetBrains ReSharper comes to VS Code, SQL Server 2025 actually looks good]]></title><description><![CDATA[Teds Tech, May 29th 2025]]></description><link>https://www.edandersen.com/p/jetbrains-resharper-comes-to-vs-code</link><guid isPermaLink="false">https://www.edandersen.com/p/jetbrains-resharper-comes-to-vs-code</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Thu, 29 May 2025 10:06:03 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>What a week. If you haven&#8217;t seen it, I missed some cracking news from MS Build which was released about 3 hours before going to &#8220;print&#8221; last week - loose C# file execution without a .csproj file. I made a video about it here:</p><div id="youtube2-QWYFGdEeCkQ" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;QWYFGdEeCkQ&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/QWYFGdEeCkQ?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Onto this week&#8217;s news: </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lErf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lErf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 424w, https://substackcdn.com/image/fetch/$s_!lErf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 848w, https://substackcdn.com/image/fetch/$s_!lErf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 1272w, https://substackcdn.com/image/fetch/$s_!lErf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lErf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png" width="1131" height="667" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:667,&quot;width&quot;:1131,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:108550,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/164712246?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lErf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 424w, https://substackcdn.com/image/fetch/$s_!lErf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 848w, https://substackcdn.com/image/fetch/$s_!lErf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 1272w, https://substackcdn.com/image/fetch/$s_!lErf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><br>JetBrains releases ReSharper for VS Code</h2><p>For veteran developers, the news might bring a wave of nostalgia. ReSharper was, for many, an indispensable part of the C# development experience. The cry of "Please, a ReSharper license!" from a new team member was expected.</p><p>I&#8217;ve given it a go and you need to disable Microsoft's own C# and C# Dev Kit extensions, as ReSharper apparently prefers to take full command of the C# experience. This in itself signals the potential for a fascinating dynamic in the VS Code tooling space.</p><p>Once installed and your solution is loaded (perhaps after the classic ReSharper cache processing warms up, a sight familiar to Visual Studio users), the experience aims to be classic ReSharper. The "ReSharper Solution Explorer" makes an appearance, which basically mirrors the C# Dev Kit Solution Explorer. I had great success with the iconic "Initialize field from constructor" shortcut, a beloved time-saver, and it works just as well as it did in VS.</p><p>This move by JetBrains is particularly interesting given Microsoft's recent efforts to monetize the C# experience in VS Code through the C# Dev Kit, which is tied into Visual Studio subscriptions. I do think this will get somehow squashed by MS, there was a reason JetBrains announced in 2021 that they weren&#8217;t going to bother.</p><p><strong>Relevant Links:</strong></p><ul><li><p>JetBrains ReSharper in VS Code Public Preview Announcement <a href="https://blog.jetbrains.com/dotnet/2025/05/19/resharper-comes-to-microsoft-visual-studio-code/">https://blog.jetbrains.com/dotnet/2025/05/19/resharper-comes-to-microsoft-visual-studio-code/</a></p></li><li><p>ReSharper on the Visual Studio Code Marketplace: <a href="https://marketplace.visualstudio.com/items?itemName=JetBrains.ReSharper-VSCode">https://marketplace.visualstudio.com/items?itemName=JetBrains.ReSharper-VSCode</a></p></li></ul><h2>SQL Server 2025 gets decent JSON support</h2><p>I&#8217;ll ignore the AI stuff for this update.</p><p>Fascinating to me is the new <code>sp_invoke_external_rest_endpoint</code> stored procedure. This has serious potential. For years, reaching out to external web services from within SQL Server has been a clunky affair, often requiring complex CLR assemblies or other workarounds. Now, developers will be able to directly call secure HTTPS REST endpoints (supporting GET, POST, PUT, etc.) from stored procedures, triggers, or functions, and directly process the response. Imagine enriching your data in real-time by calling an external API, or triggering a workflow in another system based on a database event &#8211; all natively within SQL Server. Scary.</p><p>Complementing this external connectivity is a long-awaited enhancement to JSON support. SQL Server has had rudimentary JSON capabilities for a while &#8211; parsing with <code>OPENJSON</code> and formatting with <code>FOR JSON</code> &#8211; but it always felt like a bolt-on rather than a first-class citizen. Developers often looked to databases like PostgreSQL for more robust native JSON handling. With SQL Server 2025, that changes. The introduction of a native JSON data type aims to bring SQL Server to feature parity with competitors, allowing for more efficient storage, indexing, and querying of JSON documents directly within the relational database.</p><p>When you combine these two features &#8211; the ability to call a REST API and get a JSON response, and then store and manipulate that JSON natively &#8211; the possibilities are quite exciting, and perhaps a little "scary" in terms of the complex logic that might now reside in the database tier. </p><p>The "AI-ready" stuff  extends to integrated vector database querying capabilities and other GenAI model integrations and obviously they added GitHub Copilot to the beloved SQL Server Management Studio. </p><p><strong>Relevant Links:</strong></p><ul><li><p>Official SQL Server 2025 Public Preview Blog: <a href="https://cloudblogs.microsoft.com/sqlserver/2025/05/19/announcing-sql-server-2025-public-preview/">https://cloudblogs.microsoft.com/sqlserver/2025/05/19/announcing-sql-server-2025-public-preview/ </a></p></li><li><p>SQL Server 2025 Public Preview Discussion on Reddit: <a href="https://www.reddit.com/r/SQLServer/comments/1kqfecc/announcing_the_public_preview_of_sql_server_2025/">https://www.reddit.com/r/SQLServer/comments/1kqfecc/announcing_the_public_preview_of_sql_server_2025/</a></p></li></ul><h2>Windows Update Gets an Update</h2><p>In a spell of wishful thinking, MS looks like they&#8217;ll try to cajole the 1000s of disparate update mechanisms on Windows to all hook into Windows Update.</p><p>This sounds like a nightmare to me but you can read more about it on &#8220;Introducing a Unified Future for App Updates on Windows&#8221; at their blog: <a href="https://techcommunity.microsoft.com/blog/windows-itpro-blog/introducing-a-unified-future-for-app-updates-on-windows/4416354">https://techcommunity.microsoft.com/blog/windows-itpro-blog/introducing-a-unified-future-for-app-updates-on-windows/4416354</a></p><div><hr></div><p>Thanks for reading and continuing to subscribe. For a video version of this post, check out:</p><div id="youtube2-975HcGOTix4" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;975HcGOTix4&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/975HcGOTix4?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Microsoft Build 2025 updates for .NET developers]]></title><description><![CDATA[Ted's Tech, May 23rd 2025]]></description><link>https://www.edandersen.com/p/microsoft-build-2025-updates-for</link><guid isPermaLink="false">https://www.edandersen.com/p/microsoft-build-2025-updates-for</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Fri, 23 May 2025 08:13:07 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!AG_0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Microsoft Build 2025 has wrapped up and I&#8217;ve done my best to find something, anything, that might be of interest to .NET/C# developers.  It really was tough.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AG_0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AG_0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 424w, https://substackcdn.com/image/fetch/$s_!AG_0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 848w, https://substackcdn.com/image/fetch/$s_!AG_0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 1272w, https://substackcdn.com/image/fetch/$s_!AG_0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AG_0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png" width="917" height="516" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:516,&quot;width&quot;:917,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:359082,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/164059378?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!AG_0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 424w, https://substackcdn.com/image/fetch/$s_!AG_0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 848w, https://substackcdn.com/image/fetch/$s_!AG_0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 1272w, https://substackcdn.com/image/fetch/$s_!AG_0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>Azure AI Foundry Local</h2><p>One significant announcement is <strong>Azure Foundry Local</strong>. Think of it as a mini-version of Ollama, allowing you to run AI models locally. It's designed to work seamlessly with <strong>Azure AI Foundry</strong> and even includes an <strong>SDK for C#</strong> (though it's not yet on NuGet). This means you can integrate AI capabilities directly into your C# and .NET applications, running models on your local machine, even with full Mac GPU support. Imagine asking a question like, "Is .NET and C# cool and popular?" and getting an AI-powered "Yes!" from your local setup just to help alleviate your anxiety.</p><p>It provides a fully OpenAI Chat Completion API compatible local endpoint when you run it, so you can actually point existing apps (assuming you can change the model name) at it and get started.</p><p>The docs are here: <a href="https://learn.microsoft.com/en-gb/azure/ai-foundry/foundry-local/get-started">https://learn.microsoft.com/en-gb/azure/ai-foundry/foundry-local/get-started</a></p><h2><br><br>.NET Aspire rolls on to 9.3</h2><p>No Blazor, MVC, Razor Pages or any other content really - just .NET Aspire.</p><p>In a sign of insane irony, they&#8217;ve actually embedded GitHub Copilot into the Aspire Dashboard, which loops through the C# Dev Kit and the GitHub Copilot extension in VS Code.</p><p>I know the KPIs at MS are brutal but this is really quite something.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!J0O5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!J0O5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 424w, https://substackcdn.com/image/fetch/$s_!J0O5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 848w, https://substackcdn.com/image/fetch/$s_!J0O5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 1272w, https://substackcdn.com/image/fetch/$s_!J0O5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!J0O5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png" width="761" height="841" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:841,&quot;width&quot;:761,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:232948,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/164059378?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!J0O5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 424w, https://substackcdn.com/image/fetch/$s_!J0O5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 848w, https://substackcdn.com/image/fetch/$s_!J0O5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 1272w, https://substackcdn.com/image/fetch/$s_!J0O5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Azure App Service: Faster, Cheaper, Better!</h3><p>Now for something positive! Azure App Service is easily my favourite service on Azure that just keeps getting better. Microsoft announced a new service plan that promises significant improvements:</p><ul><li><p><strong>24% cost savings on Windows</strong> for better cost-performance.</p></li><li><p><strong>Faster temporary storage</strong>.</p></li><li><p>A wider range of CPU and RAM options, from modest setups to massive 256GB configurations.</p></li><li><p>Finally, <strong>high availability with only two availability zones</strong>. This means you no longer need three nodes on your web tier to achieve a <strong>99.99% SLA</strong>, potentially cutting a third off your App Service bills.</p></li></ul><p>Most projects, and I do mean like over 90% of them, should be running on Azure App Service in my opinion. It really is that good.</p><div><hr></div><p>Thank you for continuing to subscribe to the newsletter. If you&#8217;d like this post in slightly ironic video form, check out the below:<br></p><div id="youtube2-0nYY_WIJ6YU" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;0nYY_WIJ6YU&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/0nYY_WIJ6YU?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[VS Code is no longer an IDE, layoffs hit MS teams and .NET 10 Preview 4 is out]]></title><description><![CDATA[Teds Tech, May 15th 2025]]></description><link>https://www.edandersen.com/p/vs-code-is-no-longer-an-ide-layoffs</link><guid isPermaLink="false">https://www.edandersen.com/p/vs-code-is-no-longer-an-ide-layoffs</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Thu, 15 May 2025 08:05:13 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Rx0f!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Its been a couple of weeks since I&#8217;ve done one of these as I was away on a business trip last week. Regular service resumes below!</p><h3>VS Code 1.100 sees GitHub Copilot get absorbed totally</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Rx0f!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Rx0f!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 424w, https://substackcdn.com/image/fetch/$s_!Rx0f!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 848w, https://substackcdn.com/image/fetch/$s_!Rx0f!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 1272w, https://substackcdn.com/image/fetch/$s_!Rx0f!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Rx0f!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png" width="1129" height="632" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:632,&quot;width&quot;:1129,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:158259,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/163511991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Rx0f!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 424w, https://substackcdn.com/image/fetch/$s_!Rx0f!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 848w, https://substackcdn.com/image/fetch/$s_!Rx0f!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 1272w, https://substackcdn.com/image/fetch/$s_!Rx0f!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In a masterclass of product reclassification, VS Code is now being referred to as an &#8220;AI Editor&#8221;. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!C2xQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!C2xQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 424w, https://substackcdn.com/image/fetch/$s_!C2xQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 848w, https://substackcdn.com/image/fetch/$s_!C2xQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 1272w, https://substackcdn.com/image/fetch/$s_!C2xQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!C2xQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png" width="600" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:600,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:44914,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/163511991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!C2xQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 424w, https://substackcdn.com/image/fetch/$s_!C2xQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 848w, https://substackcdn.com/image/fetch/$s_!C2xQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 1272w, https://substackcdn.com/image/fetch/$s_!C2xQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Tracing the history here is fascinating. In June 2023, v1.80 release notes still referred to the GitHub Copilot as a separate extension:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PEN8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PEN8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 424w, https://substackcdn.com/image/fetch/$s_!PEN8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 848w, https://substackcdn.com/image/fetch/$s_!PEN8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 1272w, https://substackcdn.com/image/fetch/$s_!PEN8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PEN8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png" width="652" height="303" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:303,&quot;width&quot;:652,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:50443,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/163511991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PEN8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 424w, https://substackcdn.com/image/fetch/$s_!PEN8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 848w, https://substackcdn.com/image/fetch/$s_!PEN8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 1272w, https://substackcdn.com/image/fetch/$s_!PEN8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>But by v1.100, updates to the AI chat function is now indistinguishable from core functionality:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pY09!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pY09!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 424w, https://substackcdn.com/image/fetch/$s_!pY09!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 848w, https://substackcdn.com/image/fetch/$s_!pY09!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 1272w, https://substackcdn.com/image/fetch/$s_!pY09!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pY09!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png" width="652" height="299" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:299,&quot;width&quot;:652,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:57512,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/163511991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pY09!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 424w, https://substackcdn.com/image/fetch/$s_!pY09!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 848w, https://substackcdn.com/image/fetch/$s_!pY09!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 1272w, https://substackcdn.com/image/fetch/$s_!pY09!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>My view is that this is purely a response to Cursor and Windsurf. The GitHub Copilot extension is not open source, and now VS Code bundles a huge closed source extension along with it. VS Code is no longer &#8220;open source&#8221; by any definition.</p><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/HA520/4/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e2cd2628-2dbc-41fb-94d1-d67cebf61cef_1260x660.png&quot;,&quot;thumbnail_url_full&quot;:&quot;&quot;,&quot;height&quot;:400,&quot;title&quot;:&quot;VS Code subsumes GitHub Copilot timeline&quot;,&quot;description&quot;:&quot;&quot;}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/HA520/4/" width="730" height="400" frameborder="0" scrolling="no"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><h3>Layoffs hit MAUI, TypeScript teams</h3><p>Just in time for Microsoft Build next week, Microsoft has decided to lay off engineers across the firm, including &#8220;folks&#8221; in their TypeScript and .NET Android and MAUI teams. I believe the .NET MAUI team has been totally gutted.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!k6Jg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!k6Jg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 424w, https://substackcdn.com/image/fetch/$s_!k6Jg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 848w, https://substackcdn.com/image/fetch/$s_!k6Jg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 1272w, https://substackcdn.com/image/fetch/$s_!k6Jg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!k6Jg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png" width="602" height="235" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/718a863f-89f1-471b-a26a-77564a95d707_602x235.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:235,&quot;width&quot;:602,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:36278,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/163511991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!k6Jg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 424w, https://substackcdn.com/image/fetch/$s_!k6Jg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 848w, https://substackcdn.com/image/fetch/$s_!k6Jg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 1272w, https://substackcdn.com/image/fetch/$s_!k6Jg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>This poor chap has been at Microsoft for 18 years and worked on the TypeScript compiler being rewritten in Go:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xznX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xznX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 424w, https://substackcdn.com/image/fetch/$s_!xznX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 848w, https://substackcdn.com/image/fetch/$s_!xznX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 1272w, https://substackcdn.com/image/fetch/$s_!xznX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xznX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png" width="616" height="236" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:236,&quot;width&quot;:616,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:44690,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/163511991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xznX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 424w, https://substackcdn.com/image/fetch/$s_!xznX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 848w, https://substackcdn.com/image/fetch/$s_!xznX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 1272w, https://substackcdn.com/image/fetch/$s_!xznX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>I guess the beatings will continue until morale improves.</p><h2>.NET 10 Preview 4 </h2><p>Two main new features I like in this are new async methods for Zip folder creation, plus Minimal APIs get another boost with validation for Record types.</p><p>You can now do the following:</p><p><code>using System.IO.Compression;</code></p><p><code>await ZipFile.CreateFromDirectoryAsync("photos", "we-have-to-go-back-async.zip");</code></p><p>Whereas you were stuck with non-async versions in .NET 8.</p><div><hr></div><p>As always, I do appreciate you continuing to subscribe. For a more visual version of this post, check out the YouTube version below: </p><div id="youtube2-93pxUFZgS9g" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;93pxUFZgS9g&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/93pxUFZgS9g?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Github Copilot for upgrading .NET projects and querying Azure - .NET Conf recap]]></title><description><![CDATA[Ted's Tech, April 25th 2025]]></description><link>https://www.edandersen.com/p/github-copilot-for-upgrading-net</link><guid isPermaLink="false">https://www.edandersen.com/p/github-copilot-for-upgrading-net</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Fri, 25 Apr 2025 01:16:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!z1yT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Thanks for continuing to subscribe to the newsletter! The weather has picked up in Japan this week as we go truly into spring. Japan has four distinct seasons I am told repeatedly and they are not wrong.</p><p>Onto the Microsoft developer content&#8230;</p><h2>.NET Conf &#8220;Focus on Modernisation&#8221;</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!z1yT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!z1yT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 424w, https://substackcdn.com/image/fetch/$s_!z1yT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 848w, https://substackcdn.com/image/fetch/$s_!z1yT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 1272w, https://substackcdn.com/image/fetch/$s_!z1yT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!z1yT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png" width="970" height="576" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:576,&quot;width&quot;:970,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:345407,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/161838420?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!z1yT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 424w, https://substackcdn.com/image/fetch/$s_!z1yT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 848w, https://substackcdn.com/image/fetch/$s_!z1yT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 1272w, https://substackcdn.com/image/fetch/$s_!z1yT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><br>CSharp &#8220;Jeff&#8221; Fritz hosted a special .NET Conf this week aimed at showing the latest techniques and tools available to upgrade the considerable estate of legacy .NET apps out there. Even Scott Hanselman dug out his <a href="https://www.hanselman.com/babysmash/">Baby Smash app</a> for an update!</p><p>A central tool highlighted was the .NET Upgrade Assistant, available as a Visual Studio extension and a CLI tool, designed to analyze projects, identify potential issues, and aid in the upgrade process for various project types like WPF, WinForms, and older ASP.NET MVC apps<strong>. </strong>The Upgrade Assistant isn&#8217;t new - its been around for donkeys years - but they have now added Github Copilot to it to provide AI generated agent-like upgrade step generation. </p><p>I remain skeptical about AI being help to help much here considering the lack of publicly available training data for this process - upgrades from .NET Framework to the latest .NET versions are the equivalent of open heart surgery and to do correctly is one of the hardest things a .NET Developer can do. It requires over a decade of archeological knowledge about .NET versions and their upgrade paths - where is this training data coming from? CodeProject blog posts?</p><p>One wild demo was the ability to embed Blazor components in a classic WebForms app. I highly suggest you watch the video as it really was a tour de force of proper .NET boomer content: </p><div id="youtube2-ZwmD__W8UFM" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;ZwmD__W8UFM&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/ZwmD__W8UFM?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Azure releases its own MCP server</h2><p>Previously this functionality was limited to Copilot within Azure itself, but to score internet points the Azure team have released a new MCP server that allows local agents, either Copilot in Agent mode, Claude, Cursor or other MCP clients to access information about your Azure infrastructure.</p><p>What&#8217;s more impressive is that it is built in .NET 9 and C#! Yes! No JavaScript or Go cop-outs here. What is wild is the way they&#8217;ve <a href="https://github.com/Azure/azure-mcp/tree/main/eng/npm">wrapped npx and the node package manager around calling different .NET binaries for each platform</a>. I think its time that .NET had an <code>npx</code> equivalent.</p><p>They have been good and for now restricted the operations to read-only. But this is only a short leap away from the MCP server being able to do tasks like creating resources and then its just another short leap away from deleting them. The team know they are on the edge of something pretty crazy here, so have added one of the best worded disclaimers I&#8217;ve ever seen:</p><blockquote><p>MCP as a phenomenon is very novel and cutting-edge. As with all new technology standards, consider doing a security review to ensure any systems that integrate with MCP servers follow all regulations and standards your system is expected to adhere to. This includes not only the Azure MCP Server, but any MCP client/agent that you choose to implement down to the model provider.</p></blockquote><p>Basically trying to pass on any blame if something goes wrong.</p><p>One amazing example of Microsoft left hand not talking to the right hand is that the MCP server they&#8217;ve built isn&#8217;t currently compatible with their own Semantic Kernel framework due to the function names <a href="https://github.com/Azure/azure-mcp/issues/42">having hyphens instead of underscores</a>, which means they&#8217;ve only tested it using the VSCode/Github Copilot MCP client and not their own .NET agents framework. Classic.</p><p><strong>Azure MCP</strong>: <a href="https://github.com/Azure/azure-mcp">https://github.com/Azure/azure-mcp</a><br></p><h2>Days since Microsoft open source drama: 0</h2><p>The author of popular Kubernetes open source project <a href="https://github.com/spegel-org/spegel">Spegel</a> saw parts of their project copied by the Azure team to a project called <a href="https://github.com/Azure/peerd">Peerd</a>. Spegel is an OSS project released under the MIT license, so so what I hear you cry? Well the MIT license requires the copyrights of the author to remain in the source code and to be distributed with the software - the dev teams at Microsoft removed the LICENSE file from the &#8220;fork&#8221;, replacing it with their own, violating the license and therefore technically the copyright.</p><p>The blog post explains that MS &#8220;interviewed&#8221; the author before the &#8220;fork&#8221;, doing a brain dump from the author (probably recorded into OneNote and summarised by Copilot). A similar technique to the <a href="https://www.theverge.com/2020/6/2/21277863/microsoft-winget-windows-package-manager-appget-response-credit-comment">WinGet / AppGet</a> clone drama.</p><p>The Hacker News outrage has been brutal and in my opinion justified. I also put the word &#8220;fork&#8221; in quotes because in reality it looks like the project was copy and pasted, which isn&#8217;t technically a &#8220;fork&#8221;. <a href="https://news.ycombinator.com/item?id=43752389">This Hacker News sleuth figured it out.</a><br><br>Based on the commit history of Peerd essentially being a single person, I think that someone thought this was a slam dunk project for a promotion. Well, they&#8217;ve just set MS&#8217;s trust with the open source community back several years instead.<br><br><strong>The post</strong>: https://philiplaine.com/posts/getting-forked-by-microsoft/<br><strong>The Github meltdown</strong>: https://github.com/Azure/peerd/issues/109<br><strong>The damage control PR reply</strong>: https://news.ycombinator.com/item?id=43755745</p><div><hr></div><p>Thanks for reading as always! For a video version of this post, be sure to check out: </p><div id="youtube2-d4jUHwbffKQ" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;d4jUHwbffKQ&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/d4jUHwbffKQ?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.edandersen.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[.NET 10 Preview 3 releases and Semantic Kernel Python catches up to .NET]]></title><description><![CDATA[Ted's Tech, April 18th 2025]]></description><link>https://www.edandersen.com/p/net-10-preview-3-releases-and-semantic</link><guid isPermaLink="false">https://www.edandersen.com/p/net-10-preview-3-releases-and-semantic</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Fri, 18 Apr 2025 23:33:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7gsb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Howdy,</p><p>This week has seen a couple of interesting emissions from the MS teams. I&#8217;ve read about them so you don&#8217;t have to. Thanks for remaining a newsletter subscriber as I try out this new format - let me know if it works for you.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7gsb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7gsb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 424w, https://substackcdn.com/image/fetch/$s_!7gsb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 848w, https://substackcdn.com/image/fetch/$s_!7gsb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 1272w, https://substackcdn.com/image/fetch/$s_!7gsb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7gsb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png" width="1133" height="684" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:684,&quot;width&quot;:1133,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:134619,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://edandersen.substack.com/i/161585803?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7gsb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 424w, https://substackcdn.com/image/fetch/$s_!7gsb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 848w, https://substackcdn.com/image/fetch/$s_!7gsb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 1272w, https://substackcdn.com/image/fetch/$s_!7gsb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>.NET 10 Preview 3 lands</h2><p>Microsoft just <a href="https://devblogs.microsoft.com/dotnet/dotnet-10-preview-3/">dropped the third preview</a> of .NET 10, the latest sneak peek at the next Long-Term Support (LTS) version due later this year. LTS releases are very important as they are quite short lived (3 years) and every .NET 8 app will have only a year to update to .NET 10 before support stops, creating a bunch of activity. They&#8217;ve been busy&#8230;</p><p><strong>Highlights:</strong></p><ul><li><p><strong>C# 14:</strong> Just what we&#8217;ve always wanted (!) - extension methods, which let you add static methods, instance properties, and static properties to existing types like Ruby Monkey Patching from 2014. There's also a cleaner way to handle nulls with null-conditional assignment.</p></li><li><p><strong>ASP.NET Core &amp; Blazor:</strong> Blazor is still going (yeah), with some improvements to manage state. Blazor WebAssembly apps now get default HttpClient response streaming for better memory usage with large responses and support for fingerprinted static assets for better caching. Plus, built-in support for Server-Sent Events (SSE) is here for real-time updates - could this finally be the end of SignalR?</p></li><li><p><strong>.NET MAUI is still alive:</strong> MAUI devs will lament the loss of ListView, Cell and TableView as they are being <a href="https://github.com/dotnet/core/blob/main/release-notes/10.0/preview/preview3/dotnetmaui.md">deprecated</a>. On the plus side, you get fullscreen video playback in Android WebViews and an easier way to check if location services are enabled with Geolocation.IsEnabled.</p></li></ul><p><strong>Helpful Links:</strong></p><ul><li><p><strong>.NET Blog Announcement:</strong> <a href="https://devblogs.microsoft.com/dotnet/dotnet-10-preview-3/">https://devblogs.microsoft.com/dotnet/dotnet-10-preview-3/</a></p></li><li><p><strong>InfoQ Article:</strong> <a href="https://www.infoq.com/news/2025/04/dotnet-10-preview3/">https://www.infoq.com/news/2025/04/dotnet-10-preview3/</a></p></li><li><p><strong>.NET 10 Release Notes (GitHub):</strong> <a href="https://github.com/dotnet/core/tree/main/release-notes/10.0">https://github.com/dotnet/core/tree/main/release-notes/10.0</a></p></li><li><p><strong>.NET Downloads:</strong> <a href="https://www.google.com/search?q=https://dotnet.microsoft.com/download/dotnet/10.0">https://dotnet.microsoft.com/download/dotnet/10.0</a></p></li></ul><h2>Semantic Kernel adds MCP and Google's A2A for Python users</h2><p>Microsoft's <a href="https://github.com/microsoft/semantic-kernel">Semantic Kernel</a> for Python just got a major upgrade, adding support for two important interoperability protocols: the <a href="https://modelcontextprotocol.io/introduction">Model Context Protocol (MCP)</a> and <a href="https://github.com/google/A2A">Google's Agent-to-Agent (A2A) protocol</a> - bringing it up to parity with the C# and .NET versions, which saw MCP capability added in March.</p><p>I&#8217;m normally super salty about this stuff, but seeing it come through first for .NET has been great, likely because they are using it for Copilot under the hood.</p><p><strong>What does this mean?</strong></p><ul><li><p><strong>MCP:</strong> SK can now act as both an <a href="https://devblogs.microsoft.com/semantic-kernel/semantic-kernel-adds-model-context-protocol-mcp-support-for-python/">MCP host/client and an MCP server</a>. Think of MCP like a "USB for AI" &#8211; it standardizes how AI apps connect to external tools and data. As a host, your SK agent can easily use tools exposed by <em>any</em> MCP server (like those for GitHub, Slack, or custom systems). As a server, you can share your SK functions, prompts, or even entire agents with other MCP clients, like Anthropic's Claude Desktop or potentially IDEs. This makes building complex, modular AI systems much easier if that is your kind of thing.</p></li><li><p><strong>A2A Integration:</strong> SK now also integrates with <a href="https://devblogs.microsoft.com/semantic-kernel/integrating-semantic-kernel-python-with-googles-a2a-protocol/">Google's A2A protocol</a> , which is designed for communication <em>between</em> different AI agents. The example MS have provided shows a &#8220;travel manager&#8221; agent delegating tasks (like currency conversion or activity planning) to specialized sub-agents using A2A for discovery and routing.</p></li></ul><p><strong>Sources:</strong></p><ul><li><p><strong>SK MCP Blog Post (Python):</strong> <a href="https://devblogs.microsoft.com/semantic-kernel/semantic-kernel-adds-model-context-protocol-mcp-support-for-python/">https://devblogs.microsoft.com/semantic-kernel/semantic-kernel-adds-model-context-protocol-mcp-support-for-python/</a></p></li><li><p><strong>SK A2A Blog Post (Python):</strong> <a href="https://devblogs.microsoft.com/semantic-kernel/integrating-semantic-kernel-python-with-googles-a2a-protocol/">https://devblogs.microsoft.com/semantic-kernel/integrating-semantic-kernel-python-with-googles-a2a-protocol/</a></p></li><li><p><strong>MCP Official Documentation:</strong> <a href="https://modelcontextprotocol.io/introduction">https://modelcontextprotocol.io/introduction</a></p></li><li><p><strong>Google A2A Repository (with SK sample):</strong> <a href="https://github.com/google/A2A">https://github.com/google/A2A</a></p></li><li><p><strong>SK Python MCP Samples:</strong> <a href="https://github.com/microsoft/semantic-kernel/tree/main/python/samples/concepts/mcp/">https://github.com/microsoft/semantic-kernel/tree/main/python/samples/concepts/mcp/</a></p></li><li><p><strong>SK Python MCP Demos:</strong> <a href="https://github.com/microsoft/semantic-kernel/tree/main/python/samples/demos/mcp_server/">https://github.com/microsoft/semantic-kernel/tree/main/python/samples/demos/mcp_server/</a></p></li></ul><h2>Copilot can now help with Azure SQL</h2><p><a href="https://techcommunity.microsoft.com/blog/azuresqlblog/announcing-general-availability-of-azure-sql-database-capabilities-for-microsoft/4403518">Microsoft have added a load of new Copilot functionality to the Azure Portal</a>, to basically save you a Google search or two.</p><p>You can now prompt it to help you&#8230;</p><ul><li><p><strong>Troubleshoot SQL Errors:</strong> Get help diagnosing common SQL errors (like 10928, 18456, 40613) with explanations and suggested fixes.</p></li><li><p><strong>Optimize Performance:</strong> Identify potential bottlenecks like storage or I/O limits and get recommendations.</p></li><li><p><strong>Simplify Configuration:</strong> Get guidance on choosing service tiers or constructing connection strings.</p></li><li><p><strong>Manage Security:</strong> Get assistance with Transparent Data Encryption (TDE) issues or data replication problems.</p></li></ul><p>Now originally they planned to allow you to use Copilot to directly write database queries in the Query editor within the Azure Portal, but it looks like they&#8217;ve decided to push this back and keep it exclusive to a future SSMS release. Seemed pretty useful.</p><p><strong>More info:</strong></p><ul><li><p><strong>Azure SQL Blog Announcement:</strong> <a href="https://techcommunity.microsoft.com/blog/azuresqlblog/announcing-general-availability-of-azure-sql-database-capabilities-for-microsoft/4403518">https://techcommunity.microsoft.com/blog/azuresqlblog/announcing-general-availability-of-azure-sql-database-capabilities-for-microsoft/4403518</a></p></li><li><p><strong>Microsoft Copilot in Azure Overview:</strong> <a href="https://learn.microsoft.com/en-us/azure/copilot/overview">https://learn.microsoft.com/en-us/azure/copilot/overview</a></p></li><li><p><strong>Copilot in Azure with Azure SQL DB:</strong> <a href="https://aka.ms/sqlcopilot">https://aka.ms/sqlcopilot</a></p></li><li><p><strong>FAQ:</strong> <a href="https://aka.ms/sqlcopilot-faq">https://aka.ms/sqlcopilot-faq</a></p></li><li><p><strong>Copilot in SSMS Session (SQL Conf 2025):</strong> <a href="https://learn.microsoft.com/en-us/shows/azure-developers-sql-conf-2025/a-developers-guide-to-using-copilot-in-ssms">https://learn.microsoft.com/en-us/shows/azure-developers-sql-conf-2025/a-developers-guide-to-using-copilot-in-ssms</a></p></li></ul><div><hr></div><p>Here is a video version of this post:</p><div id="youtube2-FzQlz_uzKQk" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;FzQlz_uzKQk&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/FzQlz_uzKQk?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p><br><br>If there is anything you&#8217;d like to see me cover next week, send me a DM on Twitter/X at <a href="https://x.com/edandersen">@edandersen</a>. Cheers.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.edandersen.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[File Search in C# / .NET with Azure OpenAI Service Assistants V2 API]]></title><description><![CDATA[The Azure OpenAI Service is a variant of OpenAI that runs within your Azure tenant, which makes it great for companies and use cases where data sovereignty is important.]]></description><link>https://www.edandersen.com/p/file-search-in-azure-openai-service-assistants-v2-api</link><guid isPermaLink="false">https://www.edandersen.com/p/file-search-in-azure-openai-service-assistants-v2-api</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Mon, 29 Jul 2024 06:27:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/4477a59f-f9b1-479e-a670-55bc2b1eada8_784x572.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The Azure OpenAI Service is a variant of OpenAI that runs within your Azure tenant, which makes it great for companies and use cases where data sovereignty is important. I think its great!</p><p>The most significant update is that the service is now open to everyone, and you don&#8217;t need special approval to access it. Previously, to gain access to Azure OpenAI Service, you had to fill a special form and get approval from Microsoft. This was exclusive to Microsoft&#8217;s partners. But now, anyone with an Azure subscription can access the service, which is great news.</p><p>We also finally now have access to the retrieval tool in the OpenAI Assistant API, named the &#8220;file search&#8221; tool. This tool allows the LLM to search for file information inside files, <a href="https://github.com/Azure/azure-sdk-for-js/issues/28550">a feature that was not previously supported</a>. Users in Azure were left with no other option but to use the slow code interpreter tool before now.</p><p>However, the <a href="https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/file-search?tabs=python">file search documentation</a> still lacks some source code examples, especially for C# and .NET. Although Python and REST calls are covered, I really wish Microsoft would include samples in C# and .NET in their docs by default.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Oohd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7afd973a-53cb-4d31-b6e8-b54f847d2496_784x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Oohd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7afd973a-53cb-4d31-b6e8-b54f847d2496_784x572.png 424w, https://substackcdn.com/image/fetch/$s_!Oohd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7afd973a-53cb-4d31-b6e8-b54f847d2496_784x572.png 848w, https://substackcdn.com/image/fetch/$s_!Oohd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7afd973a-53cb-4d31-b6e8-b54f847d2496_784x572.png 1272w, https://substackcdn.com/image/fetch/$s_!Oohd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7afd973a-53cb-4d31-b6e8-b54f847d2496_784x572.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Oohd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7afd973a-53cb-4d31-b6e8-b54f847d2496_784x572.png" width="784" height="572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7afd973a-53cb-4d31-b6e8-b54f847d2496_784x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:784,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Oohd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7afd973a-53cb-4d31-b6e8-b54f847d2496_784x572.png 424w, https://substackcdn.com/image/fetch/$s_!Oohd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7afd973a-53cb-4d31-b6e8-b54f847d2496_784x572.png 848w, https://substackcdn.com/image/fetch/$s_!Oohd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7afd973a-53cb-4d31-b6e8-b54f847d2496_784x572.png 1272w, https://substackcdn.com/image/fetch/$s_!Oohd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7afd973a-53cb-4d31-b6e8-b54f847d2496_784x572.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">No C# samples &#128577;</figcaption></figure></div><p>I already have a <a href="https://github.com/edandersen/csharp-openai-assistants-dotnet-console/">project on my GitHub</a> where I used the old Code Interpreter tool to use the Assistant API to query for information in a file. Now that File Search is supported, it was time to take another look at it and update it!</p><div><hr></div><p>To start, we&#8217;ll change the SDK package that is used, switching it over to version two of this top-level nuget package, <a href="https://www.nuget.org/packages/Azure.AI.OpenAI/2.0.0-beta.2">Azure.AI.OpenAI</a>, which now seems to include all of the Assistant stuff.</p><pre><code>&lt;ItemGroup&gt;
  &lt;PackageReference Include="Azure.AI.OpenAI" Version="2.0.0-beta.2" /&gt;
&lt;/ItemGroup&gt;</code></pre><p>Next up, load up an AzureOpenAIClient and get an AssistantClient from it. You&#8217;ll need to ignore the OPENAI001 warning at the moment.</p><pre><code>using Azure.AI.OpenAI;
using OpenAI.Assistants;

AzureOpenAIClient azureClient = new(
            new Uri("https://your-deployment-url.openai.azure.com/"),
            new Azure.AzureKeyCredential("your azure open ai api key"));

#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

var client = azureClient.GetAssistantClient();

#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.</code></pre><p>We&#8217;ll grab the filename from the command-line arguments that we pass in.</p><pre><code>var filename = args[0];</code></pre><p>We&#8217;ll create an assistant that will answer questions from a user about the provided file using the file search tool. The model name here is your deployment name on Azure OpenAI service.</p><pre><code>var assistantCreationOptions = new AssistantCreationOptions
{
    Name = "File question answerer",
    Instructions = "Answer questions from the user about the provided file.",
    Tools = { ToolDefinition.CreateFileSearch() },
};

var assistant = await client.CreateAssistantAsync("gpt4-assistant2", assistantCreationOptions);</code></pre><p>Next, we&#8217;ll upload the file and print its name to the console.</p><pre><code>var fileUploadResponse = await azureClient.GetFileClient().UploadFileAsync(File.Open(filename, FileMode.Open), 
System.IO.Path.GetFileName(filename), OpenAI.Files.FileUploadPurpose.Assistants);
Console.WriteLine($"Uploaded file {fileUploadResponse.Value.Filename}");</code></pre><p>We&#8217;ll ask the user to provide a question about the file.</p><pre><code>Console.WriteLine("Ask a question about the file (empty response to quit):");
var question = Console.ReadLine();

var thread = await client.CreateThreadAsync();</code></pre><p>Then we&#8217;ll enter a loop in which we&#8217;ll create a message with the user&#8217;s question and an attachment to the uploaded file. We&#8217;ll start a run and print the run&#8217;s updates to the console. You&#8217;ll notice we can now using the streaming API from an Assistant context, which means we can print tokens out as they are generated, something that wasn&#8217;t previously supported.</p><pre><code>while (!string.IsNullOrWhiteSpace(question))
{
    var messageCreationOptions = new MessageCreationOptions();
    messageCreationOptions.Attachments.Add(new MessageCreationAttachment(fileUploadResponse.Value.Id, new List&lt;ToolDefinition&gt;() {ToolDefinition.CreateFileSearch()}));

    await client.CreateMessageAsync(thread, new List&lt;MessageContent&gt;() { MessageContent.FromText(question)}, messageCreationOptions);

    await foreach (StreamingUpdate streamingUpdate
            in client.CreateRunStreamingAsync(thread, assistant, new RunCreationOptions()))
        {
            if (streamingUpdate.UpdateKind == StreamingUpdateReason.RunCreated)
            {
                Console.WriteLine($"--- Run started! ---");
            }

            else if (streamingUpdate is MessageContentUpdate contentUpdate)
            {
                if (contentUpdate?.TextAnnotation?.InputFileId == fileUploadResponse.Value.Id)
                {
                    Console.Write(" (From: " + fileUploadResponse.Value.Filename + ")");
                } 
                else 
                {
                    Console.Write(contentUpdate?.Text);
                }
            } 
        }

    Console.WriteLine();
    Console.WriteLine("Your response: (leave empty to quit)");
    question = Console.ReadLine();

}</code></pre><p>Finally, we&#8217;ll clean up the file and assistant before exiting the application.</p><pre><code>Console.WriteLine("Cleaning up and exiting...");
await azureClient.GetFileClient().DeleteFileAsync(fileUploadResponse.Value.Id);
await client.DeleteThreadAsync(thread.Value.Id);
await client.DeleteAssistantAsync(assistant.Value.Id);</code></pre><div><hr></div><p>By running this application passing the path to a local file as the first parameter, you&#8217;ll get a nice console prompt where you can ask questions about the file.</p><p>For a video version of this post, take a look at the following:</p><div class="captioned-image-container"><figure><div id="youtube2-umzMPlaKLQo" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;umzMPlaKLQo&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/umzMPlaKLQo?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div></figure></div><p>Thanks for reading!</p>]]></content:encoded></item><item><title><![CDATA[.NET Aspire and the future of .NET]]></title><description><![CDATA[In less than a year, .NET Aspire has made it from a preview release all the way through to General Availability and its inclusion in Visual Studio.]]></description><link>https://www.edandersen.com/p/dotnet-aspire-and-the-future-of-dotnet</link><guid isPermaLink="false">https://www.edandersen.com/p/dotnet-aspire-and-the-future-of-dotnet</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Mon, 15 Jul 2024 08:52:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/9dOKLRbDem4" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><div id="youtube2-9dOKLRbDem4" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;9dOKLRbDem4&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/9dOKLRbDem4?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div></figure></div><p>In less than a year, .NET Aspire has made it from a preview release all the way through to General Availability and its inclusion in Visual Studio. The marketing keeps saying that the important part of .NET Aspire is that it makes apps &#8220;cloud-native&#8221;, but that is only a fraction of the story &#8211; and in my opinion the least important part.</p><p>It is my opinion that .NET Aspire is an attempt to bring order to the chaos of the last decade of .NET software development. The most important quote on the documentation is the following:</p><blockquote><p>.NET Aspire is an&nbsp;<em>opinionated</em>&nbsp;stack for building resilient, observable, and configurable cloud-native applications with .NET.</p><p><a href="https://devblogs.microsoft.com/dotnet/introducing-dotnet-aspire-simplifying-cloud-native-development-with-dotnet-8/">https://devblogs.microsoft.com/dotnet/introducing-dotnet-aspire-simplifying-cloud-native-development-with-dotnet-8/</a></p></blockquote><p>Note their emphasis on <em>opinionated</em> and not cloud. The last time I&#8217;ve seen an effort this concerted to have an opinion on how software should be built was when ASP.NET MVC was released way back in 2009. Let me explain.</p><h2>.NET Aspire out of the box</h2><p>When you create a new template with .NET Aspire and launch the &#8220;AppHost&#8221; project, you get the following:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!w3Fh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d290be0-7d82-4c0f-99a7-baeb18383fd8_1024x520.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!w3Fh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d290be0-7d82-4c0f-99a7-baeb18383fd8_1024x520.png 424w, https://substackcdn.com/image/fetch/$s_!w3Fh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d290be0-7d82-4c0f-99a7-baeb18383fd8_1024x520.png 848w, https://substackcdn.com/image/fetch/$s_!w3Fh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d290be0-7d82-4c0f-99a7-baeb18383fd8_1024x520.png 1272w, https://substackcdn.com/image/fetch/$s_!w3Fh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d290be0-7d82-4c0f-99a7-baeb18383fd8_1024x520.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!w3Fh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d290be0-7d82-4c0f-99a7-baeb18383fd8_1024x520.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6d290be0-7d82-4c0f-99a7-baeb18383fd8_1024x520.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!w3Fh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d290be0-7d82-4c0f-99a7-baeb18383fd8_1024x520.png 424w, https://substackcdn.com/image/fetch/$s_!w3Fh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d290be0-7d82-4c0f-99a7-baeb18383fd8_1024x520.png 848w, https://substackcdn.com/image/fetch/$s_!w3Fh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d290be0-7d82-4c0f-99a7-baeb18383fd8_1024x520.png 1272w, https://substackcdn.com/image/fetch/$s_!w3Fh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d290be0-7d82-4c0f-99a7-baeb18383fd8_1024x520.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><ul><li><p>A full dashboard listing all services in the project, by default an API and a Blazor (&#8230;) UI project</p><ul><li><p>It should be noted that these auto-run, no more faffing with launch.json or &#8220;Multiple Startup projects&#8221; in VS</p></li></ul></li><li><p>Live console output from each running service</p></li><li><p>Structured logs and traces powered by OpenTelemetry</p></li><li><p>Process metrics (requests per second, CPU usage etc) for each service</p></li><li><p>Integration with Docker Desktop or Podman if installed</p></li></ul><p>This is such a massive stake in the ground for what the .NET Development team sees as &#8220;best practice&#8221; that it puts many open source utilities, projects and cottage industries of internal &#8220;frameworks&#8221; out of business.</p><h2>Pre-Aspire times</h2><p>Released in 2004, Ruby on Rails was taking off as the &#8220;cool&#8221; web development framework by the late 2000s. All Microsoft had to offer at the time was WebForms on .NET Framework, originally released in 2002.</p><p>Ruby on Rails was an MVC based framework promising that every application would be broadly similar, because it favoured &#8220;<a href="https://rubyonrails.org/doctrine">convention over configuration</a>&#8220;. This meant that Controllers were in one place, Views were in one place, Models were in one place, and this was common across every project you would encounter.</p><p>The .NET team at the time in Microsoft needed to compete with this. WebForms, with its code behind, viewstate and other &#8220;hide how HTTP works&#8221; horrors was not cutting it. They probably had the internal data that all these cool new startups were all using Rails.</p><p>So ASP.NET MVC was born and version 1 was released in 2009, bringing an &#8220;opinionated&#8221; MVC framework to .NET for the first time. Like Rails, Controllers were where you expected them to be in every project you came across. It was even actually made open source, unlike WebForms.</p><p>.NET MVC was wildly popular in the .NET world and continued through to version 5 and then merged with its &#8220;Web API&#8221; cousin for .NET Core. For a period of time .NET was sane. You could go into a project and &#8211; assuming the architects had not gone crazy trying to create job security &#8211; find things where they were supposed to be. With .NET Core, the .NET team even finally created a &#8220;blessed&#8221; dependency injection framework so you didn&#8217;t have to argue over NInject, Autofac, Castle Windsor etc.</p><h2>Where it started going &#8220;off the Rails&#8221;</h2><p>The issues with convention over configuration are that</p><ul><li><p>You have to learn the conventions</p></li><li><p>it is hard for an architect to &#8220;invent&#8221; any new patterns</p></li><li><p>There is a bit of boilerplate required to get started</p></li></ul><p>And this is where I believe things started taking a turn for the worse.</p><p>The mid 2010s saw the birth of .NET Core, and the original versions looked like we were going to have a clean break from WebForms and other .NET Framework bleakness.</p><p>However, for some people following patterns and architectural principles was just too hard &#8211; and to placate the WebForms crowd who had been left out of the cold with the release of .NET Core, Razor Pages were born.</p><p>Razor Pages promised the same rapid application development experience as WebForms. It even had code-behind like WebForms. At some point during this journey Blazor was invented, putting Razor Pages on steroids and adding even more layers of abstraction and drag&#8217;n&#8217;drop goodness. Need to know where your business logic goes? Who cares! Just whack it in the file alongside your UI code! Or maybe not! Its your chance to create your own software architecture!</p><p>Minimal API project templates also now truly allow your application to be unique, and you can invent any number of ways to get data on the screen. Roll your own fake controller system? Why not! Directly call the database in your GET handler? Cool! Use <a href="https://github.com/FastEndpoints/FastEndpoints">fastendpoints</a> to er, bring a hybrid Controller/Mediatr CQRS system back? Lets go!</p><h2>Towards a better future with .NET Aspire (?)</h2><p>Which is why Aspire excites me.</p><p>Who remembers ELMAH? Well, now you don&#8217;t need it as its built in to the Aspire UI.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IksC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8cb5852-7bbd-426c-8985-65a2e81ed77d_829x633.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IksC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8cb5852-7bbd-426c-8985-65a2e81ed77d_829x633.png 424w, https://substackcdn.com/image/fetch/$s_!IksC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8cb5852-7bbd-426c-8985-65a2e81ed77d_829x633.png 848w, https://substackcdn.com/image/fetch/$s_!IksC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8cb5852-7bbd-426c-8985-65a2e81ed77d_829x633.png 1272w, https://substackcdn.com/image/fetch/$s_!IksC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8cb5852-7bbd-426c-8985-65a2e81ed77d_829x633.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IksC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8cb5852-7bbd-426c-8985-65a2e81ed77d_829x633.png" width="829" height="633" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b8cb5852-7bbd-426c-8985-65a2e81ed77d_829x633.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:633,&quot;width&quot;:829,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!IksC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8cb5852-7bbd-426c-8985-65a2e81ed77d_829x633.png 424w, https://substackcdn.com/image/fetch/$s_!IksC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8cb5852-7bbd-426c-8985-65a2e81ed77d_829x633.png 848w, https://substackcdn.com/image/fetch/$s_!IksC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8cb5852-7bbd-426c-8985-65a2e81ed77d_829x633.png 1272w, https://substackcdn.com/image/fetch/$s_!IksC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8cb5852-7bbd-426c-8985-65a2e81ed77d_829x633.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Who remembers MiniProfiler? An amazing add-on that was ported from Ruby to .NET that showed tracing directly in the UI of your app? Well, thats also now built into Aspire via OpenTelemetry.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Zb-P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6c9d10-87c8-44d8-a88e-7d05456bfad8_652x235.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Zb-P!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6c9d10-87c8-44d8-a88e-7d05456bfad8_652x235.png 424w, https://substackcdn.com/image/fetch/$s_!Zb-P!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6c9d10-87c8-44d8-a88e-7d05456bfad8_652x235.png 848w, https://substackcdn.com/image/fetch/$s_!Zb-P!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6c9d10-87c8-44d8-a88e-7d05456bfad8_652x235.png 1272w, https://substackcdn.com/image/fetch/$s_!Zb-P!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6c9d10-87c8-44d8-a88e-7d05456bfad8_652x235.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Zb-P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6c9d10-87c8-44d8-a88e-7d05456bfad8_652x235.png" width="652" height="235" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cd6c9d10-87c8-44d8-a88e-7d05456bfad8_652x235.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:235,&quot;width&quot;:652,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Zb-P!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6c9d10-87c8-44d8-a88e-7d05456bfad8_652x235.png 424w, https://substackcdn.com/image/fetch/$s_!Zb-P!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6c9d10-87c8-44d8-a88e-7d05456bfad8_652x235.png 848w, https://substackcdn.com/image/fetch/$s_!Zb-P!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6c9d10-87c8-44d8-a88e-7d05456bfad8_652x235.png 1272w, https://substackcdn.com/image/fetch/$s_!Zb-P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd6c9d10-87c8-44d8-a88e-7d05456bfad8_652x235.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OX6s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefb11412-b70e-45be-846a-889bf9f9c9ef_988x579.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OX6s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefb11412-b70e-45be-846a-889bf9f9c9ef_988x579.png 424w, https://substackcdn.com/image/fetch/$s_!OX6s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefb11412-b70e-45be-846a-889bf9f9c9ef_988x579.png 848w, https://substackcdn.com/image/fetch/$s_!OX6s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefb11412-b70e-45be-846a-889bf9f9c9ef_988x579.png 1272w, https://substackcdn.com/image/fetch/$s_!OX6s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefb11412-b70e-45be-846a-889bf9f9c9ef_988x579.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OX6s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefb11412-b70e-45be-846a-889bf9f9c9ef_988x579.png" width="988" height="579" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/efb11412-b70e-45be-846a-889bf9f9c9ef_988x579.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:579,&quot;width&quot;:988,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!OX6s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefb11412-b70e-45be-846a-889bf9f9c9ef_988x579.png 424w, https://substackcdn.com/image/fetch/$s_!OX6s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefb11412-b70e-45be-846a-889bf9f9c9ef_988x579.png 848w, https://substackcdn.com/image/fetch/$s_!OX6s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefb11412-b70e-45be-846a-889bf9f9c9ef_988x579.png 1272w, https://substackcdn.com/image/fetch/$s_!OX6s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fefb11412-b70e-45be-846a-889bf9f9c9ef_988x579.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Previously all of these amazing tools were expected to be developed by the open source community, for free, to make .NET a viable platform. Now Microsoft are building those tools directly into Visual Studio templates, forcing applications to standardise on ways of setting up logging, monitoring and service discovery around best practices decided by the .NET team (with probably a bit of community input). No more Bobs-homemade-logging-framework, no more Garys-custom-OpenTelemetry-sink. If an application is Aspire compatible it will be expected to meet certain standards.</p><p>And this means the team have finally brought an opinionated system back to .NET. This is a beautiful thing, but what is next for Aspire?</p><p>The menu on the left of the Aspire Dashboard could easily fit in some new tabs. How about:</p><ul><li><p>Database access, built in CRUD admin panel (maybe I should port <a href="https://github.com/edandersen/core-admin">CoreAdmin</a> to it&#8230;)</p></li><li><p>User management</p></li><li><p>API browser/test console (like Swagger)</p></li><li><p>Visitor analytics</p></li></ul><p>I&#8217;m sure a healthy ecosystem of &#8220;plugins&#8221; will be developed.</p><h2>Final thoughts</h2><p>Believe it or not I am actually excited about where Aspire is going. A genuine out of the box value add from the team, finally some standardisation around common components, the dev team having an <em>opinion</em> again, and a return to the days where projects would follow known patterns. Anything that aims to improve the state of .NET apps &#8220;in the wild&#8221; gets a thumbs up from me.</p>]]></content:encoded></item><item><title><![CDATA[Why Microsoft Orleans is important for .NET Developers]]></title><description><![CDATA[Small to medium sized development teams often grapple with the complexities of microservices architecture.]]></description><link>https://www.edandersen.com/p/why-microsoft-orleans-is-important-for-net-developers</link><guid isPermaLink="false">https://www.edandersen.com/p/why-microsoft-orleans-is-important-for-net-developers</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Thu, 20 Jun 2024 02:37:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/2p_83Umbj2I" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Small to medium sized development teams often grapple with the complexities of microservices architecture. While the distributed approach offers scalability and flexibility, it can quickly become overwhelming, especially without a large team to manage the infrastructure. Enter Microsoft Orleans, a framework designed to simplify building scalable, resilient, and distributed backends. This post delves into how Orleans can be a game-changer for teams seeking the benefits of distributed systems without the overhead of traditional microservices.</p><div class="captioned-image-container"><figure><div id="youtube2-2p_83Umbj2I" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;2p_83Umbj2I&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/2p_83Umbj2I?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div></figure></div><h2>The Drawbacks of Microservices</h2><p>Typically, a microservices infrastructure involves splitting your application into smaller, independent services, each running in its own environment and possibly using different technologies. For example, a user service might handle all user-related operations, running in its own Docker container with separate monitoring, deployment, and API documentation. As your application grows, you could end up managing a plethora of services like event, post, subscription, and billing services, each with its own complexities. This fragmentation can quickly become a management nightmare, especially for smaller teams.</p><p>Now with real interest rates, the backlash against microservices and the associated complexity is getting real.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hHHj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd516d202-921a-4dd9-983f-cd3355b563d3_1024x875.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hHHj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd516d202-921a-4dd9-983f-cd3355b563d3_1024x875.png 424w, https://substackcdn.com/image/fetch/$s_!hHHj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd516d202-921a-4dd9-983f-cd3355b563d3_1024x875.png 848w, https://substackcdn.com/image/fetch/$s_!hHHj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd516d202-921a-4dd9-983f-cd3355b563d3_1024x875.png 1272w, https://substackcdn.com/image/fetch/$s_!hHHj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd516d202-921a-4dd9-983f-cd3355b563d3_1024x875.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hHHj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd516d202-921a-4dd9-983f-cd3355b563d3_1024x875.png" width="1024" height="875" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d516d202-921a-4dd9-983f-cd3355b563d3_1024x875.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:875,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Industry titans calling microservices out&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Industry titans calling microservices out" title="Industry titans calling microservices out" srcset="https://substackcdn.com/image/fetch/$s_!hHHj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd516d202-921a-4dd9-983f-cd3355b563d3_1024x875.png 424w, https://substackcdn.com/image/fetch/$s_!hHHj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd516d202-921a-4dd9-983f-cd3355b563d3_1024x875.png 848w, https://substackcdn.com/image/fetch/$s_!hHHj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd516d202-921a-4dd9-983f-cd3355b563d3_1024x875.png 1272w, https://substackcdn.com/image/fetch/$s_!hHHj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd516d202-921a-4dd9-983f-cd3355b563d3_1024x875.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Microsoft Orleans: A Simplified Approach</h2><p>Microsoft Orleans (re)introduces the actor model that simplifies the design and deployment of distributed systems. In Orleans, entities like users are represented as grains, which are essentially instances of C# classes that can be instantiated as needed. These grains are identified by a primary key (e.g., a user ID) and can live in the system&#8217;s memory, being activated or deactivated as required.</p><p>You could call it a &#8220;distributed modular monolith&#8221;, in that there is a single deployable unit (unless you want to get fancy) but certain parts are activated on certain load balanced servers as required.</p><h3>Benefits of Using Orleans</h3><ul><li><p><strong>Reduced Boilerplate</strong>: Unlike microservices, which require extensive setup for each service (Docker files, API docs, etc.), Orleans grains share resources within the same deployment, minimizing the need for additional boilerplate.</p></li><li><p><strong>Built-in Caching</strong>: The grain storage pattern keeps frequently accessed data in memory, eliminating the need for a separate caching layer.</p></li><li><p><strong>Automatic Scalability</strong>: Grains are automatically distributed across the available servers in the cluster, ensuring efficient use of resources without manual configuration.</p></li><li><p><strong>Ease of Testing</strong>: Since grains are based on C# interfaces, they can be easily mocked for unit testing, simplifying the testing process compared to microservices.</p></li><li><p><strong>It&#8217;s F5-able!</strong>&nbsp;You can just run it locally. No docker compose. No minikube. It just works and behaves just like it will spread over 50 servers. Nice!</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!f7qG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2084c1-96f2-47a3-af07-d1ad2f2cf5d2_1160x781.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!f7qG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2084c1-96f2-47a3-af07-d1ad2f2cf5d2_1160x781.png 424w, https://substackcdn.com/image/fetch/$s_!f7qG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2084c1-96f2-47a3-af07-d1ad2f2cf5d2_1160x781.png 848w, https://substackcdn.com/image/fetch/$s_!f7qG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2084c1-96f2-47a3-af07-d1ad2f2cf5d2_1160x781.png 1272w, https://substackcdn.com/image/fetch/$s_!f7qG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2084c1-96f2-47a3-af07-d1ad2f2cf5d2_1160x781.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!f7qG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2084c1-96f2-47a3-af07-d1ad2f2cf5d2_1160x781.png" width="1160" height="781" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8f2084c1-96f2-47a3-af07-d1ad2f2cf5d2_1160x781.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:781,&quot;width&quot;:1160,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!f7qG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2084c1-96f2-47a3-af07-d1ad2f2cf5d2_1160x781.png 424w, https://substackcdn.com/image/fetch/$s_!f7qG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2084c1-96f2-47a3-af07-d1ad2f2cf5d2_1160x781.png 848w, https://substackcdn.com/image/fetch/$s_!f7qG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2084c1-96f2-47a3-af07-d1ad2f2cf5d2_1160x781.png 1272w, https://substackcdn.com/image/fetch/$s_!f7qG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f2084c1-96f2-47a3-af07-d1ad2f2cf5d2_1160x781.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Grains are distributed across silos (servers, normally) by default, but placement can be controlled</figcaption></figure></div><h2>Real-World Use Cases</h2><p>Orleans isn&#8217;t just a theoretical solution; it&#8217;s used in major Microsoft products like Xbox Live and Azure, proving its effectiveness at scale. Its adoption underscores the framework&#8217;s reliability and performance, making it an attractive option for developers looking for proven solutions.</p><p>This is the key differentiator between Orleans and other products that Microsoft pump out (see MAUI, Blazor etc) &#8211;&nbsp;<em>they actually use it themselves for large production public facing systems!</em></p><h2>A quick Orleans refactor</h2><p>Consider the scenario where a traditional MVC application accesses a database using Entity Framework for user management. We&#8217;ll see how this approach can be refactored using Microsoft Orleans.</p><h3>Traditional MVC Users Controller</h3><p>In a typical MVC application, you might have a&nbsp;<code>UsersController</code>&nbsp;that interacts directly with an Entity Framework&nbsp;<code>DbContext</code>&nbsp;to perform CRUD operations on users. This is an Index action that loads a single user from the database.</p><pre><code>public class UsersController : ApiController
{
    private readonly ApplicationDbContext _context;

    public UsersController(ApplicationDbContext context)
    {
        _context = context;
    }

    [HttpPost]
    public async Task&lt;IActionResult&gt; Create([FromForm] User user)
    {
        _context.Users.Add(user);
        await _context.SaveChangesAsync();
        return Ok();
    }

    [HttpGet]
    public async Task&lt;IActionResult&gt; Index(string id)
    {
        return Ok(await _context.Users.FindAsync(id));
    }
}
</code></pre><h3>Refactoring with Orleans</h3><p>With Orleans, you abstract user operations into a grain interface and implementation, removing direct database access from the controller and instead relying on Orleans to manage user information in Grain state.</p><h4>Defining the User Grain Interface and Implementation</h4><p>First, define an interface for the user grain operations:</p><pre><code>public interface IUserGrain : IGrainWithStringKey
{
    Task&lt;User&gt; GetUserAsync();
    Task SetUserAsync(User user);
}
</code></pre><p>Then, implement the grain:</p><pre><code>public class UserGrain : Grain, IUserGrain
{
    private readonly IPersistentState&lt;User&gt; _userState;

    public UserGrain(
        [PersistentState("user", "userStore")] IPersistentState&lt;User&gt; userState)
    {
        _userState = userState;
    }

    public Task&lt;User&gt; GetUserAsync()
    {
        return Task.FromResult(_userState.State);
    }

    public async Task SetUserAsync(User user)
    {
        _userState.State = user;
        await _userState.WriteStateAsync(); 
    }

}
</code></pre><h4>Modifying the Controller to Use the Grain</h4><p>Instead of directly accessing the database, the controller now interacts with the user grain:</p><pre><code>public class UsersController : Controller
{
    private readonly IClusterClient _client;

    public UsersController(IClusterClient client)
    {
        _client = client;
    }

    [HttpPost]
    public async Task&lt;IActionResult&gt; Create([FromForm] User user)
    {
        var userGrain = _client.GetGrain&lt;IUserGrain&gt;(id);
        userGrain.SetUserAsync(user);
        return Ok();
    }

    [HttpGet]
    public async Task&lt;IActionResult&gt; Index(string id)
    {
        var userGrain = _client.GetGrain&lt;IUserGrain&gt;(id);
        var user = await userGrain.GetUserAsync();
        return View(user);
    }
}
</code></pre><p>This refactoring moves the saving and loading out of Entity Framework and into grain state &#8211; which will be persisted to your preferred backing data store. Of course, ADO.NET / MSSQL is supported but also lots of cloudy blob storage solutions. One major benefit is that the user information here is cached &#8211; if the user does not change between calls to the grain, and it has not been deactivated, the result is served from memory. That&#8217;s your Redis cache you can send off to pastures new too.</p><h3>The team behind it</h3><p>The strength of a development framework is not just in its technology but also in the community and leadership behind it. Microsoft Orleans benefits greatly from the expertise and dedication of individuals like Reuben Bond and Brady Gaster. Reuben is not just a senior developer but a central figure in the Orleans community. His active participation in the Orleans Discord channel, where he answers questions and provides guidance, is testament to his commitment to the project. You can actually ask him questions!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JkWo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F691ed9c5-ae4a-4d8f-b47e-924312c1a4ea_1331x698.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JkWo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F691ed9c5-ae4a-4d8f-b47e-924312c1a4ea_1331x698.png 424w, https://substackcdn.com/image/fetch/$s_!JkWo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F691ed9c5-ae4a-4d8f-b47e-924312c1a4ea_1331x698.png 848w, https://substackcdn.com/image/fetch/$s_!JkWo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F691ed9c5-ae4a-4d8f-b47e-924312c1a4ea_1331x698.png 1272w, https://substackcdn.com/image/fetch/$s_!JkWo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F691ed9c5-ae4a-4d8f-b47e-924312c1a4ea_1331x698.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JkWo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F691ed9c5-ae4a-4d8f-b47e-924312c1a4ea_1331x698.png" width="1331" height="698" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/691ed9c5-ae4a-4d8f-b47e-924312c1a4ea_1331x698.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:698,&quot;width&quot;:1331,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!JkWo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F691ed9c5-ae4a-4d8f-b47e-924312c1a4ea_1331x698.png 424w, https://substackcdn.com/image/fetch/$s_!JkWo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F691ed9c5-ae4a-4d8f-b47e-924312c1a4ea_1331x698.png 848w, https://substackcdn.com/image/fetch/$s_!JkWo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F691ed9c5-ae4a-4d8f-b47e-924312c1a4ea_1331x698.png 1272w, https://substackcdn.com/image/fetch/$s_!JkWo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F691ed9c5-ae4a-4d8f-b47e-924312c1a4ea_1331x698.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The brains behind the operation</figcaption></figure></div><p>On the management side, Brady Gaster&#8217;s involvement brings a wealth of experience. Known for his significant contributions to the .NET world, Brady&#8217;s role in overseeing Orleans&#8217; development ensures that the framework not only meets current developer needs but also anticipates future trends and challenges. The collaboration between these two industry veterans, along with the broader team and community, is a significant reason why Microsoft Orleans has emerged as a powerful and reliable solution.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dHV4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfe80e8b-2cf7-4924-bcd0-79cda3c86b5f_1301x817.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dHV4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfe80e8b-2cf7-4924-bcd0-79cda3c86b5f_1301x817.png 424w, https://substackcdn.com/image/fetch/$s_!dHV4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfe80e8b-2cf7-4924-bcd0-79cda3c86b5f_1301x817.png 848w, https://substackcdn.com/image/fetch/$s_!dHV4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfe80e8b-2cf7-4924-bcd0-79cda3c86b5f_1301x817.png 1272w, https://substackcdn.com/image/fetch/$s_!dHV4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfe80e8b-2cf7-4924-bcd0-79cda3c86b5f_1301x817.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dHV4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfe80e8b-2cf7-4924-bcd0-79cda3c86b5f_1301x817.png" width="1301" height="817" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cfe80e8b-2cf7-4924-bcd0-79cda3c86b5f_1301x817.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:817,&quot;width&quot;:1301,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!dHV4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfe80e8b-2cf7-4924-bcd0-79cda3c86b5f_1301x817.png 424w, https://substackcdn.com/image/fetch/$s_!dHV4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfe80e8b-2cf7-4924-bcd0-79cda3c86b5f_1301x817.png 848w, https://substackcdn.com/image/fetch/$s_!dHV4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfe80e8b-2cf7-4924-bcd0-79cda3c86b5f_1301x817.png 1272w, https://substackcdn.com/image/fetch/$s_!dHV4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfe80e8b-2cf7-4924-bcd0-79cda3c86b5f_1301x817.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">A manager that can actually code, good lord</figcaption></figure></div><h2>In summary</h2><p>.NET dev shops wanting to build distributed applications should take a look at Orleans before going down the garden path of microservices. By abstracting the complexities of distributed systems into manageable grains, Orleans allows developers to focus on building features rather than managing infrastructure.</p><p>For those interested in diving deeper into Microsoft Orleans, take a look at the&nbsp;<a href="https://learn.microsoft.com/en-us/dotnet/orleans/?ref=tedstech.com">docs on the Microsoft website</a>, which is pleasantly well written.</p>]]></content:encoded></item><item><title><![CDATA[Daily Driving the Jelly Star 3 inch Android phone]]></title><description><![CDATA[Unihertz launched the Kickstarter for their latest device, the &#8220;Jelly Star&#8221; in June 2023 and I was one of the first in line.]]></description><link>https://www.edandersen.com/p/daily-driving-the-jelly-star-3-inch</link><guid isPermaLink="false">https://www.edandersen.com/p/daily-driving-the-jelly-star-3-inch</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Sat, 21 Oct 2023 02:32:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fe684813-1880-4b36-8c38-95aa9a81310a_1533x1193.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Unihertz launched the Kickstarter for their latest device, the &#8220;<a href="https://www.unihertz.com/products/jelly-star?ref=tedstech.com">Jelly Star</a>&#8221; in June 2023 and I was one of the first in line. I previously owned a &#8220;Jelly 2&#8221; before my son decided to drop it screen first onto concrete. The Jelly Star comes with a pre-applied screen protector so they&#8217;ve been listening.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yQRL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe25c75d5-c509-4901-8900-102ef71f086f_1533x1193.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yQRL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe25c75d5-c509-4901-8900-102ef71f086f_1533x1193.jpeg 424w, https://substackcdn.com/image/fetch/$s_!yQRL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe25c75d5-c509-4901-8900-102ef71f086f_1533x1193.jpeg 848w, https://substackcdn.com/image/fetch/$s_!yQRL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe25c75d5-c509-4901-8900-102ef71f086f_1533x1193.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!yQRL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe25c75d5-c509-4901-8900-102ef71f086f_1533x1193.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yQRL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe25c75d5-c509-4901-8900-102ef71f086f_1533x1193.jpeg" width="1533" height="1193" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e25c75d5-c509-4901-8900-102ef71f086f_1533x1193.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1193,&quot;width&quot;:1533,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Broken Jelly 2 next to Unihertz Jelly Star&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Broken Jelly 2 next to Unihertz Jelly Star" title="Broken Jelly 2 next to Unihertz Jelly Star" srcset="https://substackcdn.com/image/fetch/$s_!yQRL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe25c75d5-c509-4901-8900-102ef71f086f_1533x1193.jpeg 424w, https://substackcdn.com/image/fetch/$s_!yQRL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe25c75d5-c509-4901-8900-102ef71f086f_1533x1193.jpeg 848w, https://substackcdn.com/image/fetch/$s_!yQRL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe25c75d5-c509-4901-8900-102ef71f086f_1533x1193.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!yQRL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe25c75d5-c509-4901-8900-102ef71f086f_1533x1193.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Broken Jelly 2 on the left, new Jelly Star on the right. The Jelly Star is slightly thicker.</figcaption></figure></div><p>I won&#8217;t go into detail of the specs as&nbsp;<a href="https://www.devicespecifications.com/en/model/84665cda?ref=tedstech.com">you can find proper tech specs elsewhere</a>, but this thing is small, around the size of a credit card with its 3 inch screen. It also has the headphone jack, dual SIM card support and SD card slot of the Xperia. The 48 megapixel single camera lens is passable and I have real cameras I need to use more of anyway. It is a full upgrade from the Jelly 2, with the only downgrade being the lack of FeLiCa support so no osaifu-keitai or Suica supporting model for Japan this time around.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!p2mz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f234da7-8604-4db4-9b59-81c70bf179a4_2328x1406.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!p2mz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f234da7-8604-4db4-9b59-81c70bf179a4_2328x1406.jpeg 424w, https://substackcdn.com/image/fetch/$s_!p2mz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f234da7-8604-4db4-9b59-81c70bf179a4_2328x1406.jpeg 848w, https://substackcdn.com/image/fetch/$s_!p2mz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f234da7-8604-4db4-9b59-81c70bf179a4_2328x1406.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!p2mz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f234da7-8604-4db4-9b59-81c70bf179a4_2328x1406.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!p2mz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f234da7-8604-4db4-9b59-81c70bf179a4_2328x1406.jpeg" width="2328" height="1406" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f234da7-8604-4db4-9b59-81c70bf179a4_2328x1406.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1406,&quot;width&quot;:2328,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Unihertz Jelly Star with Headphones in&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Unihertz Jelly Star with Headphones in" title="Unihertz Jelly Star with Headphones in" srcset="https://substackcdn.com/image/fetch/$s_!p2mz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f234da7-8604-4db4-9b59-81c70bf179a4_2328x1406.jpeg 424w, https://substackcdn.com/image/fetch/$s_!p2mz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f234da7-8604-4db4-9b59-81c70bf179a4_2328x1406.jpeg 848w, https://substackcdn.com/image/fetch/$s_!p2mz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f234da7-8604-4db4-9b59-81c70bf179a4_2328x1406.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!p2mz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f234da7-8604-4db4-9b59-81c70bf179a4_2328x1406.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Absolutely impossible for Apple to do this, yeah? No space in the phone or something.</figcaption></figure></div><p>It sat in a box until last month when I found myself with screen times of four or five hours a day on my phone, a mahoosive Sony Xperia 1 V. This isn&#8217;t a &#8220;Digital Detox&#8221; but more of a &#8220;re-tooling&#8221; and I just wanted to use my phone less whilst still remaining a functional member of society.</p><h2>Re-evaluating what I need my &#8220;daily driver&#8221; phone for</h2><p>When putting apps back on the phone, I wanted to carefully think about what I actually need this thing in my pocket to be able to do. The purpose was to change my phone use from this constant mental tether to the information super highway into just a tool.</p><h3>Tools required to function in society</h3><ul><li><p>WhatsApp, Signal, LINE &#8211; IM</p></li><li><p>Primary daily use bank account app to check balance and move money</p></li><li><p>Authenticator for 2FA</p></li><li><p>Personal and work email</p></li><li><p>Google Maps</p></li><li><p>Fitbit for tracking and for topping up Suica on Fitbit Charge 5</p></li><li><p>Companion apps for my Sony cameras to pass GPS info for geotagging</p></li><li><p>DiDi/Uber for transport</p></li><li><p>PayPay for shops that have been &#8220;coerced&#8221; into to only accepting it</p></li><li><p>YouTube Music for gym bangers</p></li></ul><h3>What has to go nowhere near this thing</h3><ul><li><p>Games</p></li><li><p>Web Browser</p></li><li><p>Social Media</p></li><li><p>Slack</p></li><li><p>Discord</p></li><li><p>Non-primary banking apps (can stay on the old phone in a drawer)</p></li></ul><p>In the end I had less than 20 apps on the Jelly Star, compared to way over 100 on the Sony.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gDWX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a7fdf01-027a-4f1d-af03-01abc8bcb40b_2424x1414.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gDWX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a7fdf01-027a-4f1d-af03-01abc8bcb40b_2424x1414.png 424w, https://substackcdn.com/image/fetch/$s_!gDWX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a7fdf01-027a-4f1d-af03-01abc8bcb40b_2424x1414.png 848w, https://substackcdn.com/image/fetch/$s_!gDWX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a7fdf01-027a-4f1d-af03-01abc8bcb40b_2424x1414.png 1272w, https://substackcdn.com/image/fetch/$s_!gDWX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a7fdf01-027a-4f1d-af03-01abc8bcb40b_2424x1414.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gDWX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a7fdf01-027a-4f1d-af03-01abc8bcb40b_2424x1414.png" width="2424" height="1414" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2a7fdf01-027a-4f1d-af03-01abc8bcb40b_2424x1414.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1414,&quot;width&quot;:2424,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Sony Xperia 1 V next to Jelly Star&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Sony Xperia 1 V next to Jelly Star" title="Sony Xperia 1 V next to Jelly Star" srcset="https://substackcdn.com/image/fetch/$s_!gDWX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a7fdf01-027a-4f1d-af03-01abc8bcb40b_2424x1414.png 424w, https://substackcdn.com/image/fetch/$s_!gDWX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a7fdf01-027a-4f1d-af03-01abc8bcb40b_2424x1414.png 848w, https://substackcdn.com/image/fetch/$s_!gDWX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a7fdf01-027a-4f1d-af03-01abc8bcb40b_2424x1414.png 1272w, https://substackcdn.com/image/fetch/$s_!gDWX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a7fdf01-027a-4f1d-af03-01abc8bcb40b_2424x1414.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Collecting apps like Pokemon on the Sony Xperia. Turns out you can live with less than 20.</figcaption></figure></div><p>The Web Browser is the hardest app to live without, but it also was the primary cause of phone overuse. You can be out somewhere and modern society expects you to be able to any of the following activities at a moment&#8217;s notice:</p><ul><li><p>Scan a QR code to order food from a menu in a browser</p></li><li><p>Sign up to something for a discount</p></li><li><p>Reset a password</p></li><li><p>Book an appointment</p></li></ul><p>I wasn&#8217;t going to compromise on being able to function, so the solution I found was to keep Chrome on the device, don&#8217;t sign in, don&#8217;t install any adblockers (which would make the web tolerable) and keep the browser Disabled. Some Android apps will just randomly crash with no available browser (for in-app sign up or some form flows) so I do have to go and toggle Chrome back to being active occasionally. If I leave Chrome enabled in this state, even the BBC News website is full of scammy adverts and is basically unusable.</p><p>&#8220;But why not just delete all this stuff from your big phone?&#8221; I hear you say. The point of having a tiny annoying phone to use is that even if I was to give into a moment of weakness and install a game or SNS, the experience is so awful on this device that I don&#8217;t want to use it. Most apps optimised for engagement now expect you to have a large phablet sized 6 inch beautiful OLED screen.</p><h2>The benefits after 4 weeks</h2><p>It took a while but I feel more present. I can actually go out for lunch with people and talk to them without being distracted by a ridiculously large pocket computer in my pocket. I don&#8217;t feel compelled to suddenly google information to settle arguments.</p><p>The battery life has been fantastic since I barely use the thing. It only has a 2000mAh battery but the aggressive app freezing (which needs to be disabled in some cases) and reduced usage makes the phone last just as long as my Sony Xperia 1 V with a 5000mAh battery.</p><p>Instant messages from WhatsApp, Signal etc do not always have to be responded to straight away, and the tiny keyboard makes this annoying anyway. You can just respond on the desktop apps when you get back to your computer and getting into this habit feels quite healthy.</p><h2>How to stay connected</h2><p>There are still some cases where it is perfectly acceptable to use a device for extended periods of time without upsetting the people you are with. Such as</p><ul><li><p>Your train commute assuming you are sitting down</p></li><li><p>a 2 hour Shinkansen journey</p></li><li><p>Reading before bed</p></li><li><p>Alone in a coffee shop</p></li></ul><p>It turns out I had the perfect device for this &#8211; an iPad Mini with 5G. This is big enough that you cannot get it out in public for a bit of news perusal unless its really appropriate and you are by yourself. Whipping the iPad out at dinner with your family would be a quick way of getting a divorce!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vr-R!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e92e892-e8ec-4b7b-90bb-2de379239d3b_2457x1412.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vr-R!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e92e892-e8ec-4b7b-90bb-2de379239d3b_2457x1412.jpeg 424w, https://substackcdn.com/image/fetch/$s_!vr-R!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e92e892-e8ec-4b7b-90bb-2de379239d3b_2457x1412.jpeg 848w, https://substackcdn.com/image/fetch/$s_!vr-R!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e92e892-e8ec-4b7b-90bb-2de379239d3b_2457x1412.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!vr-R!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e92e892-e8ec-4b7b-90bb-2de379239d3b_2457x1412.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vr-R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e92e892-e8ec-4b7b-90bb-2de379239d3b_2457x1412.jpeg" width="2457" height="1412" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2e92e892-e8ec-4b7b-90bb-2de379239d3b_2457x1412.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1412,&quot;width&quot;:2457,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;iPad Mini 6th Generation next to Unihertz Jelly Star&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="iPad Mini 6th Generation next to Unihertz Jelly Star" title="iPad Mini 6th Generation next to Unihertz Jelly Star" srcset="https://substackcdn.com/image/fetch/$s_!vr-R!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e92e892-e8ec-4b7b-90bb-2de379239d3b_2457x1412.jpeg 424w, https://substackcdn.com/image/fetch/$s_!vr-R!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e92e892-e8ec-4b7b-90bb-2de379239d3b_2457x1412.jpeg 848w, https://substackcdn.com/image/fetch/$s_!vr-R!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e92e892-e8ec-4b7b-90bb-2de379239d3b_2457x1412.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!vr-R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e92e892-e8ec-4b7b-90bb-2de379239d3b_2457x1412.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">If Apple still made iPhone Mini&#8217;s I wouldn&#8217;t have to do this.</figcaption></figure></div><p>The 48MP camera on the Jelly Star is acceptable for a Chinese 30,000 yen phone, and is fine for taking pictures of signs and expense receipts. You can get away with the odd family photo but you&#8217;ll need a proper camera. The smallest real camera I have that I can sort of carry in a pocket is the Sony ZV-E10 with its slightly rubbish kit lens. I&#8217;m looking at getting a smaller camera that is truly pocketable to go alongside this phone. If anyone happens to be reading this and has any suggestions for a good pocketable &#8220;Everyday Carry&#8221; camera please let me know in the comments &#128578;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JI6k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60efd4da-ed12-4bb5-bab6-c65ca7ba07e5_2285x1270.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JI6k!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60efd4da-ed12-4bb5-bab6-c65ca7ba07e5_2285x1270.jpeg 424w, https://substackcdn.com/image/fetch/$s_!JI6k!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60efd4da-ed12-4bb5-bab6-c65ca7ba07e5_2285x1270.jpeg 848w, https://substackcdn.com/image/fetch/$s_!JI6k!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60efd4da-ed12-4bb5-bab6-c65ca7ba07e5_2285x1270.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!JI6k!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60efd4da-ed12-4bb5-bab6-c65ca7ba07e5_2285x1270.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JI6k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60efd4da-ed12-4bb5-bab6-c65ca7ba07e5_2285x1270.jpeg" width="2285" height="1270" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/60efd4da-ed12-4bb5-bab6-c65ca7ba07e5_2285x1270.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1270,&quot;width&quot;:2285,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Sony ZV-E10 next to Unihertz Jelly Star&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Sony ZV-E10 next to Unihertz Jelly Star" title="Sony ZV-E10 next to Unihertz Jelly Star" srcset="https://substackcdn.com/image/fetch/$s_!JI6k!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60efd4da-ed12-4bb5-bab6-c65ca7ba07e5_2285x1270.jpeg 424w, https://substackcdn.com/image/fetch/$s_!JI6k!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60efd4da-ed12-4bb5-bab6-c65ca7ba07e5_2285x1270.jpeg 848w, https://substackcdn.com/image/fetch/$s_!JI6k!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60efd4da-ed12-4bb5-bab6-c65ca7ba07e5_2285x1270.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!JI6k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60efd4da-ed12-4bb5-bab6-c65ca7ba07e5_2285x1270.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Sony ZV-E10 next to the Unihertz Jelly Star. Not quite an &#8220;Everyday carry&#8221; camera but I&#8217;m looking for something else.</figcaption></figure></div><p>So, the experiment continues. Having a smart phone is not really optional today sadly unless you want to struggle and do extra planning before leaving the house compared to other people, but the negative affects appear to be able to be tamed without having to go completely off grid with a &#8220;dumb phone&#8221; like the Nokia Flip. I would recommend giving it a go if you want to use your phone as a tool and not have your phone use you.</p><p>I&#8217;ll keep going with this until I crack and go back to the big Sony phone. Until then I&#8217;m happy to answer any questions you have about the phone in the comments below. Thanks for reading!</p>]]></content:encoded></item><item><title><![CDATA[C# 11 and .NET 7 – early look at new features]]></title><description><![CDATA[.NET 7 is coming soon alongside C# 11.]]></description><link>https://www.edandersen.com/p/c-11-and-net-7-early-look-at-new-features</link><guid isPermaLink="false">https://www.edandersen.com/p/c-11-and-net-7-early-look-at-new-features</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Thu, 10 Mar 2022 14:28:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/ljwz-YZZZ7g" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>.NET 7 is coming soon alongside C# 11. It&#8217;s possible to take a look at some of the new features now, including the bang bang (!!) operator and List patterns, which I think are really cool.</p><p>Here is a video on the topic:</p><div id="youtube2-ljwz-YZZZ7g" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;ljwz-YZZZ7g&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/ljwz-YZZZ7g?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Converting .NET 6 Minimal API back to Startup.cs]]></title><description><![CDATA[I&#8217;ve had to convert a Minimal API app back to Startup.cs a few times now.]]></description><link>https://www.edandersen.com/p/converting-net-6-minimal-api-back-to-startup-cs</link><guid isPermaLink="false">https://www.edandersen.com/p/converting-net-6-minimal-api-back-to-startup-cs</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Tue, 11 Jan 2022 09:44:30 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/tcWT7g_BQdM" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I&#8217;ve had to convert a Minimal API app back to Startup.cs a few times now. Here are some reasons why you might want to do it:</p><h3>Incompatible NuGet packages</h3><p>For example as of time of writing <a href="https://www.nuget.org/packages/Amazon.Lambda.AspNetCoreServer/">Amazon.Lambda.AspNetCoreServer</a> doesn&#8217;t work with Minimal APIs. If you didn&#8217;t know that&nbsp;<a href="https://www.nuget.org/packages/Amazon.Lambda.AspNetCoreServer.Hosting/">Amazon.Lambda.AspNetCoreServer.Hosting</a> was what you needed for Minimal APIs, you&#8217;d be stuck without converting to Startup.cs.</p><h3>Needing to return to .NET 5</h3><p>Again, as of time of writing, AWS Elastic Beanstalk does not support .NET 6 on runtime based deployments. So if you have built half your app in Minimal API style you&#8217;ll need to change it to Startup.cs to use .NET 5.</p><h3>It is different to everything else in your org</h3><p>Minimal APIs introduce new training overhead for teams used to Startup.cs. Remember, some people are only just getting their heads around Startup.cs which was a big (and brilliant) change from Global.asax.</p><h3>It doesn&#8217;t match documentation</h3><p>Most documentation out there still has instructions for Startup.cs. Figuring out how to apply the instructions in a Minimal API Program.cs can be a headache.</p><p>If you want to convert to a Startup class, here are the empty Program.cs and Startup.cs classes that will be useful</p><p>Program.cs:</p><pre><code>using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();
        host.Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =&gt;
    WebHost.CreateDefaultBuilder(args).UseStartup&lt;Startup&gt;();
}
</code></pre><p>and Startup.cs:</p><pre><code>using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {

    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {

    }
}
</code></pre><p>Here is a video walking through how to do it:</p><div id="youtube2-tcWT7g_BQdM" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;tcWT7g_BQdM&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/tcWT7g_BQdM?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[New C# 9.0 features – Record Types, Init Only Setters, Pattern Matching]]></title><description><![CDATA[I&#8217;ve recently added a quick series of videos on new C# 9 features available in .NET 5.]]></description><link>https://www.edandersen.com/p/new-c-9-0-features-record-types-init-only-setters-pattern-matching</link><guid isPermaLink="false">https://www.edandersen.com/p/new-c-9-0-features-record-types-init-only-setters-pattern-matching</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Fri, 26 Feb 2021 15:07:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/XaYwiGBn3p8" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I&#8217;ve recently added a quick series of videos on new C# 9 features available in .NET 5. You can find them below.</p><div id="youtube2-XaYwiGBn3p8" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;XaYwiGBn3p8&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/XaYwiGBn3p8?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div id="youtube2-syto6WcdsKw" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;syto6WcdsKw&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/syto6WcdsKw?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div id="youtube2-YGF8ay4W-So" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;YGF8ay4W-So&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/YGF8ay4W-So?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Looking forward to using these!</p>]]></content:encoded></item><item><title><![CDATA[Adding Razor cshtml view runtime re-compilation to a ASP.NET Core 5.0 app after creating it]]></title><description><![CDATA[I recently came across an interesting issue where after starting a new ASP.NET Core 5.0 .NET 5 project using the &#8220;ASP.NET Core Web App (Model-View-Controller)&#8221; template did not include the ability to update .cshtml Razor files without recompiling and restarting the whole app.]]></description><link>https://www.edandersen.com/p/adding-razor-cshtml-view-runtime-re-compilation-to-new-asp-net-core-5-0-project-templates</link><guid isPermaLink="false">https://www.edandersen.com/p/adding-razor-cshtml-view-runtime-re-compilation-to-new-asp-net-core-5-0-project-templates</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Thu, 18 Feb 2021 15:29:19 GMT</pubDate><enclosure url="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/179af674-a4ea-489e-8ce7-b5ef9fad652f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I recently came across an interesting issue where after starting a new ASP.NET Core 5.0 .NET 5 project using the &#8220;ASP.NET Core Web App (Model-View-Controller)&#8221; template did not include the ability to update .cshtml Razor files without recompiling and restarting the whole app. There is a checkbox to &#8220;Enable Razor Runtime Compilation&#8221; during project setup but it&#8217;s easy to miss and tricky to add afterwards if you don&#8217;t know what you are looking for.</p><p>First, you need to head to the Nuget Package explorer by right clicking on the Dependencies node of your project tree selecting &#8220;Manage NuGet packages&#8230;&#8221;. Search for and install <strong>Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation</strong>. Install it and it&#8217;s dependencies into your project.</p><a class="image-link image2" target="_blank" href="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/179af674-a4ea-489e-8ce7-b5ef9fad652f.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/179af674-a4ea-489e-8ce7-b5ef9fad652f.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/179af674-a4ea-489e-8ce7-b5ef9fad652f.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/179af674-a4ea-489e-8ce7-b5ef9fad652f.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/179af674-a4ea-489e-8ce7-b5ef9fad652f.png 1456w" sizes="100vw"><img src="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/179af674-a4ea-489e-8ce7-b5ef9fad652f.png" data-attrs="{&quot;src&quot;:&quot;//old-site-assets.edandersen.com/wp-content/uploads/2021/02/179af674-a4ea-489e-8ce7-b5ef9fad652f.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/179af674-a4ea-489e-8ce7-b5ef9fad652f.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/179af674-a4ea-489e-8ce7-b5ef9fad652f.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/179af674-a4ea-489e-8ce7-b5ef9fad652f.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/179af674-a4ea-489e-8ce7-b5ef9fad652f.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p>Then head to Startup.cs. Find the line within ConfigureServices() that says &#8220;services.AddControllersWithViews()&#8221; and add &#8220;.AddRazorRuntimeCompilation()&#8221;. You won&#8217;t be able to add this line without first installing the previous NuGet package.</p><a class="image-link image2" target="_blank" href="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/f8a8691f-e37a-450f-aac7-61aed85741c2.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/f8a8691f-e37a-450f-aac7-61aed85741c2.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/f8a8691f-e37a-450f-aac7-61aed85741c2.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/f8a8691f-e37a-450f-aac7-61aed85741c2.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/f8a8691f-e37a-450f-aac7-61aed85741c2.png 1456w" sizes="100vw"><img src="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/f8a8691f-e37a-450f-aac7-61aed85741c2.png" data-attrs="{&quot;src&quot;:&quot;//old-site-assets.edandersen.com/wp-content/uploads/2021/02/f8a8691f-e37a-450f-aac7-61aed85741c2.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/f8a8691f-e37a-450f-aac7-61aed85741c2.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/f8a8691f-e37a-450f-aac7-61aed85741c2.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/f8a8691f-e37a-450f-aac7-61aed85741c2.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/f8a8691f-e37a-450f-aac7-61aed85741c2.png 1456w" sizes="100vw"></picture><div></div></div></a><p>Now run your app. You will see that changing the contents of Razor .cshtml files and refreshing the page will cause the view to be updated with your new content, without having to stop and recompile.</p><a class="image-link image2" target="_blank" href="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/87fee0b4-0f0e-452e-bbf6-96677391299a.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/87fee0b4-0f0e-452e-bbf6-96677391299a.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/87fee0b4-0f0e-452e-bbf6-96677391299a.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/87fee0b4-0f0e-452e-bbf6-96677391299a.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/87fee0b4-0f0e-452e-bbf6-96677391299a.png 1456w" sizes="100vw"><img src="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/87fee0b4-0f0e-452e-bbf6-96677391299a.png" data-attrs="{&quot;src&quot;:&quot;//old-site-assets.edandersen.com/wp-content/uploads/2021/02/87fee0b4-0f0e-452e-bbf6-96677391299a.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/87fee0b4-0f0e-452e-bbf6-96677391299a.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/87fee0b4-0f0e-452e-bbf6-96677391299a.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/87fee0b4-0f0e-452e-bbf6-96677391299a.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/87fee0b4-0f0e-452e-bbf6-96677391299a.png 1456w" sizes="100vw"></picture><div></div></div></a><h3>YouTube demonstration video</h3><div id="youtube2-NYAd42yIp2M" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;NYAd42yIp2M&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/NYAd42yIp2M?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Handling and intercepting Back button Navigation in Xamarin Forms Shell]]></title><description><![CDATA[I&#8217;ve recently ended up needing to ask if the user really wants to navigate away from a page in my Xamarin app, Net Writer.]]></description><link>https://www.edandersen.com/p/handling-and-intercepting-back-button-navigation-in-xamarin-forms-shell</link><guid isPermaLink="false">https://www.edandersen.com/p/handling-and-intercepting-back-button-navigation-in-xamarin-forms-shell</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Sun, 07 Feb 2021 14:55:14 GMT</pubDate><enclosure url="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/4a2bf9b3-bc7d-41c1-aaeb-4dd7c90a9cad.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I&#8217;ve recently ended up needing to ask if the user really wants to navigate away from a page in my Xamarin app, Net Writer. Essentially whilst a post is being edited I don&#8217;t want the user to accidentally lose their progress, necessitating the need to inject a &#8220;Are you sure?&#8221; or &#8220;Confirm exit&#8221; prompt when the user presses either the Android hardware or OS level back button or the back button on the navigation bar provided by the Xamarin Forms Shell.</p><p>I found a <a href="https://benetskyybogdan.medium.com/handle-back-button-in-xamarin-forms-for-android-7e1557ce6e59">blog post</a> by Bohdan Benetskyi from March 2020, but it was geared for MvvmCross and not Xamarin Forms Shell. Here is how to adapt it.</p><a class="image-link image2" target="_blank" href="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/4a2bf9b3-bc7d-41c1-aaeb-4dd7c90a9cad.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/4a2bf9b3-bc7d-41c1-aaeb-4dd7c90a9cad.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/4a2bf9b3-bc7d-41c1-aaeb-4dd7c90a9cad.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/4a2bf9b3-bc7d-41c1-aaeb-4dd7c90a9cad.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/4a2bf9b3-bc7d-41c1-aaeb-4dd7c90a9cad.png 1456w" sizes="100vw"><img src="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/4a2bf9b3-bc7d-41c1-aaeb-4dd7c90a9cad.png" data-attrs="{&quot;src&quot;:&quot;//old-site-assets.edandersen.com/wp-content/uploads/2021/02/4a2bf9b3-bc7d-41c1-aaeb-4dd7c90a9cad.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2021/02/4a2bf9b3-bc7d-41c1-aaeb-4dd7c90a9cad.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/4a2bf9b3-bc7d-41c1-aaeb-4dd7c90a9cad.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/4a2bf9b3-bc7d-41c1-aaeb-4dd7c90a9cad.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2021/02/4a2bf9b3-bc7d-41c1-aaeb-4dd7c90a9cad.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><h3>Introducing IBackButtonHandler to your Page</h3><p>If you have existing pages you can add an interface to them and implement it, for example here is a page from my app with IBackButtonHandler added to the class:</p><pre><code>  [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class PostListSummaryPage : ReactiveContentPage&lt;PostSummaryListPageViewModel&gt;, IBackButtonHandler
    {
</code></pre><p>This can then be implemented however you like, returning true if the back navigation should be aborted, for example:</p><pre><code>public async Task&lt;bool&gt; HandleBackButton()
{
    if (await ShouldNotGoBack())
    {
        return true;
    }

    return false;
}

public async Task&lt;bool&gt; ShouldNotGoBack()
{
    var response = await this.DisplayActionSheet("Would you like to discard any unsaved changes to this post?", "No", "Yes, go back");

    return response == "No";
}

</code></pre><p>Once that is done next up is wiring up the back buttons.</p><h3>Handling the Android OS back button</h3><p>To handle the OS level back button you&#8217;ll need to add the following to MainActivity.cs:</p><pre><code>public async override void OnBackPressed()
{
    var backButtonHandler = Shell.Current.CurrentPage as IBackButtonHandler;

    if (backButtonHandler == null)
    {
        base.OnBackPressed();
        return;
    }

    var backButtonHandled = await backButtonHandler.HandleBackButton();
    if (!backButtonHandled)
    {
        base.OnBackPressed();
    }
}
</code></pre><p>Lets step through this. First up, it attempts to cast the current Shell page as IBackButtonHandler. This means that only Pages that implement this interface will have any change of behaviour here. If the interface is not implemented, the base implementation of OnBackPressed() is called. If it is implemented, the HandleBackButton method is evaluated. If it returns true, the back navigation is cancelled. If false, the base implementation of OnBackPressed() is called, continuing the back navigation.</p><h3>Handling the Shell back button</h3><p>This is the button on the top left that shows when you are more than one level deep in the navigation stack. Handling this is less clean as it requires adding a call to Shell.SetBackButtonBehaviour in the constructor of each page that requires this. For example, add the following to the constructor of your page, after InitializeComponent():</p><pre><code>Shell.SetBackButtonBehavior(this, new BackButtonBehavior()
{
    Command = new Command(async () =&gt; {

        var backButtonHandled = await this.HandleBackButton();
        if (!backButtonHandled)
        {
            await Navigation.PopAsync();
        }

    })
});
</code></pre><p>After that, you&#8217;l get a nice prompt pop up whenever you hit the OS back button or the shell navigation back button, but only on pages that implement IBackButtonHandler.</p><h3>Walkthough video</h3><p>If you would like a video walkthough of the above, check out this video:</p><div id="youtube2-l5Ti9pQJs1E" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;l5Ti9pQJs1E&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/l5Ti9pQJs1E?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Improve Remote Desktop frame rate to 60fps by enabling AVC 4:4:4 encoding]]></title><description><![CDATA[I am a great fan of Remote Desktop and have been using it for over a decade.]]></description><link>https://www.edandersen.com/p/improve-remote-desktop-frame-rate-to-60fps-by-enabling-avc-444-encoding</link><guid isPermaLink="false">https://www.edandersen.com/p/improve-remote-desktop-frame-rate-to-60fps-by-enabling-avc-444-encoding</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Tue, 15 Dec 2020 10:53:08 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/9c53ae6e-c97d-4ffb-81bc-32a496661cfd_800x343.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I am a great fan of Remote Desktop and have been using it for over a decade. It&#8217;s built in and just works. One gripe of mine has always been the poor framerate which makes animations and transitions super janky by default.</p><p>In RDP 10 it turns out this can be massively improved by enabling a screen encoding based on&nbsp;AVC/H.264 video. By enabling the group policy &#8220;Prioritize H.264/AVC 444 graphics mode for Remote Desktop Connections&#8221; under&nbsp;Computer Configuration &gt; Administrative Templates &gt; Windows Components &gt; Remote Desktop Services &gt; Remote Desktop Session Host &gt; Remote Session Environment, I was able to get a glorious 60fps, almost double what I was getting before.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7X01!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cea0031-9bbf-4870-876b-855f54dab888_800x343.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7X01!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cea0031-9bbf-4870-876b-855f54dab888_800x343.png 424w, https://substackcdn.com/image/fetch/$s_!7X01!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cea0031-9bbf-4870-876b-855f54dab888_800x343.png 848w, https://substackcdn.com/image/fetch/$s_!7X01!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cea0031-9bbf-4870-876b-855f54dab888_800x343.png 1272w, https://substackcdn.com/image/fetch/$s_!7X01!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cea0031-9bbf-4870-876b-855f54dab888_800x343.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7X01!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cea0031-9bbf-4870-876b-855f54dab888_800x343.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8cea0031-9bbf-4870-876b-855f54dab888_800x343.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7X01!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cea0031-9bbf-4870-876b-855f54dab888_800x343.png 424w, https://substackcdn.com/image/fetch/$s_!7X01!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cea0031-9bbf-4870-876b-855f54dab888_800x343.png 848w, https://substackcdn.com/image/fetch/$s_!7X01!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cea0031-9bbf-4870-876b-855f54dab888_800x343.png 1272w, https://substackcdn.com/image/fetch/$s_!7X01!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cea0031-9bbf-4870-876b-855f54dab888_800x343.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p>There is also another group policy to use your GPU to do the encoding, &#8220;Configure H.264/AVC hardware encoding for Remote Desktop connections&#8221;. Turn this on and you can see your GPU in task manager doing a little bit of work to encode the video, potentially saving the CPU from some effort. However in my testing I actually saw slightly worse performance in this mode, as you can see from this video:</p><div id="youtube2-QgzYk53jINs" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;QgzYk53jINs&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/QgzYk53jINs?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>I do wonder why this option is buried in Group Policy and not more user accessible &#8211; it appears to be enabled by default in cloud hosted environments with virtual GPUs (such as the Azure GPU instances). I also wonder if you can actually game using it?</p>]]></content:encoded></item><item><title><![CDATA[Using Google APIs and Auth in Xamarin Forms]]></title><description><![CDATA[I&#8217;m working on porting Net Writer from UWP to Android using Xamarin Forms.]]></description><link>https://www.edandersen.com/p/using-google-apis-and-auth-in-xamarin-forms</link><guid isPermaLink="false">https://www.edandersen.com/p/using-google-apis-and-auth-in-xamarin-forms</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Thu, 02 Jul 2020 16:39:50 GMT</pubDate><enclosure url="//old-site-assets.edandersen.com/wp-content/uploads/2020/07/428db403-457b-457b-8b8f-1ec6564a670b.png#0.3955815818394577" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I&#8217;m working on porting Net Writer from UWP to Android using Xamarin Forms. The Google authentication is a little bit tricky as it is constantly changing. Working off <a href="//timothelariviere.com/2017/09/01/authenticate-users-through-google-with-xamarin-auth/">this amazing blog post by Timoth&#233; Larivi&#232;re</a> got me 90% of the way there but there are some updates to the process in 2020.</p><h2>Pre-requisites to register an app with Google</h2><p>At this point in time you&#8217;ll need to do the following before you can register a Public app:</p><ul><li><p>&nbsp;Create a project in GCP via the <a href="https://console.developers.google.com/">Google Developer Console</a></p></li><li><p>&nbsp;Verify a domain via the Google Search Console. To do this you will need access to your nameserver and DNS records in order to copy and paste a TXT record. <a href="//search.google.com/search-console/welcome?hl=en&amp;utm_source=wmx&amp;utm_medium=deprecation-pane&amp;utm_content=home">Access this here</a>.</p></li><li><p>&nbsp;Know the SHA-1 fingerprint of the key that will be used to sign your package</p></li></ul><h2>Getting the SHA-1 fingerprint used to sign a locally deployed debug Xamarin Forms app</h2><p>You&#8217;ll need to do this:</p><ul><li><p>&nbsp;In Visual Studio, go to Tools &gt; Android &gt; Android Adb Command Prompt</p></li><li><p>&nbsp;Navigate to C:\Users\{username}\AppData\Local\Xamarin\Mono for Android</p></li></ul><p>Run the command</p><pre><code>keytool -keystore debug.keystore -list -v
</code></pre><p>And when prompted enter the keystore password &#8220;android&#8221;.</p><p>You&#8217;ll see a result like this:</p><a class="image-link image2" target="_blank" href="//old-site-assets.edandersen.com/wp-content/uploads/2020/07/428db403-457b-457b-8b8f-1ec6564a670b.png#0.3955815818394577" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2020/07/428db403-457b-457b-8b8f-1ec6564a670b.png#0.3955815818394577 424w, //old-site-assets.edandersen.com/wp-content/uploads/2020/07/428db403-457b-457b-8b8f-1ec6564a670b.png#0.3955815818394577 848w, //old-site-assets.edandersen.com/wp-content/uploads/2020/07/428db403-457b-457b-8b8f-1ec6564a670b.png#0.3955815818394577 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2020/07/428db403-457b-457b-8b8f-1ec6564a670b.png#0.3955815818394577 1456w" sizes="100vw"><img src="//old-site-assets.edandersen.com/wp-content/uploads/2020/07/428db403-457b-457b-8b8f-1ec6564a670b.png#0.3955815818394577" data-attrs="{&quot;src&quot;:&quot;//old-site-assets.edandersen.com/wp-content/uploads/2020/07/428db403-457b-457b-8b8f-1ec6564a670b.png#0.3955815818394577&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2020/07/428db403-457b-457b-8b8f-1ec6564a670b.png#0.3955815818394577 424w, //old-site-assets.edandersen.com/wp-content/uploads/2020/07/428db403-457b-457b-8b8f-1ec6564a670b.png#0.3955815818394577 848w, //old-site-assets.edandersen.com/wp-content/uploads/2020/07/428db403-457b-457b-8b8f-1ec6564a670b.png#0.3955815818394577 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2020/07/428db403-457b-457b-8b8f-1ec6564a670b.png#0.3955815818394577 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>The SHA1 value is what you need.</p><h2>Registering the application</h2><p>You should have everything you need now. Go to Credentials in the GCP panel and create the OAuth consent screen. Fill in the details (you&#8217;ll need the verified domain you created earlier).</p><p>Then you can create an OAuth 2.0 Client ID. Select &#8220;Android&#8221; as the platform and enter the package name from AndroidManifest.xml and the SHA-1 fingerprint you figured out earlier. You&#8217;ll see something like the below:</p><a class="image-link image2" target="_blank" href="//old-site-assets.edandersen.com/wp-content/uploads/2020/07/df6c9026-f3d1-494c-b0ac-84e9ea3cc0ec.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2020/07/df6c9026-f3d1-494c-b0ac-84e9ea3cc0ec.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2020/07/df6c9026-f3d1-494c-b0ac-84e9ea3cc0ec.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2020/07/df6c9026-f3d1-494c-b0ac-84e9ea3cc0ec.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2020/07/df6c9026-f3d1-494c-b0ac-84e9ea3cc0ec.png 1456w" sizes="100vw"><img src="//old-site-assets.edandersen.com/wp-content/uploads/2020/07/df6c9026-f3d1-494c-b0ac-84e9ea3cc0ec.png" data-attrs="{&quot;src&quot;:&quot;//old-site-assets.edandersen.com/wp-content/uploads/2020/07/df6c9026-f3d1-494c-b0ac-84e9ea3cc0ec.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2020/07/df6c9026-f3d1-494c-b0ac-84e9ea3cc0ec.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2020/07/df6c9026-f3d1-494c-b0ac-84e9ea3cc0ec.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2020/07/df6c9026-f3d1-494c-b0ac-84e9ea3cc0ec.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2020/07/df6c9026-f3d1-494c-b0ac-84e9ea3cc0ec.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><h2>Adding the code</h2><p>The approach I have taken is to create a class in the main Android project to encapsulate everything, rather than putting the logic inside the Mobile/PCL project. This is because the Android version needs references to Activities and other Android-specific concepts to work effectively. It&#8217;s not just a case of adding Xamarin.Auth and calling a method unfortunately.</p><p>Using <a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/enterprise-application-patterns/dependency-injection">Xamarin Forms dependency injection</a> I can refer to and call this class within the portable Mobile project when I need an access token from the API.</p><h3>The token reader class</h3><p>There are some nuget dependencies you&#8217;ll need for this &#8211; the &#8220;Google.Apis.Auth&#8221; libraries for the TokenResponse class (although you can probably remove the dependency from the below code if you&#8217;d like), &#8220;Xamarin.Auth&#8221;, &#8220;Xamarin.Auth.XamarinForms&#8221; and &#8220;Plugin.CurrentActivity&#8221;. The last one allows code outside of an Activity to get access to the current Activity.</p><pre><code>    public class GoogleAccessTokenReader : IGoogleAccessTokenReader
    {
        public static readonly string[] GoogleAPIScopes =
        {
            DriveService.Scope.DriveFile,
            BloggerService.Scope.Blogger
        };

        public static TokenResponse Token { get; set; }

        public static OAuth2Authenticator Auth;

        public async Task&lt;TokenResponse&gt; GetOrNullAsync()
        {
            if (Auth == null)
            {
                Auth = new OAuth2Authenticator(
                "your-client-id",
                string.Empty,
                String.Join(" ", GoogleAPIScopes),
                new Uri("//accounts.google.com/o/oauth2/v2/auth"),
                new Uri("com.yourpackageid:/oauth2redirect"),
                new Uri("//www.googleapis.com/oauth2/v4/token"),
                isUsingNativeUI: true);

                Auth.Completed += OnAuthenticationCompleted;
            }

            if (Token != null) return Token;


            Xamarin.Auth.CustomTabsConfiguration.CustomTabsClosingMessage = null;

            var intent = Auth.GetUI(CrossCurrentActivity.Current.AppContext);

            CrossCurrentActivity.Current.Activity.StartActivity(intent);

            while(!Auth.HasCompleted)
            {
                await Task.Delay(500);
            }

            return Token;
        }

        private void OnAuthenticationCompleted(object sender, AuthenticatorCompletedEventArgs e)
        {
            if (e.IsAuthenticated)
            {
                Token = new TokenResponse()
                {
                    AccessToken = e.Account.Properties["access_token"],
                    TokenType = e.Account.Properties["token_type"],
                    Scope = e.Account.Properties["scope"],
                    ExpiresInSeconds = int.Parse(e.Account.Properties["expires_in"]),
                    RefreshToken = e.Account.Properties["refresh_token"]
                };

             }
         }

    }


</code></pre><p>Add the following dependency injection declaration to the namespace:</p><pre><code>[assembly: Dependency(typeof(NetWriter.Mobile.Droid.GoogleAccessTokenReader))]
</code></pre><p>Where NetWriter.Mobile.Droid should be replaced with your namespace.</p><p>You can use this interface too as IGoogleAccessTokenReader (or change depending on your needs):</p><pre><code>using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Responses;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace BlogWriter.Shared.NetStandard.Interfaces
{
    public interface IGoogleAccessTokenReader
    {
        Task&lt;TokenResponse&gt; GetOrNullAsync();
    }
}

</code></pre><h3>Add the activity that receives the response</h3><p>Somewhere in your main Android app you should add the following:</p><pre><code> [Activity(Label = "GoogleAuthInterceptor")]
    [IntentFilter(actions: new[] { Intent.ActionView },
              Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
              DataSchemes = new[]
              {
                  "com.yourpackageid"
              },
              DataPaths = new[]
              {
                   "/oauth2redirect"
              })]
    public class GoogleAuthInterceptor : Activity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            Android.Net.Uri uri_android = Intent.Data;

            var uri_netfx = new Uri(uri_android.ToString());

            GoogleAccessTokenReader.Auth?.OnPageLoading(uri_netfx);

            var intent = new Intent(this, typeof(MainActivity));
            intent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
            StartActivity(intent);

            Finish();
        }
    }
</code></pre><p>The last three lines before Finish() are really important as they actually make the Google login window go away after logging in. if you don&#8217;t add them it will stay there and the user will need to manually close the window.</p><h2>Retrieving the token from within your app</h2><p>Using the Xamarin Forms dependency injection system, you can get an instance of the token reader like:</p><pre><code>var reader = DependencyService.Get&lt;IGoogleAccessTokenReader&gt;();
var token = await reader.GetOrNullAsync();
</code></pre><h2>Coming soon</h2><p>Refresh tokens, remembering the login and other stuff. Oh my!</p><h2>Demo video</h2><p>If you want a walkthough of doing this, you can watch the following video:</p><div id="youtube2-DjiDis5uybo" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;DjiDis5uybo&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/DjiDis5uybo?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[UWP and Xamarin Forms – How to display your app’s version number]]></title><description><![CDATA[Assume we want to automatically show the version number of your app in your UI, for example, the settings page or elsewhere.]]></description><link>https://www.edandersen.com/p/uwp-and-xamarin-forms-how-to-display-your-apps-version-number</link><guid isPermaLink="false">https://www.edandersen.com/p/uwp-and-xamarin-forms-how-to-display-your-apps-version-number</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Thu, 25 Jun 2020 10:26:47 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/637ca094-fba5-4362-bb94-4bd8a7d57e9f_246x113.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Assume we want to automatically show the version number of your app in your UI, for example, the settings page or elsewhere. Your version number will normally be updated by your CI/CD system (updating Package.appxmanifest for UWP and AndroidManifest.xml for an Android Xamarin app).</p><h2>Create a property to bind to</h2><p>ViewModels and how they bind to your UI are out of scope for this post (as you&#8217;ll have already got this far). However, if you add a property to your view model like this:</p><pre><code>        public string VersionString 
        { 
            get
            {
                return "hello";
            } 
        }
</code></pre><p>We can use this to test the binding before updating to the actual version number later.</p><h2>Bind to it using UWP</h2><p>For UWP you&#8217;ll need to use the TextBlock control and bind the Text property:</p><pre><code>&lt;TextBlock Text="{Binding VersionString}"&gt;&lt;/TextBlock&gt;
</code></pre><h2>Bind to it in Xamarin Forms</h2><p>Xamarin Forms has a slightly different flavour, so you&#8217;ll need to use the Label control:</p><pre><code>&lt;Label Text="{Binding VersionString}"&gt;&lt;/Label&gt;
</code></pre><h2>Check the UI</h2><p>You&#8217;ll see the following in your app:</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aeZ8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4342d60-d143-4c24-8ef9-e4a49323c394_246x113.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aeZ8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4342d60-d143-4c24-8ef9-e4a49323c394_246x113.png 424w, https://substackcdn.com/image/fetch/$s_!aeZ8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4342d60-d143-4c24-8ef9-e4a49323c394_246x113.png 848w, https://substackcdn.com/image/fetch/$s_!aeZ8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4342d60-d143-4c24-8ef9-e4a49323c394_246x113.png 1272w, https://substackcdn.com/image/fetch/$s_!aeZ8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4342d60-d143-4c24-8ef9-e4a49323c394_246x113.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aeZ8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4342d60-d143-4c24-8ef9-e4a49323c394_246x113.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f4342d60-d143-4c24-8ef9-e4a49323c394_246x113.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!aeZ8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4342d60-d143-4c24-8ef9-e4a49323c394_246x113.png 424w, https://substackcdn.com/image/fetch/$s_!aeZ8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4342d60-d143-4c24-8ef9-e4a49323c394_246x113.png 848w, https://substackcdn.com/image/fetch/$s_!aeZ8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4342d60-d143-4c24-8ef9-e4a49323c394_246x113.png 1272w, https://substackcdn.com/image/fetch/$s_!aeZ8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4342d60-d143-4c24-8ef9-e4a49323c394_246x113.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><h2>Updating the binding to show the version number</h2><p>Because getting the version number differs by platform, you should use the super awesome <a href="https://docs.microsoft.com/en-us/xamarin/essentials/">Xamarin Essentials</a> library.</p><p>Add nuget package to your Mobile class library and your UWP app <a href="https://docs.microsoft.com/en-us/xamarin/essentials/get-started?tabs=windows%2Candroid">following the documentation</a>.</p><p>Then add the following using statement to the top of your ViewModel:</p><pre><code>using Xamarin.Essentials;
</code></pre><p>And update your property code:</p><pre><code>        public string VersionString 
        { 
            get
            {
                return "Version " + AppInfo.VersionString;
            } 
        }
</code></pre><p>That&#8217;s it! Xamarin Essentials handles the cross platform bits.</p><h2>Confirm the version number is displayed&nbsp;</h2><p>Relaunch your UWP app and you&#8217;ll see:</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i4Tt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda95216-ea56-46a2-ba18-943a4c130edd_248x129.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i4Tt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda95216-ea56-46a2-ba18-943a4c130edd_248x129.png 424w, https://substackcdn.com/image/fetch/$s_!i4Tt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda95216-ea56-46a2-ba18-943a4c130edd_248x129.png 848w, https://substackcdn.com/image/fetch/$s_!i4Tt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda95216-ea56-46a2-ba18-943a4c130edd_248x129.png 1272w, https://substackcdn.com/image/fetch/$s_!i4Tt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda95216-ea56-46a2-ba18-943a4c130edd_248x129.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i4Tt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda95216-ea56-46a2-ba18-943a4c130edd_248x129.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eda95216-ea56-46a2-ba18-943a4c130edd_248x129.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!i4Tt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda95216-ea56-46a2-ba18-943a4c130edd_248x129.png 424w, https://substackcdn.com/image/fetch/$s_!i4Tt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda95216-ea56-46a2-ba18-943a4c130edd_248x129.png 848w, https://substackcdn.com/image/fetch/$s_!i4Tt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda95216-ea56-46a2-ba18-943a4c130edd_248x129.png 1272w, https://substackcdn.com/image/fetch/$s_!i4Tt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feda95216-ea56-46a2-ba18-943a4c130edd_248x129.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Launch the Xamarin app and you&#8217;ll see:</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Zwv_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2955249f-4760-438d-8ea3-f39c3a23971b_230x87.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Zwv_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2955249f-4760-438d-8ea3-f39c3a23971b_230x87.png 424w, https://substackcdn.com/image/fetch/$s_!Zwv_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2955249f-4760-438d-8ea3-f39c3a23971b_230x87.png 848w, https://substackcdn.com/image/fetch/$s_!Zwv_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2955249f-4760-438d-8ea3-f39c3a23971b_230x87.png 1272w, https://substackcdn.com/image/fetch/$s_!Zwv_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2955249f-4760-438d-8ea3-f39c3a23971b_230x87.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Zwv_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2955249f-4760-438d-8ea3-f39c3a23971b_230x87.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2955249f-4760-438d-8ea3-f39c3a23971b_230x87.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Zwv_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2955249f-4760-438d-8ea3-f39c3a23971b_230x87.png 424w, https://substackcdn.com/image/fetch/$s_!Zwv_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2955249f-4760-438d-8ea3-f39c3a23971b_230x87.png 848w, https://substackcdn.com/image/fetch/$s_!Zwv_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2955249f-4760-438d-8ea3-f39c3a23971b_230x87.png 1272w, https://substackcdn.com/image/fetch/$s_!Zwv_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2955249f-4760-438d-8ea3-f39c3a23971b_230x87.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>These values are pulled from&nbsp;Package.appxmanifest and AndroidManifest.xml respectively.</p><h2>Demo video</h2><p>Here is a nice YouTube Style&#8482; video demo of the above:</p><div id="youtube2-rzQk-7VT6hE" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;rzQk-7VT6hE&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/rzQk-7VT6hE?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Adding an Admin Panel to a .NET Core web app with CoreAdmin]]></title><description><![CDATA[I&#8217;ve published version 1.0.0 of a new open source package and a corresponding nuget package &#8211; CoreAdmin.]]></description><link>https://www.edandersen.com/p/adding-an-admin-panel-to-a-net-core-web-app-with-coreadmin</link><guid isPermaLink="false">https://www.edandersen.com/p/adding-an-admin-panel-to-a-net-core-web-app-with-coreadmin</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Thu, 11 Jun 2020 11:05:14 GMT</pubDate><enclosure url="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/3a1e702d-7d51-4df6-af0e-5d1ad8cb208e.png#0.08441591262084302" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I&#8217;ve published version 1.0.0 of a new open source package and a corresponding nuget package &#8211; CoreAdmin.</p><p>CoreAdmin adds a nice set of CRUD screens to your .NET Core web app in one line of code!</p><h3>Adding CoreAdmin to your app</h3><p>Given a typical Startup.cs file, you will have a ConfigureServices method. You need to add the line services.AddCoreAdmin() somewhere near the bottom (at least after you register your Entity Framework DbContexts).</p><a class="image-link image2" target="_blank" href="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/3a1e702d-7d51-4df6-af0e-5d1ad8cb208e.png#0.08441591262084302" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/3a1e702d-7d51-4df6-af0e-5d1ad8cb208e.png#0.08441591262084302 424w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/3a1e702d-7d51-4df6-af0e-5d1ad8cb208e.png#0.08441591262084302 848w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/3a1e702d-7d51-4df6-af0e-5d1ad8cb208e.png#0.08441591262084302 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/3a1e702d-7d51-4df6-af0e-5d1ad8cb208e.png#0.08441591262084302 1456w" sizes="100vw"><img src="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/3a1e702d-7d51-4df6-af0e-5d1ad8cb208e.png#0.08441591262084302" data-attrs="{&quot;src&quot;:&quot;//old-site-assets.edandersen.com/wp-content/uploads/2020/06/3a1e702d-7d51-4df6-af0e-5d1ad8cb208e.png#0.08441591262084302&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/3a1e702d-7d51-4df6-af0e-5d1ad8cb208e.png#0.08441591262084302 424w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/3a1e702d-7d51-4df6-af0e-5d1ad8cb208e.png#0.08441591262084302 848w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/3a1e702d-7d51-4df6-af0e-5d1ad8cb208e.png#0.08441591262084302 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/3a1e702d-7d51-4df6-af0e-5d1ad8cb208e.png#0.08441591262084302 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p>Then when you visit your site with /coreadmin on the end of the URL, you&#8217;ll see this:</p><a class="image-link image2" target="_blank" href="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/1abf5d43-d998-4492-93ec-6195673cbcce.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/1abf5d43-d998-4492-93ec-6195673cbcce.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/1abf5d43-d998-4492-93ec-6195673cbcce.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/1abf5d43-d998-4492-93ec-6195673cbcce.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/1abf5d43-d998-4492-93ec-6195673cbcce.png 1456w" sizes="100vw"><img src="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/1abf5d43-d998-4492-93ec-6195673cbcce.png" data-attrs="{&quot;src&quot;:&quot;//old-site-assets.edandersen.com/wp-content/uploads/2020/06/1abf5d43-d998-4492-93ec-6195673cbcce.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/1abf5d43-d998-4492-93ec-6195673cbcce.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/1abf5d43-d998-4492-93ec-6195673cbcce.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/1abf5d43-d998-4492-93ec-6195673cbcce.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/1abf5d43-d998-4492-93ec-6195673cbcce.png 1456w" sizes="100vw"></picture><div></div></div></a><p>On the left you can see your database tables (these are the DBSets in your DbContexts). Click one and you get:</p><a class="image-link image2" target="_blank" href="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/688a4435-e339-4d67-8548-2a969bdd5f6f.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/688a4435-e339-4d67-8548-2a969bdd5f6f.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/688a4435-e339-4d67-8548-2a969bdd5f6f.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/688a4435-e339-4d67-8548-2a969bdd5f6f.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/688a4435-e339-4d67-8548-2a969bdd5f6f.png 1456w" sizes="100vw"><img src="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/688a4435-e339-4d67-8548-2a969bdd5f6f.png" data-attrs="{&quot;src&quot;:&quot;//old-site-assets.edandersen.com/wp-content/uploads/2020/06/688a4435-e339-4d67-8548-2a969bdd5f6f.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/688a4435-e339-4d67-8548-2a969bdd5f6f.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/688a4435-e339-4d67-8548-2a969bdd5f6f.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/688a4435-e339-4d67-8548-2a969bdd5f6f.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/688a4435-e339-4d67-8548-2a969bdd5f6f.png 1456w" sizes="100vw"></picture><div></div></div></a><p>From here you can Create new entities, Delete and Edit them. Full searching, sorting, filtering etc are also supported.</p><a class="image-link image2" target="_blank" href="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/f627bad8-e18d-4ef6-b729-c8c28bfcf28d.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/f627bad8-e18d-4ef6-b729-c8c28bfcf28d.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/f627bad8-e18d-4ef6-b729-c8c28bfcf28d.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/f627bad8-e18d-4ef6-b729-c8c28bfcf28d.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/f627bad8-e18d-4ef6-b729-c8c28bfcf28d.png 1456w" sizes="100vw"><img src="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/f627bad8-e18d-4ef6-b729-c8c28bfcf28d.png" data-attrs="{&quot;src&quot;:&quot;//old-site-assets.edandersen.com/wp-content/uploads/2020/06/f627bad8-e18d-4ef6-b729-c8c28bfcf28d.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="//old-site-assets.edandersen.com/wp-content/uploads/2020/06/f627bad8-e18d-4ef6-b729-c8c28bfcf28d.png 424w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/f627bad8-e18d-4ef6-b729-c8c28bfcf28d.png 848w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/f627bad8-e18d-4ef6-b729-c8c28bfcf28d.png 1272w, //old-site-assets.edandersen.com/wp-content/uploads/2020/06/f627bad8-e18d-4ef6-b729-c8c28bfcf28d.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>There are a few limitations on data types and primary keys (for example, entities with composite primary keys are not supported for editing or deletion yet) but this should be sufficient for basic quick and dirty editing of entities.</p><h3>How to get it</h3><p><a href="https://github.com/edandersen/core-admin">CoreAdmin on Github</a></p><p><a href="https://www.nuget.org/packages/CoreAdmin/">CoreAdmin on NuGet</a></p><p> Simply install the nuget package &#8220;CoreAdmin&#8221; and you are good to go! &nbsp;</p><h3>Or watch a demo!</h3><p>Here is a YouTube Style video demo.</p><div id="youtube2-LMZEOgS8t8g" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;LMZEOgS8t8g&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/LMZEOgS8t8g?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item></channel></rss>