<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mauricio Acuña</title>
    <description>The latest articles on DEV Community by Mauricio Acuña (@mauricio_acua_5650fb8a8c).</description>
    <link>https://dev.to/mauricio_acua_5650fb8a8c</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4144734%2Fc92aa06e-b5a1-459a-a590-404cbe81d814.jpg</url>
      <title>DEV Community: Mauricio Acuña</title>
      <link>https://dev.to/mauricio_acua_5650fb8a8c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mauricio_acua_5650fb8a8c"/>
    <language>en</language>
    <item>
      <title>Moving off Microsoft.Azure.ServiceBus and WindowsAzure.ServiceBus before September 30, 2026</title>
      <dc:creator>Mauricio Acuña</dc:creator>
      <pubDate>Sat, 26 Sep 2026 20:05:00 +0000</pubDate>
      <link>https://dev.to/mauricio_acua_5650fb8a8c/moving-off-microsoftazureservicebus-and-windowsazureservicebus-before-september-30-2026-4bcg</link>
      <guid>https://dev.to/mauricio_acua_5650fb8a8c/moving-off-microsoftazureservicebus-and-windowsazureservicebus-before-september-30-2026-4bcg</guid>
      <description>&lt;p&gt;On September 30, 2026, Microsoft retires the old Azure Service Bus client libraries for .NET: &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; and &lt;code&gt;Microsoft.Azure.ServiceBus&lt;/code&gt; (plus &lt;code&gt;com.microsoft.azure.servicebus&lt;/code&gt; on Java). Support for the SBMP protocol ends the same day. The replacement is &lt;code&gt;Azure.Messaging.ServiceBus&lt;/code&gt;, which has been available since November 2020.&lt;/p&gt;

&lt;p&gt;I went through the official docs and migration guides to separate what actually stops working from what just loses support, and turned the rest into a type map and a checklist. Every claim below links to the source.&lt;/p&gt;

&lt;h2&gt;
  
  
  What retires, and what that means in practice
&lt;/h2&gt;

&lt;p&gt;The notice is repeated on several Microsoft Learn pages, including the &lt;a href="https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-faq" rel="noopener noreferrer"&gt;Service Bus FAQ&lt;/a&gt;. In short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt;, &lt;code&gt;Microsoft.Azure.ServiceBus&lt;/code&gt; and &lt;code&gt;com.microsoft.azure.servicebus&lt;/code&gt; are retired on September 30, 2026.&lt;/li&gt;
&lt;li&gt;Support for the SBMP protocol ends and "you'll no longer be able to use this protocol after 30 September 2026".&lt;/li&gt;
&lt;li&gt;The old libraries "can still be used beyond 30 September 2026", but they get no official support and no updates from Microsoft.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So there are two different situations, depending on the package and the transport.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; on its default settings stops working.&lt;/strong&gt; That package talks to Service Bus over SBMP by default (&lt;a href="https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-amqp-dotnet" rel="noopener noreferrer"&gt;Microsoft Learn&lt;/a&gt;). If your connection string does not contain &lt;code&gt;TransportType=Amqp&lt;/code&gt;, your app is using SBMP, and SBMP is what goes away.&lt;/p&gt;

&lt;p&gt;There is a documented stopgap: append &lt;code&gt;;TransportType=Amqp&lt;/code&gt; to the connection string and the same package uses AMQP 1.0 instead. The same page lists the behavior differences when you do that: &lt;code&gt;OperationTimeout&lt;/code&gt; is ignored, &lt;code&gt;Receive(TimeSpan.Zero)&lt;/code&gt; becomes a 10 second receive, and only the receiver that got a message can complete it by lock token. It also changes how message bodies are serialized (more on that below). Treat it as a way to buy a few weeks, not as the migration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Microsoft.Azure.ServiceBus&lt;/code&gt; keeps connecting, without support.&lt;/strong&gt; This package already uses AMQP (the transport options the FAQ lists for it are AMQP and AMQP over WebSockets), so nothing on the wire changes on October 1. What changes is that it gets no more fixes, including security fixes, and it has been &lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md" rel="noopener noreferrer"&gt;officially deprecated&lt;/a&gt; for a while.&lt;/p&gt;

&lt;h3&gt;
  
  
  The exception: WCF Relay
&lt;/h3&gt;

&lt;p&gt;If you reference &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; for Azure WCF Relay (&lt;code&gt;NetTcpRelayBinding&lt;/code&gt; and friends), that use is not part of this retirement. Microsoft staff confirmed on Microsoft Q&amp;amp;A that the package is "NOT deprecated for usage with Relay" and will stay supported with WCF Relay until further notice (&lt;a href="https://learn.microsoft.com/en-us/answers/questions/1822432/retiring-of-the-windowsazure-servicebus-nuget-pack" rel="noopener noreferrer"&gt;answer&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-in/answers/questions/5910253/will-the-retiring-of-the-smbp-protocol-also-hit-wc" rel="noopener noreferrer"&gt;second answer about SBMP ports&lt;/a&gt;). The same thread points to Azure Relay Hybrid Connections as the long-term direction.&lt;/p&gt;

&lt;p&gt;If one project uses the package for both relay and queues, only the queue and topic code has to move.&lt;/p&gt;

&lt;h2&gt;
  
  
  The type map
&lt;/h2&gt;

&lt;p&gt;The new library has a single entry point, &lt;code&gt;ServiceBusClient&lt;/code&gt;, and you create senders, receivers and processors from it. One client holds one AMQP connection that everything created from it shares (&lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md#connection-pooling" rel="noopener noreferrer"&gt;migration guide, connection pooling&lt;/a&gt;).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Old&lt;/th&gt;
&lt;th&gt;New (&lt;code&gt;Azure.Messaging.ServiceBus&lt;/code&gt;)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;QueueClient&lt;/code&gt;, &lt;code&gt;TopicClient&lt;/code&gt;, &lt;code&gt;MessageSender&lt;/code&gt; (sending)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ServiceBusClient.CreateSender(queueOrTopic)&lt;/code&gt; returns &lt;code&gt;ServiceBusSender&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;MessageReceiver&lt;/code&gt;, &lt;code&gt;QueueClient.ReceiveAsync&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ServiceBusClient.CreateReceiver(...)&lt;/code&gt; returns &lt;code&gt;ServiceBusReceiver&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;RegisterMessageHandler&lt;/code&gt; + &lt;code&gt;MessageHandlerOptions&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ServiceBusProcessor&lt;/code&gt; + &lt;code&gt;ServiceBusProcessorOptions&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;SessionClient&lt;/code&gt;, &lt;code&gt;RegisterSessionHandler&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ServiceBusSessionProcessor&lt;/code&gt;, &lt;code&gt;ServiceBusSessionReceiver&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Message&lt;/code&gt; / &lt;code&gt;BrokeredMessage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ServiceBusMessage&lt;/code&gt; to send, &lt;code&gt;ServiceBusReceivedMessage&lt;/code&gt; when received&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Message.UserProperties&lt;/code&gt; / &lt;code&gt;BrokeredMessage.Properties&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ApplicationProperties&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Label&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Subject&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ScheduledEnqueueTimeUtc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ScheduledEnqueueTime&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ManagementClient&lt;/code&gt; / &lt;code&gt;NamespaceManager&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ServiceBusAdministrationClient&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;MessagingFactory&lt;/code&gt;, shared &lt;code&gt;ServiceBusConnection&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;One &lt;code&gt;ServiceBusClient&lt;/code&gt; per app, one connection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Library-specific exception types&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ServiceBusException&lt;/code&gt; with a &lt;code&gt;Reason&lt;/code&gt; (&lt;a href="https://learn.microsoft.com/en-us/dotnet/api/azure.messaging.servicebus.servicebusfailurereason" rel="noopener noreferrer"&gt;&lt;code&gt;ServiceBusFailureReason&lt;/code&gt;&lt;/a&gt;), for example &lt;code&gt;MessageLockLost&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sources: &lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md" rel="noopener noreferrer"&gt;guide for Microsoft.Azure.ServiceBus&lt;/a&gt;, &lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide_WindowsAzureServiceBus.md" rel="noopener noreferrer"&gt;guide for WindowsAzure.ServiceBus&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/azure.messaging.servicebus.servicebusmessage" rel="noopener noreferrer"&gt;ServiceBusMessage reference&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A typical handler, before and after:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Microsoft.Azure.ServiceBus&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;QueueClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterMessageHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;HandleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SystemProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LockToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MessageHandlerOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;AutoComplete&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MaxConcurrentCalls&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MaxAutoRenewDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Azure.Messaging.ServiceBus&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ServiceBusClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// one per app&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;processor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ServiceBusProcessorOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;AutoCompleteMessages&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MaxConcurrentCalls&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MaxAutoLockRenewalDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProcessMessageAsync&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToObjectFromJson&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;HandleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteMessageAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProcessErrorAsync&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartProcessingAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Behavior changes that compile fine and break at runtime
&lt;/h2&gt;

&lt;p&gt;Renaming types is the easy half. These are the changes that pass the build and show up later as duplicates, lost locks or messages nobody can read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Settlement moves off the message.&lt;/strong&gt; With &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; you called &lt;code&gt;CompleteAsync&lt;/code&gt;, &lt;code&gt;AbandonAsync&lt;/code&gt; or &lt;code&gt;DeadLetterAsync&lt;/code&gt; on the &lt;code&gt;BrokeredMessage&lt;/code&gt;. Now those calls live on the &lt;code&gt;ServiceBusReceiver&lt;/code&gt;, or on the event args when you use the processor (&lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide_WindowsAzureServiceBus.md#brokeredmessage-changed-to-message" rel="noopener noreferrer"&gt;guide&lt;/a&gt;). There is also no batch settlement API; the guide's FAQ explains why and shows the &lt;code&gt;Task.WhenAll&lt;/code&gt; loop to write instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Auto-complete is on by default.&lt;/strong&gt; The processor completes the message after your handler returns unless you set &lt;code&gt;AutoCompleteMessages = false&lt;/code&gt;. If the handler throws without settling, the processor abandons the message. Decide per handler which one you want, and write it down in the options instead of relying on the default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Lock renewal has a 5 minute ceiling by default.&lt;/strong&gt; &lt;code&gt;ServiceBusProcessorOptions.MaxAutoLockRenewalDuration&lt;/code&gt; defaults to 5 minutes (&lt;a href="https://learn.microsoft.com/en-us/dotnet/api/azure.messaging.servicebus.servicebusprocessoroptions.maxautolockrenewalduration" rel="noopener noreferrer"&gt;reference&lt;/a&gt;). A handler that runs longer loses the lock, the message is delivered again with a higher &lt;code&gt;DeliveryCount&lt;/code&gt;, and after enough attempts it lands in the dead-letter queue. Also note that automatic renewal is a processor feature: &lt;code&gt;ServiceBusReceiverOptions&lt;/code&gt; has no renewal setting, so with a plain receiver you call &lt;code&gt;RenewMessageLockAsync&lt;/code&gt; yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Properties and dead-lettering.&lt;/strong&gt; &lt;code&gt;UserProperties&lt;/code&gt; becomes &lt;code&gt;ApplicationProperties&lt;/code&gt;. Instead of writing the reason and description into &lt;code&gt;UserProperties&lt;/code&gt; with the &lt;code&gt;DeadLetterReasonHeader&lt;/code&gt; constants, you pass them to &lt;code&gt;DeadLetterMessageAsync(message, reason, description)&lt;/code&gt; and read them back from &lt;code&gt;DeadLetterReason&lt;/code&gt; and &lt;code&gt;DeadLetterErrorDescription&lt;/code&gt;. To read the dead-letter queue you set &lt;code&gt;SubQueue = SubQueue.DeadLetter&lt;/code&gt; on the receiver options instead of building the &lt;code&gt;$DeadLetterQueue&lt;/code&gt; path yourself (&lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md#dead-letter-messages" rel="noopener noreferrer"&gt;guide&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. The body is &lt;code&gt;BinaryData&lt;/code&gt;, and old bodies may not be what you think.&lt;/strong&gt; &lt;code&gt;Message.Body&lt;/code&gt; was a &lt;code&gt;byte[]&lt;/code&gt;; &lt;code&gt;ServiceBusMessage.Body&lt;/code&gt; is &lt;code&gt;BinaryData&lt;/code&gt;, and the &lt;code&gt;string&lt;/code&gt; constructor encodes UTF-8. The bigger issue is with &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt;: &lt;code&gt;new BrokeredMessage(myObject)&lt;/code&gt; serialized the object for you, with &lt;code&gt;DataContractSerializer&lt;/code&gt; on SBMP, and the receiver got it back with &lt;code&gt;GetBody&amp;lt;T&amp;gt;()&lt;/code&gt; (&lt;a href="https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messages-payloads#payload-serialization" rel="noopener noreferrer"&gt;Microsoft Learn: payload serialization&lt;/a&gt;). The new library has no &lt;code&gt;GetBody&amp;lt;T&amp;gt;()&lt;/code&gt;. Messages already sitting in a queue, or sent by services you have not migrated yet, still carry that old format. The same page recommends taking explicit control of serialization and, while old and new versions run side by side, trying more than one deserializer before dead-lettering. In practice: switch senders to an explicit format (JSON with &lt;code&gt;ContentType&lt;/code&gt; set) and make receivers able to read both formats until the queues are clean, or drain the queues before the switch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. &lt;code&gt;MessageId&lt;/code&gt; is no longer generated for you.&lt;/strong&gt; &lt;code&gt;BrokeredMessage&lt;/code&gt; constructors assigned a new GUID to &lt;code&gt;MessageId&lt;/code&gt;; &lt;code&gt;ServiceBusMessage&lt;/code&gt; leaves it empty. If you rely on duplicate detection, set &lt;code&gt;MessageId&lt;/code&gt; to a stable value from your domain (an order id, not a new GUID per retry). On a partitioned entity with duplicate detection enabled, a send with no &lt;code&gt;SessionId&lt;/code&gt;, &lt;code&gt;PartitionKey&lt;/code&gt; or &lt;code&gt;MessageId&lt;/code&gt; fails (&lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide_WindowsAzureServiceBus.md#sending-messages" rel="noopener noreferrer"&gt;guide&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Batches are measured before sending.&lt;/strong&gt; Sending a list still works, but the safe path is &lt;code&gt;CreateMessageBatchAsync&lt;/code&gt; plus &lt;code&gt;TryAddMessage&lt;/code&gt;, which returns &lt;code&gt;false&lt;/code&gt; when a message does not fit. Batching messages bound for different partitions is no longer supported.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Plugins and baggage are gone.&lt;/strong&gt; &lt;code&gt;RegisterPlugin&lt;/code&gt; has no direct equivalent; the guide points to extending the types, with a dedicated claim-check sample. Activity baggage no longer flows through a &lt;code&gt;Correlation-Context&lt;/code&gt; property; with the OpenTelemetry support, &lt;code&gt;tracestate&lt;/code&gt; goes into &lt;code&gt;ApplicationProperties&lt;/code&gt; instead (&lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md#distributed-tracing" rel="noopener noreferrer"&gt;guide&lt;/a&gt;). If a downstream consumer reads &lt;code&gt;Correlation-Context&lt;/code&gt;, it will stop getting it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. Client lifetime.&lt;/strong&gt; Code that creates a &lt;code&gt;QueueClient&lt;/code&gt; per send and closes it opens and closes a connection each time. With the new library, keep one &lt;code&gt;ServiceBusClient&lt;/code&gt; for the life of the app (a singleton in DI) and dispose it on shutdown; it implements &lt;code&gt;IAsyncDisposable&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Functions: the Service Bus extension 5.x
&lt;/h2&gt;

&lt;p&gt;If you use the Service Bus trigger or output binding, the relevant package is the Functions extension, not the SDK directly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extension 4.x and earlier exposed types from &lt;code&gt;Microsoft.Azure.ServiceBus&lt;/code&gt; (&lt;code&gt;Message&lt;/code&gt;, &lt;code&gt;MessageReceiver&lt;/code&gt;, &lt;code&gt;IMessageSession&lt;/code&gt;). Extension 5.x is built on &lt;code&gt;Azure.Messaging.ServiceBus&lt;/code&gt;, so the trigger binds to &lt;code&gt;ServiceBusReceivedMessage&lt;/code&gt; and settlement goes through &lt;code&gt;ServiceBusMessageActions&lt;/code&gt; (&lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-service-bus" rel="noopener noreferrer"&gt;bindings reference&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Extension 4.x itself was retired on March 31, 2025 (&lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/migrate-service-bus-version-4-version-5" rel="noopener noreferrer"&gt;migration article&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Recommended versions: &lt;code&gt;Microsoft.Azure.WebJobs.Extensions.ServiceBus&lt;/code&gt; 5.13.4 or later for in-process, &lt;code&gt;Microsoft.Azure.Functions.Worker.Extensions.ServiceBus&lt;/code&gt; 5.14.1 or later for isolated. In the isolated worker, binding to &lt;code&gt;ServiceBusReceivedMessage&lt;/code&gt; and &lt;code&gt;ServiceBusMessageActions&lt;/code&gt; needs 5.14.1 or later; older isolated extensions only bind to &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;byte[]&lt;/code&gt; and POCOs.&lt;/li&gt;
&lt;li&gt;When you settle through &lt;code&gt;ServiceBusMessageActions&lt;/code&gt;, set &lt;code&gt;AutoCompleteMessages = false&lt;/code&gt; on the trigger so the runtime does not also try to complete the message.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;host.json&lt;/code&gt; changes shape between 4.x and 5.x. The nested &lt;code&gt;messageHandlerOptions&lt;/code&gt;, &lt;code&gt;sessionHandlerOptions&lt;/code&gt; and &lt;code&gt;batchOptions&lt;/code&gt; blocks become flat properties under &lt;code&gt;serviceBus&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"serviceBus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"autoCompleteMessages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maxAutoLockRenewalDuration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"00:05:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maxConcurrentCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maxConcurrentSessions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare the defaults while you are there. The documented default for &lt;code&gt;maxConcurrentSessions&lt;/code&gt; is 2000 in 4.x and 8 in 5.x, which changes throughput for session-enabled queues if you never set it explicitly.&lt;/p&gt;

&lt;p&gt;And if the function app still runs on the in-process model, that model reaches end of support on November 10, 2026 (&lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/migrate-dotnet-to-isolated-model" rel="noopener noreferrer"&gt;notice&lt;/a&gt;). It makes sense to plan both moves together: the isolated worker plus extension 5.x.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;List every project that references &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; or &lt;code&gt;Microsoft.Azure.ServiceBus&lt;/code&gt;, including shared libraries and Functions extensions below 5.x.&lt;/li&gt;
&lt;li&gt;For each &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; reference, note whether it is used for messaging, WCF Relay or both.&lt;/li&gt;
&lt;li&gt;Find connection strings without &lt;code&gt;TransportType=Amqp&lt;/code&gt; on &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt;: those are on SBMP. If the migration will not be in production by September 30, add &lt;code&gt;;TransportType=Amqp&lt;/code&gt; and test it now.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;Azure.Messaging.ServiceBus&lt;/code&gt; and create one &lt;code&gt;ServiceBusClient&lt;/code&gt; per app, registered as a singleton.&lt;/li&gt;
&lt;li&gt;Replace senders, receivers and handlers using the type map above.&lt;/li&gt;
&lt;li&gt;For every handler, set &lt;code&gt;AutoCompleteMessages&lt;/code&gt;, &lt;code&gt;MaxConcurrentCalls&lt;/code&gt; and &lt;code&gt;MaxAutoLockRenewalDuration&lt;/code&gt; explicitly, and check the longest processing time against the lock renewal window.&lt;/li&gt;
&lt;li&gt;Move &lt;code&gt;UserProperties&lt;/code&gt; / &lt;code&gt;Properties&lt;/code&gt; to &lt;code&gt;ApplicationProperties&lt;/code&gt; and &lt;code&gt;Label&lt;/code&gt; to &lt;code&gt;Subject&lt;/code&gt;, and check consumers that read them by name.&lt;/li&gt;
&lt;li&gt;Replace manual dead-letter headers with &lt;code&gt;DeadLetterMessageAsync(message, reason, description)&lt;/code&gt; and &lt;code&gt;SubQueue.DeadLetter&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Decide the body format (JSON with &lt;code&gt;ContentType&lt;/code&gt;), and handle messages in the old format until queues are drained, especially anything sent with &lt;code&gt;new BrokeredMessage(object)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;MessageId&lt;/code&gt; explicitly wherever duplicate detection matters.&lt;/li&gt;
&lt;li&gt;Replace exception handling on old types with &lt;code&gt;ServiceBusException.Reason&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Functions: move to the Service Bus extension 5.x, convert &lt;code&gt;host.json&lt;/code&gt;, and settle with &lt;code&gt;ServiceBusMessageActions&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Test against a real namespace (the emulator or a dev namespace), including redelivery, dead-lettering and a handler that runs longer than the lock duration.&lt;/li&gt;
&lt;li&gt;Roll out consumers before producers if the body format changes, so nothing arrives in a format no one can read.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Checking your own solution
&lt;/h2&gt;

&lt;p&gt;I maintain a small free command-line tool, Net10Check, that scans a solution for the things that block or complicate a move to .NET 10. It flags projects that reference &lt;code&gt;Microsoft.Azure.ServiceBus&lt;/code&gt; or &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; (and the old Event Hubs packages), in-process Azure Functions, and &lt;code&gt;BinaryFormatter&lt;/code&gt;. It runs locally and uploads nothing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; Net10Check
net10-check path/to/YourSolution.sln
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works from project files, so it tells you where the old packages are referenced, not whether a &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; reference is only there for WCF Relay. That part still needs a look at the code. Source: &lt;a href="https://github.com/mauri0686/net10check" rel="noopener noreferrer"&gt;github.com/mauri0686/net10check&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you would rather hand the migration off, I do it at a fixed price and deliver it as a pull request: &lt;a href="https://mauri0686.github.io/onlts/servicebus/" rel="noopener noreferrer"&gt;Service Bus SDK migration&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>azure</category>
      <category>csharp</category>
      <category>servicebus</category>
    </item>
    <item>
      <title>Moving .NET 8/9 apps to .NET 10 before Nov 10: a checklist with links to every breaking change</title>
      <dc:creator>Mauricio Acuña</dc:creator>
      <pubDate>Sat, 26 Sep 2026 18:38:10 +0000</pubDate>
      <link>https://dev.to/mauricio_acua_5650fb8a8c/moving-net-89-apps-to-net-10-before-nov-10-a-checklist-with-links-to-every-breaking-change-5ge0</link>
      <guid>https://dev.to/mauricio_acua_5650fb8a8c/moving-net-89-apps-to-net-10-before-nov-10-a-checklist-with-links-to-every-breaking-change-5ge0</guid>
      <description>&lt;p&gt;.NET 8 and .NET 9 both reach end of support on November 10, 2026 (&lt;a href="https://devblogs.microsoft.com/dotnet/dotnet-8-9-end-of-support/" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;). I have been going through the official breaking change lists for .NET 10 and turned them into a checklist. Sharing it in case it saves someone an afternoon. Everything below links to learn.microsoft.com.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. SDK and project files&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install the .NET 10 SDK and update &lt;code&gt;global.json&lt;/code&gt; if you pin one.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TargetFramework&lt;/code&gt; to &lt;code&gt;net10.0&lt;/code&gt; (check &lt;code&gt;Directory.Build.props&lt;/code&gt; first, it is often set there).&lt;/li&gt;
&lt;li&gt;Update &lt;code&gt;Microsoft.*&lt;/code&gt; packages to 10.x together: ASP.NET Core, EF Core, &lt;code&gt;Microsoft.Extensions.*&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PackageReference&lt;/code&gt; without a version is now an error (&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/10.0/nu1015-packagereference-version" rel="noopener noreferrer"&gt;NU1015&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;You may get &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/10.0/nu1510-pruned-references" rel="noopener noreferrer"&gt;NU1510&lt;/a&gt; for direct references that the platform now provides. Usually you can just remove them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. NuGet audit now includes transitive packages&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On &lt;code&gt;net10.0&lt;/code&gt;, &lt;code&gt;NuGetAuditMode&lt;/code&gt; defaults to &lt;code&gt;all&lt;/code&gt;, so vulnerable transitive packages now produce NU1901 to NU1904 warnings (&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/10.0/nugetaudit-transitive-packages" rel="noopener noreferrer"&gt;details&lt;/a&gt;). With &lt;code&gt;TreatWarningsAsErrors&lt;/code&gt;, restore fails. &lt;code&gt;dotnet nuget why &amp;lt;package&amp;gt;&lt;/code&gt; tells you which top-level package pulls it in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. ASP.NET Core 10&lt;/strong&gt; (&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/breaking-changes/10/overview" rel="noopener noreferrer"&gt;full list&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cookie auth no longer redirects to the login page for known API endpoints (&lt;code&gt;[ApiController]&lt;/code&gt;, minimal APIs with JSON, &lt;code&gt;TypedResults&lt;/code&gt;, SignalR). They return 401/403 instead (&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/breaking-changes/10/cookie-authentication-api-endpoints" rel="noopener noreferrer"&gt;details&lt;/a&gt;). This one breaks integration tests that assert a 302. There is an AppContext switch to restore the old behavior if you need time.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WebHostBuilder&lt;/code&gt;, &lt;code&gt;IWebHost&lt;/code&gt; and &lt;code&gt;WebHost&lt;/code&gt; are obsolete. Old &lt;code&gt;Program.cs&lt;/code&gt; files still using &lt;code&gt;WebHost.CreateDefaultBuilder&lt;/code&gt; will light up with warnings.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ForwardedHeadersOptions.KnownNetworks&lt;/code&gt; is obsolete, use &lt;code&gt;KnownIPNetworks&lt;/code&gt; with &lt;code&gt;System.Net.IPNetwork&lt;/code&gt; (warning ASPDEPR005).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IActionContextAccessor&lt;/code&gt; is obsolete, &lt;code&gt;WithOpenApi&lt;/code&gt; is deprecated, Razor runtime compilation is obsolete.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. EF Core 10&lt;/strong&gt; (&lt;a href="https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/breaking-changes" rel="noopener noreferrer"&gt;full list&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Contains()&lt;/code&gt; over a collection parameter now translates to multiple scalar parameters instead of a JSON array (&lt;code&gt;OPENJSON&lt;/code&gt;). Better plans in most cases, but check hot queries with large lists. You can switch back per query or globally.&lt;/li&gt;
&lt;li&gt;SQL parameter names are simplified (&lt;code&gt;@city&lt;/code&gt; instead of &lt;code&gt;@__city_0&lt;/code&gt;). Snapshot tests that compare SQL text will fail, and expect a burst of plan recompilation after deploy.&lt;/li&gt;
&lt;li&gt;EF now injects &lt;code&gt;Application Name&lt;/code&gt; into the connection string if you don't set one. If you mix EF with Dapper or ADO.NET inside a &lt;code&gt;TransactionScope&lt;/code&gt;, the two different connection strings can escalate to a distributed transaction. Setting &lt;code&gt;Application Name&lt;/code&gt; yourself avoids it.&lt;/li&gt;
&lt;li&gt;With &lt;code&gt;UseAzureSql&lt;/code&gt; or compatibility level 170, JSON columns map to the new &lt;code&gt;json&lt;/code&gt; type, and your next migration will alter existing &lt;code&gt;nvarchar(max)&lt;/code&gt; JSON columns. Read the migration before applying it.&lt;/li&gt;
&lt;li&gt;Check your EF Core provider before anything else. Some third-party providers have no EF Core 10 release yet: as of today the latest Pomelo MySQL provider (9.0.0) still requires EF Core 9.x, so the upgrade cannot restore until it ships or you switch providers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dotnet ef&lt;/code&gt; on multi-targeted projects now requires &lt;code&gt;--framework&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you use Microsoft.Data.Sqlite: &lt;code&gt;DateTimeOffset&lt;/code&gt; and &lt;code&gt;DateTime&lt;/code&gt; handling moved to UTC in three places, all marked high impact.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Core libraries&lt;/strong&gt; (&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/10" rel="noopener noreferrer"&gt;full list&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;System.Linq.AsyncEnumerable&lt;/code&gt; is now in the framework. If you reference &lt;code&gt;System.Linq.Async&lt;/code&gt;, remove it or move to 7.0.0, otherwise you get ambiguity errors (&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/10.0/asyncenumerable" rel="noopener noreferrer"&gt;details&lt;/a&gt;). &lt;code&gt;SelectAwait&lt;/code&gt; call sites become &lt;code&gt;Select&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BackgroundService.ExecuteAsync&lt;/code&gt; now runs entirely on a background thread. If you relied on the synchronous part before the first &lt;code&gt;await&lt;/code&gt; running during startup, move it to the constructor or &lt;code&gt;StartAsync&lt;/code&gt; (&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/extensions/10.0/backgroundservice-executeasync-task" rel="noopener noreferrer"&gt;details&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Null values in configuration are now preserved, &lt;code&gt;System.Text.Json&lt;/code&gt; checks for property name conflicts, and the default trace context propagator is W3C. Worth a quick search in your codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6. Containers and hosting&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The default &lt;code&gt;10.0&lt;/code&gt; image tags are Ubuntu 24.04, and there are no Debian images for .NET 10 (&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/containers/10.0/default-images-use-ubuntu" rel="noopener noreferrer"&gt;details&lt;/a&gt;). If your Dockerfile installs packages with apt, check the package names.&lt;/li&gt;
&lt;li&gt;Azure Functions: the in-process model does not support .NET 10 and its own support ends November 10, 2026 too. You have to move to the isolated worker model (&lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/migrate-dotnet-to-isolated-model" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt;). Watch out for &lt;code&gt;[JsonProperty]&lt;/code&gt; attributes: the isolated model uses System.Text.Json and silently ignores them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;7. CI&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bump the SDK version in your pipeline (&lt;code&gt;actions/setup-dotnet&lt;/code&gt;, &lt;code&gt;UseDotNet@2&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Run the full test suite, not just the build. Most of the items above are behavioral changes, so they compile fine and fail at runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I missed something that bit you, please add it in the comments and I'll update the list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking your own solution
&lt;/h2&gt;

&lt;p&gt;I am a .NET developer and I now offer fixed-price .NET 10 upgrades, which is why I made this list. I also wrote a small free command-line tool that checks a solution against these items, including third-party packages that have no .NET 10 release yet, and prints what needs attention. It runs locally and uploads nothing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; Net10Check
net10-check path/to/YourApp.sln
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source is on &lt;a href="https://github.com/mauri0686/net10check" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Happy to hear what it misses. If you would rather hand the upgrade off, details are &lt;a href="https://mauri0686.github.io/onlts/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>aspnetcore</category>
      <category>efcore</category>
    </item>
  </channel>
</rss>
