├── .gitignore ├── AtomicSends ├── .vscode │ ├── launch.json │ └── tasks.json ├── AtomicSends.csproj ├── MessageExtensions.cs ├── Prepare.cs └── Program.cs ├── Azure Service Bus Deep Dive.pptx ├── AzureServiceBus.DeepDive.sln ├── BindaryData ├── BindaryData.csproj └── Program.cs ├── ConnectionsManagement ├── .vscode │ ├── launch.json │ └── tasks.json ├── ConnectionsManagement.csproj ├── MessageExtensions.cs ├── Prepare.cs └── Program.cs ├── Directory.Build.props ├── LICENSE ├── MessageBatching ├── .vscode │ ├── launch.json │ └── tasks.json ├── MessageBatching.csproj ├── MessageExtensions.cs ├── Prepare.cs └── Program.cs ├── MessageDeadlettering ├── .vscode │ ├── launch.json │ └── tasks.json ├── MessageDeadlettering.csproj ├── MessageExtensions.cs ├── Prepare.cs └── Program.cs ├── MessageDeduplication ├── .vscode │ ├── launch.json │ └── tasks.json ├── MessageDeduplication.csproj ├── MessageExtensions.cs ├── Prepare.cs └── Program.cs ├── MessageExpiry ├── .vscode │ ├── launch.json │ └── tasks.json ├── MessageExpiry.csproj ├── MessageExtensions.cs ├── Prepare.cs └── Program.cs ├── MessageForwarding ├── .vscode │ ├── launch.json │ └── tasks.json ├── MessageExtensions.cs ├── MessageForwarding.csproj ├── Prepare.cs └── Program.cs ├── MessageScheduling ├── .vscode │ ├── launch.json │ └── tasks.json ├── MessageExtensions.cs ├── MessageScheduling.csproj ├── Prepare.cs └── Program.cs ├── MessageSessions ├── .vscode │ ├── launch.json │ └── tasks.json ├── MessageExtensions.cs ├── MessageSessions.csproj ├── Prepare.cs └── Program.cs ├── Plugins ├── .vscode │ ├── launch.json │ └── tasks.json ├── PluggableServiceBusReceiver.cs ├── PluggableServiceBusSender.cs ├── Plugins.cs ├── Plugins.csproj ├── Prepare.cs ├── Program.cs └── ServiceBusClientExtensions.cs ├── PubSub ├── .vscode │ ├── launch.json │ └── tasks.json ├── MessageExtensions.cs ├── Prepare.cs ├── Program.cs └── PubSub.csproj ├── README.md ├── Receiving ├── .vscode │ ├── launch.json │ └── tasks.json ├── MessageExtensions.cs ├── Prepare.cs ├── Program.cs ├── Receiving.csproj └── Trivia.cs ├── SafeBatching ├── Prepare.cs ├── Program.cs └── SafeBatching.csproj ├── SendVia ├── .vscode │ ├── launch.json │ └── tasks.json ├── CrossEntityTransaction.csproj ├── MessageExtensions.cs ├── Prepare.cs ├── Program.cs └── SendVia.csproj ├── Sending ├── .vscode │ ├── launch.json │ └── tasks.json ├── Prepare.cs ├── Program.cs └── Sending.csproj ├── Topologies ├── .vscode │ ├── launch.json │ └── tasks.json ├── MessageExtensions.cs ├── Prepare.cs ├── Program.cs └── Topologies.csproj ├── TransferDLQ ├── .vscode │ ├── launch.json │ └── tasks.json ├── EntityNameFormatter.cs ├── MessageExtensions.cs ├── Prepare.cs ├── Program.cs └── TransferDLQ.csproj └── delete.obj.bin.cmd /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /AtomicSends/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/AtomicSend.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /AtomicSends/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/AtomicSends.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/AtomicSends.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/AtomicSends.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /AtomicSends/AtomicSends.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AtomicSends/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace AtomicSends 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /AtomicSends/Prepare.cs: -------------------------------------------------------------------------------- 1 | using Azure.Messaging.ServiceBus.Administration; 2 | 3 | namespace AtomicSends; 4 | 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | public static class Prepare 9 | { 10 | public static async Task Infrastructure(string connectionString, string destination) 11 | { 12 | var client = new ServiceBusAdministrationClient(connectionString); 13 | if (await client.QueueExistsAsync(destination)) 14 | { 15 | await client.DeleteQueueAsync(destination); 16 | } 17 | await client.CreateQueueAsync(destination); 18 | } 19 | 20 | public static async Task ReportNumberOfMessages(string connectionString, string destination) 21 | { 22 | var client = new ServiceBusAdministrationClient(connectionString); 23 | 24 | QueueRuntimeProperties queueProperties = await client.GetQueueRuntimePropertiesAsync(destination); 25 | Console.WriteLine($"{queueProperties.ActiveMessageCount} messages in '{destination}'"); 26 | } 27 | } -------------------------------------------------------------------------------- /AtomicSends/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using System.Transactions; 6 | using Azure.Messaging.ServiceBus; 7 | 8 | namespace AtomicSends; 9 | 10 | internal class Program 11 | { 12 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 13 | 14 | private static readonly string destination = "queue"; 15 | 16 | private static async Task Main(string[] args) 17 | { 18 | await Prepare.Infrastructure(connectionString, destination); 19 | 20 | var serviceBusClient = new ServiceBusClient(connectionString); 21 | 22 | var sender = serviceBusClient.CreateSender(destination); 23 | 24 | // using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) 25 | // { 26 | // var message = new ServiceBusMessage("Deep Dive 1"); 27 | // await sender.SendMessageAsync(message); 28 | // Console.WriteLine($"Sent message 1 in transaction '{Transaction.Current.TransactionInformation.LocalIdentifier}'"); 29 | // 30 | // await Prepare.ReportNumberOfMessages(connectionString, destination); 31 | // 32 | // message = new ServiceBusMessage("Deep Dive 2"); 33 | // await sender.SendMessageAsync(message); 34 | // Console.WriteLine($"Sent message 2 in transaction '{Transaction.Current.TransactionInformation.LocalIdentifier}'"); 35 | // 36 | // Console.WriteLine("About to complete transaction scope."); 37 | // await Prepare.ReportNumberOfMessages(connectionString, destination); 38 | // 39 | // scope.Complete(); 40 | // Console.WriteLine("Completed transaction scope."); 41 | // } 42 | // 43 | // await Prepare.ReportNumberOfMessages(connectionString, destination); 44 | // 45 | var messages = new List(); 46 | // messages.Clear(); 47 | // Console.WriteLine(); 48 | 49 | for (var i = 0; i < 101; i++) 50 | { 51 | messages.Add(new ServiceBusMessage($"Deep Dive {i}")); 52 | } 53 | 54 | try 55 | { 56 | using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); 57 | Console.WriteLine($"Sending {messages.Count} messages in a batch with in transaction '{Transaction.Current.TransactionInformation.LocalIdentifier}'."); 58 | await sender.SendMessagesAsync(messages); 59 | scope.Complete(); 60 | } 61 | catch (ServiceBusException ex) when (ex.Reason == ServiceBusFailureReason.QuotaExceeded) 62 | { 63 | Console.Error.WriteLine(ex.Message); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Azure Service Bus Deep Dive.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanFeldman/AzureServiceBus.DeepDive/871e51cae132554bba46a1f79bc37469e5fc8437/Azure Service Bus Deep Dive.pptx -------------------------------------------------------------------------------- /AzureServiceBus.DeepDive.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32328.378 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sending", "Sending\Sending.csproj", "{706CF99B-4682-4E5A-9884-84B3B2318FD5}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectionsManagement", "ConnectionsManagement\ConnectionsManagement.csproj", "{DBEAEDAC-C076-4D73-8F00-330E69E228B6}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessageScheduling", "MessageScheduling\MessageScheduling.csproj", "{9AC9AE46-0F0A-4AC0-A37D-930FEAD8A8A0}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessageExpiry", "MessageExpiry\MessageExpiry.csproj", "{D93C1D5A-0D19-41F4-BCF9-90008DF5C4BF}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessageForwarding", "MessageForwarding\MessageForwarding.csproj", "{9A039549-7569-4253-A43D-448E590AD772}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessageDeadlettering", "MessageDeadlettering\MessageDeadlettering.csproj", "{8E44CF95-976E-4B5C-8CC6-1BA2D5D4F993}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PubSub", "PubSub\PubSub.csproj", "{D1013AB1-F771-4FFF-AC9D-2B46B80BF9D6}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessageBatching", "MessageBatching\MessageBatching.csproj", "{0661D574-53D1-46B8-ADE1-92631F268195}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AtomicSends", "AtomicSends\AtomicSends.csproj", "{6BC3222D-169C-455E-880C-2C2E8CE1D4D0}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CrossEntityTransaction", "SendVia\CrossEntityTransaction.csproj", "{6521E347-381B-4579-ACBF-67C160F17C9D}" 25 | EndProject 26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TransferDLQ", "TransferDLQ\TransferDLQ.csproj", "{C28AA524-739F-49C1-B572-9ADB6A0F660D}" 27 | EndProject 28 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Topologies", "Topologies\Topologies.csproj", "{2293AA45-CD6E-4B10-9E21-7F86BE066377}" 29 | EndProject 30 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessageSessions", "MessageSessions\MessageSessions.csproj", "{D1E1D92E-5881-455F-B02F-0B785926AA47}" 31 | EndProject 32 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessageDeduplication", "MessageDeduplication\MessageDeduplication.csproj", "{58574C25-62F4-45F6-A24E-8CA580C2CDBD}" 33 | EndProject 34 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugins", "Plugins\Plugins.csproj", "{F0B8C4DD-B0DB-462B-98C6-D2960FB8BBDE}" 35 | EndProject 36 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Receiving", "Receiving\Receiving.csproj", "{D5E4B0C0-65A2-4DB3-9C13-3AA7F30B6BF6}" 37 | EndProject 38 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution Files", "{5DC072F9-4AAD-4887-B8F6-350660854409}" 39 | ProjectSection(SolutionItems) = preProject 40 | Directory.Build.props = Directory.Build.props 41 | EndProjectSection 42 | EndProject 43 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeBatching", "SafeBatching\SafeBatching.csproj", "{FACA55A4-95F6-47E3-B886-97E76A30185E}" 44 | EndProject 45 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BindaryData", "BindaryData\BindaryData.csproj", "{B043EA13-95D1-4D0A-9514-26926221E814}" 46 | EndProject 47 | Global 48 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 49 | Debug|Any CPU = Debug|Any CPU 50 | Release|Any CPU = Release|Any CPU 51 | EndGlobalSection 52 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 53 | {706CF99B-4682-4E5A-9884-84B3B2318FD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 54 | {706CF99B-4682-4E5A-9884-84B3B2318FD5}.Debug|Any CPU.Build.0 = Debug|Any CPU 55 | {706CF99B-4682-4E5A-9884-84B3B2318FD5}.Release|Any CPU.ActiveCfg = Release|Any CPU 56 | {706CF99B-4682-4E5A-9884-84B3B2318FD5}.Release|Any CPU.Build.0 = Release|Any CPU 57 | {DBEAEDAC-C076-4D73-8F00-330E69E228B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 58 | {DBEAEDAC-C076-4D73-8F00-330E69E228B6}.Debug|Any CPU.Build.0 = Debug|Any CPU 59 | {DBEAEDAC-C076-4D73-8F00-330E69E228B6}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {DBEAEDAC-C076-4D73-8F00-330E69E228B6}.Release|Any CPU.Build.0 = Release|Any CPU 61 | {9AC9AE46-0F0A-4AC0-A37D-930FEAD8A8A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 62 | {9AC9AE46-0F0A-4AC0-A37D-930FEAD8A8A0}.Debug|Any CPU.Build.0 = Debug|Any CPU 63 | {9AC9AE46-0F0A-4AC0-A37D-930FEAD8A8A0}.Release|Any CPU.ActiveCfg = Release|Any CPU 64 | {9AC9AE46-0F0A-4AC0-A37D-930FEAD8A8A0}.Release|Any CPU.Build.0 = Release|Any CPU 65 | {D93C1D5A-0D19-41F4-BCF9-90008DF5C4BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 66 | {D93C1D5A-0D19-41F4-BCF9-90008DF5C4BF}.Debug|Any CPU.Build.0 = Debug|Any CPU 67 | {D93C1D5A-0D19-41F4-BCF9-90008DF5C4BF}.Release|Any CPU.ActiveCfg = Release|Any CPU 68 | {D93C1D5A-0D19-41F4-BCF9-90008DF5C4BF}.Release|Any CPU.Build.0 = Release|Any CPU 69 | {9A039549-7569-4253-A43D-448E590AD772}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 70 | {9A039549-7569-4253-A43D-448E590AD772}.Debug|Any CPU.Build.0 = Debug|Any CPU 71 | {9A039549-7569-4253-A43D-448E590AD772}.Release|Any CPU.ActiveCfg = Release|Any CPU 72 | {9A039549-7569-4253-A43D-448E590AD772}.Release|Any CPU.Build.0 = Release|Any CPU 73 | {8E44CF95-976E-4B5C-8CC6-1BA2D5D4F993}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 74 | {8E44CF95-976E-4B5C-8CC6-1BA2D5D4F993}.Debug|Any CPU.Build.0 = Debug|Any CPU 75 | {8E44CF95-976E-4B5C-8CC6-1BA2D5D4F993}.Release|Any CPU.ActiveCfg = Release|Any CPU 76 | {8E44CF95-976E-4B5C-8CC6-1BA2D5D4F993}.Release|Any CPU.Build.0 = Release|Any CPU 77 | {D1013AB1-F771-4FFF-AC9D-2B46B80BF9D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 78 | {D1013AB1-F771-4FFF-AC9D-2B46B80BF9D6}.Debug|Any CPU.Build.0 = Debug|Any CPU 79 | {D1013AB1-F771-4FFF-AC9D-2B46B80BF9D6}.Release|Any CPU.ActiveCfg = Release|Any CPU 80 | {D1013AB1-F771-4FFF-AC9D-2B46B80BF9D6}.Release|Any CPU.Build.0 = Release|Any CPU 81 | {0661D574-53D1-46B8-ADE1-92631F268195}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 82 | {0661D574-53D1-46B8-ADE1-92631F268195}.Debug|Any CPU.Build.0 = Debug|Any CPU 83 | {0661D574-53D1-46B8-ADE1-92631F268195}.Release|Any CPU.ActiveCfg = Release|Any CPU 84 | {0661D574-53D1-46B8-ADE1-92631F268195}.Release|Any CPU.Build.0 = Release|Any CPU 85 | {6BC3222D-169C-455E-880C-2C2E8CE1D4D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 86 | {6BC3222D-169C-455E-880C-2C2E8CE1D4D0}.Debug|Any CPU.Build.0 = Debug|Any CPU 87 | {6BC3222D-169C-455E-880C-2C2E8CE1D4D0}.Release|Any CPU.ActiveCfg = Release|Any CPU 88 | {6BC3222D-169C-455E-880C-2C2E8CE1D4D0}.Release|Any CPU.Build.0 = Release|Any CPU 89 | {6521E347-381B-4579-ACBF-67C160F17C9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 90 | {6521E347-381B-4579-ACBF-67C160F17C9D}.Debug|Any CPU.Build.0 = Debug|Any CPU 91 | {6521E347-381B-4579-ACBF-67C160F17C9D}.Release|Any CPU.ActiveCfg = Release|Any CPU 92 | {6521E347-381B-4579-ACBF-67C160F17C9D}.Release|Any CPU.Build.0 = Release|Any CPU 93 | {C28AA524-739F-49C1-B572-9ADB6A0F660D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 94 | {C28AA524-739F-49C1-B572-9ADB6A0F660D}.Debug|Any CPU.Build.0 = Debug|Any CPU 95 | {C28AA524-739F-49C1-B572-9ADB6A0F660D}.Release|Any CPU.ActiveCfg = Release|Any CPU 96 | {C28AA524-739F-49C1-B572-9ADB6A0F660D}.Release|Any CPU.Build.0 = Release|Any CPU 97 | {2293AA45-CD6E-4B10-9E21-7F86BE066377}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 98 | {2293AA45-CD6E-4B10-9E21-7F86BE066377}.Debug|Any CPU.Build.0 = Debug|Any CPU 99 | {2293AA45-CD6E-4B10-9E21-7F86BE066377}.Release|Any CPU.ActiveCfg = Release|Any CPU 100 | {2293AA45-CD6E-4B10-9E21-7F86BE066377}.Release|Any CPU.Build.0 = Release|Any CPU 101 | {D1E1D92E-5881-455F-B02F-0B785926AA47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 102 | {D1E1D92E-5881-455F-B02F-0B785926AA47}.Debug|Any CPU.Build.0 = Debug|Any CPU 103 | {D1E1D92E-5881-455F-B02F-0B785926AA47}.Release|Any CPU.ActiveCfg = Release|Any CPU 104 | {D1E1D92E-5881-455F-B02F-0B785926AA47}.Release|Any CPU.Build.0 = Release|Any CPU 105 | {58574C25-62F4-45F6-A24E-8CA580C2CDBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 106 | {58574C25-62F4-45F6-A24E-8CA580C2CDBD}.Debug|Any CPU.Build.0 = Debug|Any CPU 107 | {58574C25-62F4-45F6-A24E-8CA580C2CDBD}.Release|Any CPU.ActiveCfg = Release|Any CPU 108 | {58574C25-62F4-45F6-A24E-8CA580C2CDBD}.Release|Any CPU.Build.0 = Release|Any CPU 109 | {F0B8C4DD-B0DB-462B-98C6-D2960FB8BBDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 110 | {F0B8C4DD-B0DB-462B-98C6-D2960FB8BBDE}.Debug|Any CPU.Build.0 = Debug|Any CPU 111 | {F0B8C4DD-B0DB-462B-98C6-D2960FB8BBDE}.Release|Any CPU.ActiveCfg = Release|Any CPU 112 | {F0B8C4DD-B0DB-462B-98C6-D2960FB8BBDE}.Release|Any CPU.Build.0 = Release|Any CPU 113 | {D5E4B0C0-65A2-4DB3-9C13-3AA7F30B6BF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 114 | {D5E4B0C0-65A2-4DB3-9C13-3AA7F30B6BF6}.Debug|Any CPU.Build.0 = Debug|Any CPU 115 | {D5E4B0C0-65A2-4DB3-9C13-3AA7F30B6BF6}.Release|Any CPU.ActiveCfg = Release|Any CPU 116 | {D5E4B0C0-65A2-4DB3-9C13-3AA7F30B6BF6}.Release|Any CPU.Build.0 = Release|Any CPU 117 | {FACA55A4-95F6-47E3-B886-97E76A30185E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 118 | {FACA55A4-95F6-47E3-B886-97E76A30185E}.Debug|Any CPU.Build.0 = Debug|Any CPU 119 | {FACA55A4-95F6-47E3-B886-97E76A30185E}.Release|Any CPU.ActiveCfg = Release|Any CPU 120 | {FACA55A4-95F6-47E3-B886-97E76A30185E}.Release|Any CPU.Build.0 = Release|Any CPU 121 | {B043EA13-95D1-4D0A-9514-26926221E814}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 122 | {B043EA13-95D1-4D0A-9514-26926221E814}.Debug|Any CPU.Build.0 = Debug|Any CPU 123 | {B043EA13-95D1-4D0A-9514-26926221E814}.Release|Any CPU.ActiveCfg = Release|Any CPU 124 | {B043EA13-95D1-4D0A-9514-26926221E814}.Release|Any CPU.Build.0 = Release|Any CPU 125 | EndGlobalSection 126 | GlobalSection(SolutionProperties) = preSolution 127 | HideSolutionNode = FALSE 128 | EndGlobalSection 129 | GlobalSection(ExtensibilityGlobals) = postSolution 130 | SolutionGuid = {98C73E09-06C3-4B1D-AD1C-B0ABD99D20E9} 131 | EndGlobalSection 132 | EndGlobal 133 | -------------------------------------------------------------------------------- /BindaryData/BindaryData.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /BindaryData/Program.cs: -------------------------------------------------------------------------------- 1 | var dataString = new BinaryData("Loki"); 2 | Console.WriteLine(dataString); 3 | 4 | var person = new Person("Loki", "Sanekat-Feldman"); 5 | var data = new BinaryData(person); 6 | Console.WriteLine(data.ToString()); 7 | 8 | var deserialized = data.ToObjectFromJson(); 9 | Console.WriteLine(deserialized.ToString()); 10 | 11 | record Person(string FirstName, string LastName); -------------------------------------------------------------------------------- /ConnectionsManagement/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Connections.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /ConnectionsManagement/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/ConnectionsManagement.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/ConnectionsManagement.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/ConnectionsManagement.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /ConnectionsManagement/ConnectionsManagement.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ConnectionsManagement/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace ConnectionsManagement 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /ConnectionsManagement/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Azure.Messaging.ServiceBus.Administration; 3 | 4 | namespace AtomicSends; 5 | 6 | public static class Prepare 7 | { 8 | public static async Task Infrastructure(string connectionString, string destination) 9 | { 10 | var client = new ServiceBusAdministrationClient(connectionString); 11 | if (await client.QueueExistsAsync(destination)) 12 | { 13 | await client.DeleteQueueAsync(destination); 14 | } 15 | await client.CreateQueueAsync(destination); 16 | } 17 | } -------------------------------------------------------------------------------- /ConnectionsManagement/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using AtomicSends; 4 | using Azure.Messaging.ServiceBus; 5 | 6 | namespace ConnectionsManagement; 7 | 8 | internal class Program 9 | { 10 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 11 | 12 | private static readonly string destination = "queue"; 13 | 14 | private static async Task Main(string[] args) 15 | { 16 | await Prepare.Infrastructure(connectionString, destination); 17 | 18 | Console.WriteLine("Using shared ServiceBusClient"); 19 | 20 | { 21 | var serviceBusClient = new ServiceBusClient(connectionString); 22 | 23 | var sender = serviceBusClient.CreateSender(destination); 24 | await sender.SendMessageAsync(new ServiceBusMessage("Deep Dive")); 25 | var receiver = serviceBusClient.CreateReceiver(destination); 26 | await receiver.ReceiveMessageAsync(); 27 | 28 | Console.WriteLine("Press Enter to use different ServiceBusClients"); 29 | Console.ReadLine(); 30 | 31 | await sender.CloseAsync(); 32 | await receiver.CloseAsync(); 33 | await serviceBusClient.DisposeAsync(); 34 | 35 | GC.Collect(); 36 | } 37 | 38 | { 39 | var serviceBusClient1 = new ServiceBusClient(connectionString); 40 | var serviceBusClient2 = new ServiceBusClient(connectionString); 41 | 42 | var sender = serviceBusClient1.CreateSender(destination); 43 | await sender.SendMessageAsync(new ServiceBusMessage("Deep Dive")); 44 | var receiver = serviceBusClient2.CreateReceiver(destination); 45 | await receiver.ReceiveMessageAsync(); 46 | 47 | Console.WriteLine("Enter to stop"); 48 | Console.ReadLine(); 49 | 50 | await sender.CloseAsync(); 51 | await receiver.CloseAsync(); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | latest 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MessageBatching/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Batching.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /MessageBatching/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/MessageBatching.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/MessageBatching.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/MessageBatching.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /MessageBatching/MessageBatching.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MessageBatching/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace MessageBatching 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /MessageBatching/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Azure.Messaging.ServiceBus.Administration; 4 | 5 | namespace MessageBatching; 6 | 7 | public static class Prepare 8 | { 9 | public static async Task Infrastructure(string connectionString, string destination) 10 | { 11 | var client = new ServiceBusAdministrationClient(connectionString); 12 | if (await client.QueueExistsAsync(destination)) 13 | { 14 | await client.DeleteQueueAsync(destination); 15 | } 16 | await client.CreateQueueAsync(destination); 17 | 18 | NamespaceProperties namespaceProperties = await client.GetNamespacePropertiesAsync(); 19 | Console.WriteLine($"Namespace '{namespaceProperties.Name}' info"); 20 | Console.WriteLine($"SKU: {namespaceProperties.MessagingSku}"); 21 | Console.WriteLine($"Messaging units: {namespaceProperties.MessagingUnits}\n"); 22 | } 23 | } -------------------------------------------------------------------------------- /MessageBatching/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Azure.Messaging.ServiceBus; 5 | 6 | namespace MessageBatching; 7 | 8 | internal class Program 9 | { 10 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 11 | 12 | private static readonly string destination = "queue"; 13 | 14 | private static async Task Main(string[] args) 15 | { 16 | await Prepare.Infrastructure(connectionString, destination); 17 | 18 | var serviceBusClient = new ServiceBusClient(connectionString); 19 | 20 | var sender = serviceBusClient.CreateSender(destination); 21 | 22 | var messages = new List(); 23 | for (var i = 0; i < 10; i++) 24 | { 25 | var message = new ServiceBusMessage($"Deep Dive{i}"); 26 | messages.Add(message); 27 | } 28 | 29 | Console.WriteLine($"Sending {messages.Count} messages in a batch."); 30 | await sender.SendMessagesAsync(messages); 31 | messages.Clear(); 32 | Console.WriteLine(); 33 | 34 | for (var i = 0; i < 6500; i++) 35 | { 36 | var message = new ServiceBusMessage($"Deep Dive{i}"); 37 | messages.Add(message); 38 | } 39 | 40 | await sender.SendMessagesAsync(messages); 41 | 42 | try 43 | { 44 | Console.WriteLine($"Sending {messages.Count} messages in a batch."); 45 | await sender.SendMessagesAsync(messages); 46 | } 47 | catch (ServiceBusException ex) 48 | { 49 | Console.Error.WriteLine($"{ex.Reason}: {ex.Message}"); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /MessageDeadlettering/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Deadlettering.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /MessageDeadlettering/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/MessageDeadlettering.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/MessageDeadlettering.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/MessageDeadlettering.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /MessageDeadlettering/MessageDeadlettering.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MessageDeadlettering/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace MessageDeadlettering 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /MessageDeadlettering/Prepare.cs: -------------------------------------------------------------------------------- 1 | using Azure.Messaging.ServiceBus.Administration; 2 | 3 | namespace MessageDeadlettering; 4 | 5 | using System.Threading.Tasks; 6 | 7 | public static class Prepare 8 | { 9 | public static async Task Infrastructure(string connectionString, string destination) 10 | { 11 | var client = new ServiceBusAdministrationClient(connectionString); 12 | if (await client.QueueExistsAsync(destination)) 13 | { 14 | await client.DeleteQueueAsync(destination); 15 | } 16 | 17 | var createQueueOptions = new CreateQueueOptions(destination) 18 | { 19 | DeadLetteringOnMessageExpiration = true, // default false 20 | MaxDeliveryCount = 1 21 | }; 22 | await client.CreateQueueAsync(createQueueOptions); 23 | } 24 | } -------------------------------------------------------------------------------- /MessageDeadlettering/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Azure.Messaging.ServiceBus; 5 | 6 | namespace MessageDeadlettering; 7 | 8 | internal class Program 9 | { 10 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 11 | 12 | private static readonly string destination = "queue"; 13 | 14 | private static async Task Main(string[] args) 15 | { 16 | await Prepare.Infrastructure(connectionString, destination); 17 | 18 | var serviceBusClient = new ServiceBusClient(connectionString); 19 | 20 | var sender = serviceBusClient.CreateSender(destination); 21 | 22 | var message1 = new ServiceBusMessage("Deep Dive 1"); 23 | message1.Subject = "first"; 24 | message1.TimeToLive = TimeSpan.FromSeconds(1); 25 | await sender.SendMessageAsync(message1); 26 | Console.WriteLine("Sent first message"); 27 | 28 | var message2 = new ServiceBusMessage("Deep Dive 2"); 29 | message2.Subject = "second"; 30 | await sender.SendMessageAsync(message2); 31 | Console.WriteLine("Sent second message"); 32 | 33 | var message3 = new ServiceBusMessage("Deep Dive 3"); 34 | message3.Subject = "third"; 35 | await sender.SendMessageAsync(message3); 36 | Console.WriteLine("Sent third message"); 37 | 38 | await Task.Delay(2_000); 39 | 40 | var processor = serviceBusClient.CreateProcessor(destination, 41 | new ServiceBusProcessorOptions { AutoCompleteMessages = false, MaxConcurrentCalls = 3 }); 42 | 43 | processor.ProcessMessageAsync += async args => 44 | { 45 | switch (args.Message.Subject) 46 | { 47 | case "first": 48 | throw new InvalidOperationException("Should never received the first message."); 49 | 50 | case "second": 51 | await args.AbandonMessageAsync(args.Message); 52 | Console.WriteLine("Abandon the second message --> delivery count will exceed the maximum"); 53 | break; 54 | 55 | case "third": 56 | Console.WriteLine("Dead-letter the third message explicitly"); 57 | await args.DeadLetterMessageAsync(args.Message, new Dictionary 58 | { 59 | { "Reason", "Because we can!" }, 60 | { "When", DateTime.UtcNow } 61 | }); 62 | break; 63 | } 64 | }; 65 | 66 | try 67 | { 68 | await processor.StartProcessingAsync(); 69 | 70 | Console.ReadLine(); 71 | } 72 | catch (Exception ex) 73 | { 74 | Console.Error.WriteLine(ex.Message); 75 | } 76 | finally 77 | { 78 | await processor.DisposeAsync(); 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /MessageDeduplication/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Dedup.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /MessageDeduplication/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/MessageDeduplication.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/MessageDeduplication.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/MessageDeduplication.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /MessageDeduplication/MessageDeduplication.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MessageDeduplication/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace MessageDeduplication 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /MessageDeduplication/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Azure.Messaging.ServiceBus.Administration; 4 | 5 | namespace MessageDeduplication; 6 | 7 | public static class Prepare 8 | { 9 | public static async Task Stage(string connectionString, string destination) 10 | { 11 | var client = new ServiceBusAdministrationClient(connectionString); 12 | if (await client.QueueExistsAsync(destination)) 13 | { 14 | await client.DeleteQueueAsync(destination); 15 | } 16 | 17 | var createQueueOptions = new CreateQueueOptions(destination) 18 | { 19 | RequiresDuplicateDetection = true, 20 | DuplicateDetectionHistoryTimeWindow = TimeSpan.FromSeconds(20) 21 | }; 22 | 23 | await client.CreateQueueAsync(createQueueOptions); 24 | } 25 | } -------------------------------------------------------------------------------- /MessageDeduplication/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Azure.Messaging.ServiceBus; 7 | 8 | namespace MessageDeduplication; 9 | 10 | internal class Program 11 | { 12 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 13 | 14 | private static readonly string destination = "queue"; 15 | 16 | private static async Task Main(string[] args) 17 | { 18 | await Prepare.Stage(connectionString, destination); 19 | 20 | var serviceBusClient = new ServiceBusClient(connectionString); 21 | 22 | var sender = serviceBusClient.CreateSender(destination); 23 | try 24 | { 25 | var content = "Deep Dive de-duplication"; 26 | var messageId = new Guid(content.Take(16).Select(x => (byte)x).ToArray()).ToString(); 27 | 28 | var messages = new List 29 | { 30 | new(content) { MessageId = messageId }, 31 | new(content) { MessageId = messageId }, 32 | new(content) { MessageId = messageId } 33 | }; 34 | 35 | await sender.SendMessagesAsync(messages); 36 | 37 | Console.WriteLine("Messages sent"); 38 | 39 | var processor = serviceBusClient.CreateProcessor(destination); 40 | 41 | processor.ProcessMessageAsync += args => 42 | { 43 | Console.WriteLine($"Received message with '{args.Message.MessageId}' and content '{Encoding.UTF8.GetString(args.Message.Body)}'"); 44 | 45 | return Task.CompletedTask; 46 | }; 47 | 48 | processor.ProcessErrorAsync += args => 49 | { 50 | Console.WriteLine($"Exception: {args.Exception}"); 51 | return Task.CompletedTask; 52 | }; 53 | 54 | await processor.StartProcessingAsync(); 55 | 56 | await Task.Delay(TimeSpan.FromSeconds(5)); 57 | } 58 | finally 59 | { 60 | await sender.CloseAsync(); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /MessageExpiry/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Expiry.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /MessageExpiry/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/MessageExpiry.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/MessageExpiry.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/MessageExpiry.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /MessageExpiry/MessageExpiry.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MessageExpiry/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace MessageExpiry 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /MessageExpiry/Prepare.cs: -------------------------------------------------------------------------------- 1 | namespace MessageExpiry; 2 | 3 | using Azure.Messaging.ServiceBus; 4 | using Azure.Messaging.ServiceBus.Administration; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | public static class Prepare 9 | { 10 | public static async Task Stage(string connectionString, string destination) 11 | { 12 | var client = new ServiceBusAdministrationClient(connectionString); 13 | if (await client.QueueExistsAsync(destination)) await client.DeleteQueueAsync(destination); 14 | 15 | var description = new CreateQueueOptions(destination) 16 | { 17 | MaxDeliveryCount = int.MaxValue 18 | }; 19 | await client.CreateQueueAsync(description); 20 | } 21 | 22 | public static async Task SimulateActiveReceiver(ServiceBusClient client, string entity) 23 | { 24 | var options = new ServiceBusProcessorOptions 25 | { 26 | AutoCompleteMessages = false, 27 | MaxConcurrentCalls = 1 28 | }; 29 | var processor = client.CreateProcessor(entity, options); 30 | 31 | processor.ProcessMessageAsync += async args => 32 | { 33 | await args.AbandonMessageAsync(args.Message); 34 | Console.WriteLine("(Emulating active receiver w/o receiving messages)"); 35 | await Task.Delay(TimeSpan.FromSeconds(5)); 36 | }; 37 | 38 | processor.ProcessErrorAsync += async args => 39 | { 40 | await Task.CompletedTask; 41 | }; 42 | 43 | await processor.StartProcessingAsync(); 44 | await Task.Delay(TimeSpan.FromSeconds(15)); 45 | } 46 | } -------------------------------------------------------------------------------- /MessageExpiry/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Azure.Messaging.ServiceBus; 4 | 5 | namespace MessageExpiry; 6 | internal class Program 7 | { 8 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 9 | 10 | private static readonly string destination = "queue"; 11 | 12 | private static async Task Main(string[] args) 13 | { 14 | await Prepare.Stage(connectionString, destination); 15 | 16 | var serviceBusClient = new ServiceBusClient(connectionString); 17 | 18 | var sender = serviceBusClient.CreateSender(destination); 19 | 20 | var message = new ServiceBusMessage("Deep Dive") 21 | { 22 | // if not set the default time to live on the queue counts 23 | TimeToLive = TimeSpan.FromSeconds(3) 24 | }; 25 | 26 | await sender.SendMessageAsync(message); 27 | Console.WriteLine("Message sent"); 28 | 29 | // Note that expired messages are only purged and moved to the DLQ when there is at least one 30 | // active receiver pulling from the main queue or subscription; that behavior is by design. 31 | await Prepare.SimulateActiveReceiver(serviceBusClient, destination); 32 | 33 | Console.WriteLine("Message must have expired by now"); 34 | 35 | await sender.CloseAsync(); 36 | } 37 | } -------------------------------------------------------------------------------- /MessageForwarding/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Fowarding.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /MessageForwarding/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/MessageForwarding.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/MessageForwarding.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/MessageForwarding.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /MessageForwarding/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace MessageForwarding 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /MessageForwarding/MessageForwarding.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MessageForwarding/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Azure; 3 | using Azure.Messaging.ServiceBus.Administration; 4 | 5 | namespace MessageForwarding; 6 | 7 | public static class Prepare 8 | { 9 | public static async Task Infrastructure(string connectionString) 10 | { 11 | var client = new ServiceBusAdministrationClient(connectionString); 12 | 13 | await Task.WhenAll( 14 | DeleteIfExists("queue"), 15 | DeleteIfExists("queue0"), 16 | DeleteIfExists("queue1"), 17 | DeleteIfExists("queue2"), 18 | DeleteIfExists("queue3"), 19 | DeleteIfExists("queue4"), 20 | DeleteIfExists("queue5") 21 | ); 22 | 23 | var options = new CreateQueueOptions("queue5"); 24 | await client.CreateQueueAsync(options); 25 | 26 | options = new CreateQueueOptions("queue4"); 27 | await client.CreateQueueAsync(options); 28 | 29 | options = new CreateQueueOptions("queue3") 30 | { 31 | ForwardTo = "queue4" 32 | }; 33 | await client.CreateQueueAsync(options); 34 | 35 | options = new CreateQueueOptions("queue2") 36 | { 37 | ForwardTo = "queue3" 38 | }; 39 | await client.CreateQueueAsync(options); 40 | 41 | options = new CreateQueueOptions("queue1") 42 | { 43 | ForwardTo = "queue2" 44 | }; 45 | await client.CreateQueueAsync(options); 46 | 47 | options = new CreateQueueOptions("queue0") 48 | { 49 | ForwardTo = "queue1" 50 | }; 51 | await client.CreateQueueAsync(options); 52 | 53 | async Task DeleteIfExists(string queueName) 54 | { 55 | if (await client.QueueExistsAsync(queueName)) 56 | { 57 | await client.DeleteQueueAsync(queueName); 58 | } 59 | } 60 | } 61 | 62 | public static async Task AddExtraHop(string connectionString) 63 | { 64 | var client = new ServiceBusAdministrationClient(connectionString); 65 | 66 | QueueProperties properties = await client.GetQueueAsync("queue4"); 67 | properties.ForwardTo = "queue5"; 68 | 69 | await client.UpdateQueueAsync(properties); 70 | } 71 | } -------------------------------------------------------------------------------- /MessageForwarding/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Azure.Messaging.ServiceBus; 4 | 5 | namespace MessageForwarding; 6 | 7 | internal class Program 8 | { 9 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 10 | 11 | private static async Task Main(string[] args) 12 | { 13 | await Prepare.Infrastructure(connectionString); 14 | 15 | var serviceBusClient = new ServiceBusClient(connectionString); 16 | 17 | var sender = serviceBusClient.CreateSender("queue0"); 18 | var message = new ServiceBusMessage("Deep Dive"); 19 | await sender.SendMessageAsync(message); 20 | await sender.CloseAsync(); 21 | 22 | Console.WriteLine("Message sent to 'queue0''"); 23 | 24 | var queue = "queue4"; 25 | var receiver = serviceBusClient.CreateReceiver(queue); 26 | 27 | var receivedMessage = await receiver.ReceiveMessageAsync(); 28 | await receiver.CompleteMessageAsync(receivedMessage); 29 | 30 | Console.WriteLine($"Got '{receivedMessage.Body}' on queue '{queue}'"); 31 | await receiver.CloseAsync(); 32 | Console.ReadLine(); 33 | Console.WriteLine("Add forwarding from 'queue4' to 'queue5''"); 34 | 35 | await Prepare.AddExtraHop(connectionString); 36 | 37 | sender = serviceBusClient.CreateSender("queue0"); 38 | message = new ServiceBusMessage("Forwarding"); 39 | await sender.SendMessageAsync(message); 40 | 41 | queue = "queue5"; 42 | receiver = serviceBusClient.CreateReceiver(queue); 43 | receivedMessage = await receiver.ReceiveMessageAsync(TimeSpan.FromSeconds(5)); 44 | 45 | var what = receivedMessage == null ? "nothing" : $"{receivedMessage.Body}"; 46 | Console.WriteLine($"Got {what} on queue '{queue}'"); 47 | 48 | await receiver.CloseAsync(); 49 | } 50 | } -------------------------------------------------------------------------------- /MessageScheduling/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Scheduling.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /MessageScheduling/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/MessageScheduling.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/MessageScheduling.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/MessageScheduling.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /MessageScheduling/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace MessageScheduling 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /MessageScheduling/MessageScheduling.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MessageScheduling/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Azure.Messaging.ServiceBus.Administration; 3 | 4 | namespace MessageScheduling; 5 | 6 | public static class Prepare 7 | { 8 | public static async Task Infrastructure(string connectionString, string destination) 9 | { 10 | var client = new ServiceBusAdministrationClient(connectionString); 11 | if (await client.QueueExistsAsync(destination)) 12 | { 13 | await client.DeleteQueueAsync(destination); 14 | } 15 | await client.CreateQueueAsync(destination); 16 | } 17 | } -------------------------------------------------------------------------------- /MessageScheduling/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Azure.Messaging.ServiceBus; 4 | 5 | namespace MessageScheduling; 6 | 7 | internal class Program 8 | { 9 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 10 | 11 | private static readonly string destination = "queue"; 12 | 13 | private static async Task Main(string[] args) 14 | { 15 | await Prepare.Infrastructure(connectionString, destination); 16 | 17 | var serviceBusClient = new ServiceBusClient(connectionString); 18 | 19 | var sender = serviceBusClient.CreateSender(destination); 20 | var due = DateTimeOffset.UtcNow.AddSeconds(10); 21 | 22 | await sender.ScheduleMessageAsync(new ServiceBusMessage($"Deep Dive 1 + {due}"), due); 23 | Console.WriteLine($"{DateTimeOffset.UtcNow}: Message scheduled first"); 24 | 25 | var sequenceId = await sender.ScheduleMessageAsync(new ServiceBusMessage($"Deep Dive 2 + {due}"), due); 26 | Console.WriteLine($"{DateTimeOffset.UtcNow}: Message scheduled second"); 27 | 28 | await sender.CancelScheduledMessageAsync(sequenceId); 29 | Console.WriteLine($"{DateTimeOffset.UtcNow}: Canceled second"); 30 | 31 | var receiver = serviceBusClient.CreateReceiver(destination); 32 | var message = await receiver.ReceiveMessageAsync(); 33 | Console.WriteLine($"{DateTimeOffset.UtcNow}: Received message with body: '{message.Body}'"); 34 | 35 | await sender.CloseAsync(); 36 | await receiver.CloseAsync(); 37 | } 38 | } -------------------------------------------------------------------------------- /MessageSessions/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Session.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /MessageSessions/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/MessageSessions.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/MessageSessions.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/MessageSessions.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /MessageSessions/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace MessageSessions 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /MessageSessions/MessageSessions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /MessageSessions/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Azure.Messaging.ServiceBus.Administration; 3 | 4 | namespace MessageSessions; 5 | 6 | public static class Prepare 7 | { 8 | public static async Task Infrastructure(string connectionString, string destination) 9 | { 10 | var client = new ServiceBusAdministrationClient(connectionString); 11 | if (await client.QueueExistsAsync(destination)) 12 | { 13 | await client.DeleteQueueAsync(destination); 14 | } 15 | 16 | var queueDescription = new CreateQueueOptions(destination) 17 | { 18 | RequiresSession = true 19 | }; 20 | await client.CreateQueueAsync(queueDescription); 21 | } 22 | } -------------------------------------------------------------------------------- /MessageSessions/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Azure.Messaging.ServiceBus; 5 | 6 | namespace MessageSessions; 7 | 8 | internal class Program 9 | { 10 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 11 | 12 | private static readonly string destination = "queue"; 13 | 14 | private static async Task Main(string[] args) 15 | { 16 | await Prepare.Infrastructure(connectionString, destination); 17 | 18 | var serviceBusClient = new ServiceBusClient(connectionString); 19 | 20 | try 21 | { 22 | var messages = new List 23 | { 24 | new("Orange 1") {SessionId = "Orange"}, 25 | new("Green 1") {SessionId = "Green"}, 26 | new("Blue 1") {SessionId = "Blue"}, 27 | new("Green 2") {SessionId = "Green"}, 28 | new("Orange 2") {SessionId = "Orange"}, 29 | new("Blue 2") {SessionId = "Blue"}, 30 | new("Green 3") {SessionId = "Green"}, 31 | new("Orange 3") {SessionId = "Orange"}, 32 | new("Green 4") {SessionId = "Green"}, 33 | new("Purple 1") {SessionId = "Purple"}, 34 | new("Blue 3") {SessionId = "Blue"}, 35 | new("Orange 4") {SessionId = "Orange"} 36 | }; 37 | 38 | var sender = serviceBusClient.CreateSender(destination); 39 | await sender.SendMessagesAsync(messages); 40 | await sender.DisposeAsync(); 41 | 42 | Console.WriteLine("Messages sent"); 43 | 44 | var options = new ServiceBusSessionProcessorOptions 45 | { 46 | MaxConcurrentSessions = 1, 47 | SessionIdleTimeout = TimeSpan.FromSeconds(2), 48 | //MaxConcurrentCallsPerSession = 10 49 | }; 50 | var sessionProcessor = serviceBusClient.CreateSessionProcessor(destination, options); 51 | 52 | sessionProcessor.ProcessMessageAsync += args => 53 | { 54 | Console.WriteLine($"Received message for session '{args.SessionId}' ID:'{args.Message.MessageId}' and content:'{args.Message.Body}'"); 55 | return Task.CompletedTask; 56 | }; 57 | 58 | sessionProcessor.ProcessErrorAsync += args => 59 | { 60 | Console.WriteLine($"EntityPath: {args.EntityPath}"); 61 | Console.WriteLine($"FullyQualifiedNamespace: {args.FullyQualifiedNamespace}"); 62 | Console.WriteLine($"ErrorSource: {args.ErrorSource}"); 63 | Console.WriteLine($"Exception: {args.Exception}"); 64 | return Task.CompletedTask; 65 | }; 66 | 67 | await sessionProcessor.StartProcessingAsync(); 68 | 69 | Console.ReadLine(); 70 | } 71 | finally 72 | { 73 | await serviceBusClient.DisposeAsync(); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /Plugins/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Plugin.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /Plugins/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/Plugins.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/Plugins.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/Plugins.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /Plugins/PluggableServiceBusReceiver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Azure.Messaging.ServiceBus; 6 | 7 | namespace Plugins; 8 | 9 | public class PluggableServiceBusReceiver : ServiceBusReceiver 10 | { 11 | private readonly IEnumerable> _plugins; 12 | 13 | internal PluggableServiceBusReceiver(ServiceBusClient client, string queueOrSubscriptionName, IEnumerable> plugins, ServiceBusReceiverOptions options) 14 | : base(client, queueOrSubscriptionName, options) 15 | { 16 | _plugins = plugins; 17 | } 18 | 19 | public override async Task ReceiveMessageAsync(TimeSpan? maxWaitTime = null, CancellationToken cancellationToken = new CancellationToken()) 20 | { 21 | var message = await base.ReceiveMessageAsync(maxWaitTime, cancellationToken); 22 | 23 | foreach (var plugin in _plugins) 24 | { 25 | await plugin.Invoke(message); 26 | } 27 | 28 | return message; 29 | } 30 | } -------------------------------------------------------------------------------- /Plugins/PluggableServiceBusSender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Azure.Messaging.ServiceBus; 6 | 7 | namespace Plugins; 8 | 9 | public class PluggableServiceBusSender : ServiceBusSender 10 | { 11 | private readonly IEnumerable> _plugins; 12 | 13 | internal PluggableServiceBusSender(ServiceBusClient client, string queueOrTopicName, IEnumerable> plugins) 14 | : base(client, queueOrTopicName) 15 | { 16 | _plugins = plugins; 17 | } 18 | 19 | public override async Task SendMessageAsync(ServiceBusMessage message, CancellationToken cancellationToken = default) 20 | { 21 | foreach (var plugin in _plugins) 22 | { 23 | await plugin.Invoke(message); 24 | } 25 | await base.SendMessageAsync(message, cancellationToken).ConfigureAwait(false); 26 | } 27 | } -------------------------------------------------------------------------------- /Plugins/Plugins.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using Azure.Core.Amqp; 6 | using Azure.Messaging.ServiceBus; 7 | 8 | namespace Plugins; 9 | 10 | public static class Plugins 11 | { 12 | public static Func PrefixPlugin => message => 13 | { 14 | message.Body = new BinaryData($"---PREFIX---{Environment.NewLine}{message.Body}"); 15 | 16 | return Task.CompletedTask; 17 | }; 18 | 19 | public static Func PostfixPlugin => message => 20 | { 21 | var amqpMessage = message.GetRawAmqpMessage(); 22 | 23 | amqpMessage.Body.TryGetData(out var data); 24 | amqpMessage.Body.TryGetValue(out var value); 25 | 26 | // WARNING - more sophisticated than this 27 | // See https://github.com/Azure/azure-sdk-for-net/issues/12943 for discussion 28 | var original = Encoding.UTF8.GetString(data.First().Span); 29 | var modified = $"{original}{Environment.NewLine}---SUFFIX---"; 30 | var bytes = Encoding.UTF8.GetBytes(modified); 31 | 32 | amqpMessage.Body = new AmqpMessageBody(new ReadOnlyMemory[] { new(bytes) }); 33 | 34 | return Task.CompletedTask; 35 | }; 36 | } -------------------------------------------------------------------------------- /Plugins/Plugins.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Plugins/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Azure.Messaging.ServiceBus; 4 | using Azure.Messaging.ServiceBus.Administration; 5 | 6 | namespace Plugins; 7 | 8 | public static class Prepare 9 | { 10 | public static readonly ServiceBusProcessorOptions Options = new() 11 | { 12 | AutoCompleteMessages = true, 13 | MaxConcurrentCalls = 1, 14 | MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(10) 15 | }; 16 | 17 | public static async Task Infrastructure(string connectionString, string destination) 18 | { 19 | var client = new ServiceBusAdministrationClient(connectionString); 20 | if (await client.QueueExistsAsync(destination)) 21 | { 22 | await client.DeleteQueueAsync(destination); 23 | } 24 | await client.CreateQueueAsync(destination); 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /Plugins/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Azure.Messaging.ServiceBus; 5 | 6 | namespace Plugins; 7 | 8 | internal class Program 9 | { 10 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 11 | private static readonly string queue = "queue"; 12 | 13 | private static async Task Main(string[] args) 14 | { 15 | await Prepare.Infrastructure(connectionString, queue); 16 | 17 | var serviceBusClient = new ServiceBusClient(connectionString); 18 | 19 | var sender = serviceBusClient.CreateSender(queue, new List> 20 | { 21 | Plugins.PrefixPlugin 22 | }); 23 | 24 | await sender.SendMessageAsync(new ServiceBusMessage("Deep Dive")); 25 | Console.WriteLine("Message sent"); 26 | 27 | var receiver = serviceBusClient.CreateReceiver(queue, new List> 28 | { 29 | Plugins.PostfixPlugin 30 | }); 31 | 32 | var message = await receiver.ReceiveMessageAsync(); 33 | Console.WriteLine($"Received message ID:{message.MessageId} and content:\n{message.Body}"); 34 | 35 | await serviceBusClient.DisposeAsync(); 36 | } 37 | } -------------------------------------------------------------------------------- /Plugins/ServiceBusClientExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Azure.Messaging.ServiceBus; 5 | 6 | namespace Plugins; 7 | 8 | public static class ServiceBusClientExtensions 9 | { 10 | public static PluggableServiceBusSender CreateSender(this ServiceBusClient client, string queueOrTopicName, IEnumerable> plugins) => 11 | new PluggableServiceBusSender(client, queueOrTopicName, plugins); 12 | 13 | public static PluggableServiceBusReceiver CreateReceiver(this ServiceBusClient client, string queueOrSubscriptionName, IEnumerable> plugins, ServiceBusReceiverOptions options = default) => 14 | new PluggableServiceBusReceiver(client, queueOrSubscriptionName, plugins, options ?? new ServiceBusReceiverOptions()); 15 | } -------------------------------------------------------------------------------- /PubSub/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Pubsub.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /PubSub/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/PubSub.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/PubSub.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/PubSub.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /PubSub/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace PubSub 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /PubSub/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Azure.Messaging.ServiceBus.Administration; 3 | 4 | namespace PubSub; 5 | 6 | public static class Prepare 7 | { 8 | public static async Task Infrastructure(string connectionString, string topicName, string rushSubscription, string currencySubscription) 9 | { 10 | var client = await Cleanup(connectionString, topicName, rushSubscription, currencySubscription); 11 | 12 | var subscriptionOptions = new CreateSubscriptionOptions(topicName, rushSubscription); 13 | await client.CreateSubscriptionAsync(subscriptionOptions); 14 | 15 | subscriptionOptions = new CreateSubscriptionOptions(topicName, currencySubscription); 16 | await client.CreateSubscriptionAsync(subscriptionOptions); 17 | 18 | await client.DeleteRuleAsync(topicName, rushSubscription, "$Default"); 19 | await client.DeleteRuleAsync(topicName, currencySubscription, "$Default"); 20 | 21 | var ruleOptions = new CreateRuleOptions 22 | { 23 | Name = "MessagesWithRushlabel", 24 | Filter = new CorrelationRuleFilter 25 | { 26 | Subject = "rush" 27 | }, 28 | Action = null 29 | }; 30 | await client.CreateRuleAsync(topicName, rushSubscription, ruleOptions); 31 | 32 | ruleOptions = new CreateRuleOptions 33 | { 34 | Name = "MessagesWithCurrencyGBP", 35 | Filter = new SqlRuleFilter("currency = 'GBP' OR currency = '£'"), 36 | Action = new SqlRuleAction("SET status = 'Keep calm and carry on'") 37 | }; 38 | await client.CreateRuleAsync(topicName, currencySubscription, ruleOptions); 39 | } 40 | 41 | private static async Task Cleanup(string connectionString, string topicName, string rushSubscription, string currencySubscription) 42 | { 43 | var client = new ServiceBusAdministrationClient(connectionString); 44 | 45 | if (await client.SubscriptionExistsAsync(topicName, rushSubscription)) 46 | { 47 | await client.DeleteSubscriptionAsync(topicName, rushSubscription); 48 | } 49 | 50 | if (await client.SubscriptionExistsAsync(topicName, currencySubscription)) 51 | { 52 | await client.DeleteSubscriptionAsync(topicName, currencySubscription); 53 | } 54 | 55 | if (await client.TopicExistsAsync(topicName)) 56 | { 57 | await client.DeleteTopicAsync(topicName); 58 | } 59 | 60 | var topicDescription = new CreateTopicOptions(topicName); 61 | await client.CreateTopicAsync(topicDescription); 62 | 63 | return client; 64 | } 65 | } -------------------------------------------------------------------------------- /PubSub/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Azure.Messaging.ServiceBus; 4 | 5 | namespace PubSub; 6 | 7 | internal class Program 8 | { 9 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 10 | 11 | private static readonly string topicName = "topic"; 12 | private static readonly string rushSubscription = "alwaysInRush"; 13 | private static readonly string currencySubscription = "maybeRich"; 14 | 15 | private static async Task Main(string[] args) 16 | { 17 | await Prepare.Infrastructure(connectionString, topicName, rushSubscription, currencySubscription); 18 | 19 | var serviceBusClient = new ServiceBusClient(connectionString); 20 | 21 | var sender = serviceBusClient.CreateSender(topicName); 22 | 23 | var message = new ServiceBusMessage("No time, gotta rush!") { Subject = "rush" }; 24 | await sender.SendMessageAsync(message); 25 | 26 | message = new ServiceBusMessage("I'm rich! I have 1,000!") { Subject = "rush" }; 27 | message.ApplicationProperties.Add("currency", "GBP"); 28 | await sender.SendMessageAsync(message); 29 | 30 | await sender.CloseAsync(); 31 | 32 | Console.WriteLine("Messages sent"); 33 | } 34 | } -------------------------------------------------------------------------------- /PubSub/PubSub.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Azure Service Bus - Deep Dive 2 | 3 | Messaging is the backbone of many systems. Not all messaging platforms are born the same. Some are less sophisticated, some are more. Some are so advanced it's like taking a racing car for a spin; powerful and thrilling at the same time. Say hello to Azure Service Bus. 4 | 5 | In this session, you will learn what Service Bus messaging has to offer and why it could become the next cloud service you want to use. 6 | 7 | # Posts on Azure Service Bus 8 | 9 | - [My blog](https://weblogs.asp.net/sfeldman/Tags/AzureServiceBus) 10 | - [Official documentation](https://azure.microsoft.com/en-ca/services/service-bus/) 11 | -------------------------------------------------------------------------------- /Receiving/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Receive.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /Receiving/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/Receiving.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/Receiving.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/Receiving.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /Receiving/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Receiving 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Receiving/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Azure.Messaging.ServiceBus.Administration; 3 | 4 | namespace Receiving; 5 | 6 | public static class Prepare 7 | { 8 | public static async Task Infrastructure(string connectionString, string destination) 9 | { 10 | var client = new ServiceBusAdministrationClient(connectionString); 11 | if (await client.QueueExistsAsync(destination)) 12 | { 13 | await client.DeleteQueueAsync(destination); 14 | } 15 | await client.CreateQueueAsync(destination); 16 | } 17 | } -------------------------------------------------------------------------------- /Receiving/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Azure.Messaging.ServiceBus; 4 | 5 | namespace Receiving; 6 | 7 | 8 | internal class Program 9 | { 10 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 11 | 12 | private static readonly string destination = "queue"; 13 | 14 | private static readonly TaskCompletionSource syncEvent = new(TaskCreationOptions.RunContinuationsAsynchronously); 15 | 16 | private static async Task Main(string[] args) 17 | { 18 | await Prepare.Infrastructure(connectionString, destination); 19 | 20 | var serviceBusClient = new ServiceBusClient(connectionString); 21 | 22 | var sender = serviceBusClient.CreateSender(destination); 23 | try 24 | { 25 | await sender.SendMessageAsync(new ServiceBusMessage("Deep Dive")); 26 | Console.WriteLine("Message sent"); 27 | 28 | var options = new ServiceBusProcessorOptions 29 | { 30 | AutoCompleteMessages = false, 31 | MaxConcurrentCalls = 1, 32 | MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(10), 33 | // PrefetchCount = 10, 34 | // ReceiveMode = ServiceBusReceiveMode.ReceiveAndDelete, 35 | // SubQueue = SubQueue.DeadLetter 36 | }; 37 | 38 | var processor = serviceBusClient.CreateProcessor(destination, options); 39 | 40 | processor.ProcessMessageAsync += async (ProcessMessageEventArgs x) => 41 | { 42 | Console.WriteLine($"Received message with '{x.Message.MessageId}' and content '{x.Message.Body}'"); 43 | //throw new InvalidOperationException(); 44 | await x.CompleteMessageAsync(x.Message); 45 | syncEvent.TrySetResult(true); 46 | }; 47 | 48 | processor.ProcessErrorAsync += (ProcessErrorEventArgs x) => 49 | { 50 | Console.WriteLine($"EntityPath: {x.EntityPath}"); 51 | Console.WriteLine($"FullyQualifiedNamespace: {x.FullyQualifiedNamespace}"); 52 | Console.WriteLine($"ErrorSource: {x.ErrorSource}"); 53 | Console.WriteLine($"Exception: {x.Exception}"); 54 | 55 | return Task.CompletedTask; 56 | }; 57 | 58 | await processor.StartProcessingAsync(); 59 | //await processor.StopProcessingAsync(); 60 | 61 | await syncEvent.Task; 62 | } 63 | finally 64 | { 65 | await sender.CloseAsync(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Receiving/Receiving.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Receiving/Trivia.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Azure.Messaging.ServiceBus; 5 | 6 | namespace Receiving; 7 | 8 | public static class Trivia 9 | { 10 | public static async Task Ask(ServiceBusClient client, string source) 11 | { 12 | ServiceBusReceivedMessage message; 13 | 14 | var receiver = client.CreateReceiver(source); 15 | 16 | message = await receiver.ReceiveMessageAsync(); 17 | // or 18 | var operationTimeout = TimeSpan.FromSeconds(4); 19 | message = await receiver.ReceiveMessageAsync(operationTimeout); 20 | 21 | IReadOnlyList messages; 22 | 23 | var messageCount = 5; 24 | messages = await receiver.ReceiveMessagesAsync(messageCount); 25 | // or 26 | messages = await receiver.ReceiveMessagesAsync(messageCount, operationTimeout); 27 | } 28 | 29 | // If there are 5 messages in the queue and 10 are requested, what will be the outcome? 30 | // If there are 20 messages in the queue and 10 are requested, what will be the outcome? 31 | } -------------------------------------------------------------------------------- /SafeBatching/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Azure.Messaging.ServiceBus.Administration; 4 | 5 | namespace MessageBatching; 6 | 7 | public static class Prepare 8 | { 9 | public static async Task Infrastructure(string connectionString, string destination) 10 | { 11 | var client = new ServiceBusAdministrationClient(connectionString); 12 | if (await client.QueueExistsAsync(destination)) 13 | { 14 | await client.DeleteQueueAsync(destination); 15 | } 16 | await client.CreateQueueAsync(destination); 17 | } 18 | } -------------------------------------------------------------------------------- /SafeBatching/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Azure.Messaging.ServiceBus; 4 | 5 | namespace MessageBatching; 6 | 7 | internal class Program 8 | { 9 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 10 | 11 | private static readonly string destination = "queue"; 12 | 13 | private static async Task Main(string[] args) 14 | { 15 | await Prepare.Infrastructure(connectionString, destination); 16 | 17 | var serviceBusClient = new ServiceBusClient(connectionString); 18 | var sender = serviceBusClient.CreateSender(destination); 19 | 20 | var batchOptions = new CreateMessageBatchOptions 21 | { 22 | MaxSizeInBytes = 150 23 | }; 24 | var batch = await sender.CreateMessageBatchAsync(batchOptions); 25 | 26 | var message = new ServiceBusMessage("hello"); 27 | for (var i = 0; i < 5; i++) 28 | { 29 | Console.WriteLine(batch.TryAddMessage(message) 30 | ? $"Message added to the batch (size: {batch.SizeInBytes})" 31 | : $"Message cannot fit the batch size {batch.MaxSizeInBytes}"); 32 | } 33 | 34 | await sender.SendMessagesAsync(batch); 35 | 36 | var receiver = serviceBusClient.CreateReceiver(destination, 37 | new ServiceBusReceiverOptions { ReceiveMode = ServiceBusReceiveMode.ReceiveAndDelete}); 38 | var messages = await receiver.ReceiveMessagesAsync(10); 39 | 40 | foreach (var msg in messages) 41 | { 42 | Console.WriteLine($"Message with ID: {msg.MessageId}, body: {msg.Body}"); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /SafeBatching/SafeBatching.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SendVia/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/SendVia.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /SendVia/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/SendVia.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/SendVia.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/SendVia.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /SendVia/CrossEntityTransaction.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SendVia/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace SendVia 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /SendVia/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Azure.Messaging.ServiceBus.Administration; 4 | 5 | namespace CrossEntityTransaction; 6 | 7 | public static class Prepare 8 | { 9 | public static async Task Infrastructure(string connectionString, string inputQueue, string destinationQueue) 10 | { 11 | var client = new ServiceBusAdministrationClient(connectionString); 12 | 13 | if (await client.QueueExistsAsync(inputQueue)) 14 | { 15 | await client.DeleteQueueAsync(inputQueue); 16 | } 17 | await client.CreateQueueAsync(new CreateQueueOptions(inputQueue) { MaxDeliveryCount = 2 }); 18 | 19 | if (await client.QueueExistsAsync(destinationQueue)) 20 | { 21 | await client.DeleteQueueAsync(destinationQueue); 22 | } 23 | await client.CreateQueueAsync(destinationQueue); 24 | } 25 | 26 | public static async Task ReportNumberOfMessages(string connectionString, string input, string destination) 27 | { 28 | var client = new ServiceBusAdministrationClient(connectionString); 29 | 30 | QueueRuntimeProperties inputInfo = await client.GetQueueRuntimePropertiesAsync(input); 31 | QueueRuntimeProperties destinationInfo = await client.GetQueueRuntimePropertiesAsync(destination); 32 | Console.WriteLine($"{inputInfo.ActiveMessageCount} messages in '{input}'"); 33 | Console.WriteLine($"{destinationInfo.ActiveMessageCount} messages in '{destination}'"); 34 | Console.WriteLine(); 35 | } 36 | } -------------------------------------------------------------------------------- /SendVia/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Transactions; 4 | using Azure.Messaging.ServiceBus; 5 | 6 | namespace CrossEntityTransaction; 7 | 8 | internal class Program 9 | { 10 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 11 | 12 | private static readonly string inputQueue = "queue"; 13 | private static readonly string destinationQueue = "destination"; 14 | 15 | private static async Task Main(string[] args) 16 | { 17 | await Prepare.Infrastructure(connectionString, inputQueue, destinationQueue); 18 | 19 | var options = new ServiceBusClientOptions { EnableCrossEntityTransactions = true }; 20 | var client = new ServiceBusClient(connectionString, options); 21 | 22 | var initiator = client.CreateSender(inputQueue); 23 | await initiator.SendMessageAsync(new ServiceBusMessage("Deep Dive")); 24 | 25 | var receiver = client.CreateReceiver(inputQueue); 26 | var sender = client.CreateSender(destinationQueue); 27 | 28 | var receivedMessage = await receiver.ReceiveMessageAsync(); 29 | Console.WriteLine($"Received message from '{inputQueue}"); 30 | await Prepare.ReportNumberOfMessages(connectionString, inputQueue, destinationQueue); 31 | 32 | using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) 33 | { 34 | await sender.SendMessageAsync(new ServiceBusMessage("Message for destination")); 35 | 36 | Console.WriteLine($"Sent message to '{destinationQueue}'"); 37 | await Prepare.ReportNumberOfMessages(connectionString, inputQueue, destinationQueue); 38 | 39 | await receiver.CompleteMessageAsync(receivedMessage); 40 | 41 | Console.WriteLine("Completed incoming message"); 42 | await Prepare.ReportNumberOfMessages(connectionString, inputQueue, destinationQueue); 43 | 44 | ts.Complete(); 45 | } 46 | 47 | Console.WriteLine("Completed scope"); 48 | await Prepare.ReportNumberOfMessages(connectionString, inputQueue, destinationQueue); 49 | 50 | await receiver.CloseAsync(); 51 | await sender.CloseAsync(); 52 | } 53 | } -------------------------------------------------------------------------------- /SendVia/SendVia.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp3.0 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Sending/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Sending.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /Sending/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/Sending.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/Sending.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/Sending.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /Sending/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Azure.Messaging.ServiceBus.Administration; 3 | 4 | namespace Sending; 5 | 6 | public static class Prepare 7 | { 8 | public static async Task Infrastructure(string connectionString, string destination) 9 | { 10 | var client = new ServiceBusAdministrationClient(connectionString); 11 | 12 | if (await client.QueueExistsAsync(destination)) 13 | { 14 | await client.DeleteQueueAsync(destination); 15 | } 16 | 17 | await client.CreateQueueAsync(destination); 18 | } 19 | } -------------------------------------------------------------------------------- /Sending/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Azure.Messaging.ServiceBus; 3 | using Sending; 4 | 5 | var connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 6 | var destination = "queue"; 7 | 8 | await Prepare.Infrastructure(connectionString, destination); 9 | 10 | var serviceBusClient = new ServiceBusClient(connectionString); 11 | 12 | var client = serviceBusClient.CreateSender(destination); 13 | try 14 | { 15 | var message = new ServiceBusMessage("Payload") 16 | { 17 | Subject = "Deep Dive" // Label 18 | }; 19 | message.ApplicationProperties.Add("Machine", Environment.MachineName); 20 | 21 | await client.SendMessageAsync(message); 22 | 23 | Console.WriteLine("Message sent"); 24 | } 25 | finally 26 | { 27 | await client.CloseAsync(); 28 | } -------------------------------------------------------------------------------- /Sending/Sending.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Topologies/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/Topologies.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /Topologies/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/Topologies.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/Topologies.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/Topologies.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /Topologies/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Topologies 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Topologies/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Azure.Messaging.ServiceBus.Administration; 3 | 4 | namespace Topologies; 5 | public static class Prepare 6 | { 7 | public static async Task Infrastructure(string connectionString, string inputQueue, string topicName, string serviceASubscription, string serviceBSubscription) 8 | { 9 | var client = await Cleanup(connectionString, inputQueue, topicName); 10 | 11 | var subscriptionOptions = new CreateSubscriptionOptions(topicName, serviceASubscription) 12 | { 13 | ForwardTo = inputQueue 14 | }; 15 | await client.CreateSubscriptionAsync(subscriptionOptions); 16 | 17 | subscriptionOptions = new CreateSubscriptionOptions(topicName, serviceBSubscription) 18 | { 19 | ForwardTo = inputQueue 20 | }; 21 | await client.CreateSubscriptionAsync(subscriptionOptions); 22 | 23 | await client.DeleteRuleAsync(topicName, serviceASubscription, "$Default"); 24 | await client.DeleteRuleAsync(topicName, serviceBSubscription, "$Default"); 25 | 26 | var ruleOptions = new CreateRuleOptions 27 | { 28 | Name = "MessagesFromServiceA", 29 | Filter = new CorrelationRuleFilter 30 | { 31 | Subject = "rush" 32 | } 33 | }; 34 | await client.CreateRuleAsync(topicName, serviceASubscription, ruleOptions); 35 | 36 | ruleOptions = new CreateRuleOptions 37 | { 38 | Name = "MessagesFromServiceB", 39 | Filter = new SqlRuleFilter("user.priority in ('high', 'normal', 'low')"), 40 | Action = new SqlRuleAction("SET sys.Label = user.priority") 41 | }; 42 | await client.CreateRuleAsync(topicName, serviceBSubscription, ruleOptions); 43 | } 44 | 45 | private static async Task Cleanup(string connectionString, string inputQueue, string topicName) 46 | { 47 | var client = new ServiceBusAdministrationClient(connectionString); 48 | 49 | if (await client.TopicExistsAsync(topicName)) 50 | { 51 | await client.DeleteTopicAsync(topicName); 52 | } 53 | 54 | var topicOptions = new CreateTopicOptions(topicName); 55 | await client.CreateTopicAsync(topicOptions); 56 | 57 | if (await client.QueueExistsAsync(inputQueue)) 58 | { 59 | await client.DeleteQueueAsync(inputQueue); 60 | } 61 | 62 | var queueOptions = new CreateQueueOptions(inputQueue); 63 | await client.CreateQueueAsync(queueOptions); 64 | return client; 65 | } 66 | } -------------------------------------------------------------------------------- /Topologies/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Azure.Messaging.ServiceBus; 4 | 5 | namespace Topologies; 6 | internal class Program 7 | { 8 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 9 | 10 | private static readonly string topicName = "topic"; 11 | private static readonly string rushSubscription = "ServiceASubscription"; 12 | private static readonly string currencySubscription = "ServiceBSubscription"; 13 | 14 | private static readonly string inputQueue = "queue"; 15 | 16 | private static async Task Main(string[] args) 17 | { 18 | await Prepare.Infrastructure(connectionString, inputQueue, topicName, rushSubscription, currencySubscription); 19 | 20 | var serviceBusClient = new ServiceBusClient(connectionString); 21 | 22 | var sender = serviceBusClient.CreateSender(topicName); 23 | 24 | var message = new ServiceBusMessage("Message from service A"); 25 | message.Subject = "rush"; 26 | await sender.SendMessageAsync(message); 27 | 28 | message = new ServiceBusMessage("Message from service B"); 29 | message.ApplicationProperties.Add("priority", "high"); 30 | await sender.SendMessageAsync(message); 31 | 32 | await sender.CloseAsync(); 33 | 34 | var receiver = serviceBusClient.CreateReceiver(inputQueue); 35 | try 36 | { 37 | var receivedMessages = await receiver.ReceiveMessagesAsync(2); 38 | foreach (var receivedMessage in receivedMessages) 39 | { 40 | var body = receivedMessage.Body.ToString(); 41 | var label = receivedMessage.Subject; 42 | var priority = receivedMessage.Subject ?? receivedMessage.ApplicationProperties["priority"]; 43 | 44 | Console.WriteLine($"Body = '{body}' / Label = '{label}' / Priority = '{priority}'"); 45 | } 46 | } 47 | catch (InvalidOperationException ex) 48 | { 49 | Console.Error.WriteLine(ex.Message); 50 | } 51 | finally 52 | { 53 | await receiver.CloseAsync(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Topologies/Topologies.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TransferDLQ/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/TransferDLQ.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /TransferDLQ/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/TransferDLQ.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/TransferDLQ.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/TransferDLQ.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /TransferDLQ/EntityNameFormatter.cs: -------------------------------------------------------------------------------- 1 | // Source: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/src/EntityNameFormatter.cs 2 | 3 | using Azure.Messaging.ServiceBus; 4 | 5 | namespace TransferDLQ; 6 | 7 | internal static class EntityNameFormatter 8 | { 9 | private const string PathDelimiter = @"/"; 10 | private const string SubscriptionsSubPath = "Subscriptions"; 11 | private const string RulesSubPath = "Rules"; 12 | private const string SubQueuePrefix = "$"; 13 | private const string DeadLetterQueueSuffix = "DeadLetterQueue"; 14 | private const string DeadLetterQueueName = SubQueuePrefix + DeadLetterQueueSuffix; 15 | private const string Transfer = "Transfer"; 16 | private const string TransferDeadLetterQueueName = SubQueuePrefix + Transfer + PathDelimiter + DeadLetterQueueName; 17 | 18 | /// 19 | /// Formats the entity path for a receiver or processor taking into account whether using a SubQueue. 20 | /// 21 | public static string FormatEntityPath(string entityPath, SubQueue subQueue) 22 | { 23 | return subQueue switch 24 | { 25 | SubQueue.None => entityPath, 26 | SubQueue.DeadLetter => FormatDeadLetterPath(entityPath), 27 | SubQueue.TransferDeadLetter => FormatTransferDeadLetterPath(entityPath), 28 | _ => null 29 | }; 30 | } 31 | 32 | /// 33 | /// Formats the dead letter path for either a queue, or a subscription. 34 | /// 35 | /// The name of the queue, or path of the subscription. 36 | /// The path as a string of the dead letter entity. 37 | public static string FormatDeadLetterPath(string entityPath) 38 | { 39 | return EntityNameFormatter.FormatSubQueuePath(entityPath, EntityNameFormatter.DeadLetterQueueName); 40 | } 41 | 42 | /// 43 | /// Formats the subqueue path for either a queue, or a subscription. 44 | /// 45 | /// The name of the queue, or path of the subscription. 46 | /// 47 | /// The path as a string of the subqueue entity. 48 | public static string FormatSubQueuePath(string entityPath, string subQueueName) 49 | { 50 | return string.Concat(entityPath, EntityNameFormatter.PathDelimiter, subQueueName); 51 | } 52 | 53 | /// 54 | /// Formats the subscription path, based on the topic path and subscription name. 55 | /// 56 | /// The name of the topic, including slashes. 57 | /// The name of the subscription. 58 | public static string FormatSubscriptionPath(string topicPath, string subscriptionName) 59 | { 60 | return string.Concat(topicPath, PathDelimiter, SubscriptionsSubPath, PathDelimiter, subscriptionName); 61 | } 62 | 63 | /// 64 | /// Formats the rule path, based on the topic path, subscription name and rule name. 65 | /// 66 | /// The name of the topic, including slashes. 67 | /// The name of the subscription. 68 | /// The name of the rule 69 | public static string FormatRulePath(string topicPath, string subscriptionName, string ruleName) 70 | { 71 | return string.Concat( 72 | topicPath, PathDelimiter, 73 | SubscriptionsSubPath, PathDelimiter, 74 | subscriptionName, PathDelimiter, 75 | RulesSubPath, PathDelimiter, ruleName); 76 | } 77 | 78 | /// 79 | /// Utility method that creates the name for the transfer dead letter receiver, specified by 80 | /// 81 | public static string FormatTransferDeadLetterPath(string entityPath) 82 | { 83 | return string.Concat(entityPath, PathDelimiter, TransferDeadLetterQueueName); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /TransferDLQ/MessageExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace TransferDLQ 2 | { 3 | using System.Text; 4 | 5 | public static class MessageExtensions 6 | { 7 | public static string AsString(this byte[] messageBody) 8 | { 9 | return Encoding.UTF8.GetString(messageBody); 10 | } 11 | 12 | public static byte[] AsByteArray(this string body) 13 | { 14 | return Encoding.UTF8.GetBytes(body); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /TransferDLQ/Prepare.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Azure.Messaging.ServiceBus.Administration; 4 | 5 | namespace TransferDLQ; 6 | 7 | public static class Prepare 8 | { 9 | public static async Task Infrastructure(string connectionString, string inputQueue, string destinationQueue) 10 | { 11 | var client = new ServiceBusAdministrationClient(connectionString); 12 | 13 | if (await client.QueueExistsAsync(inputQueue)) 14 | { 15 | await client.DeleteQueueAsync(inputQueue); 16 | } 17 | await client.CreateQueueAsync(new CreateQueueOptions(inputQueue) { MaxDeliveryCount = 1 }); 18 | 19 | if (await client.QueueExistsAsync(destinationQueue)) 20 | { 21 | await client.DeleteQueueAsync(destinationQueue); 22 | } 23 | await client.CreateQueueAsync(destinationQueue); 24 | } 25 | 26 | public static async Task DisableDestination(string connectionString, string destinationQueue) 27 | { 28 | var client = new ServiceBusAdministrationClient(connectionString); 29 | 30 | if (await client.QueueExistsAsync(destinationQueue)) 31 | { 32 | QueueProperties properties = await client.GetQueueAsync(destinationQueue); 33 | properties.Status = EntityStatus.SendDisabled; 34 | 35 | await client.UpdateQueueAsync(properties); 36 | } 37 | } 38 | 39 | public static async Task ReportNumberOfMessages(string connectionString, string input, string destination) 40 | { 41 | var client = new ServiceBusAdministrationClient(connectionString); 42 | QueueRuntimeProperties inputProperties = await client.GetQueueRuntimePropertiesAsync(input); 43 | QueueRuntimeProperties destinationProperties = await client.GetQueueRuntimePropertiesAsync(destination); 44 | 45 | var activeMessageCount = inputProperties.ActiveMessageCount; 46 | var deadLetterMessageCount = inputProperties.DeadLetterMessageCount; 47 | var transferDeadLetterMessageCount = inputProperties.TransferDeadLetterMessageCount; 48 | var activeMessageCountDestination = destinationProperties.ActiveMessageCount; 49 | 50 | var inputDeadLetterPath = EntityNameFormatter.FormatDeadLetterPath(input); 51 | var inputTransferDeadLetterPath = EntityNameFormatter.FormatTransferDeadLetterPath(input); 52 | 53 | Console.WriteLine($"{activeMessageCount} messages in '{input}'"); 54 | Console.WriteLine($"{deadLetterMessageCount} messages in '{inputDeadLetterPath}'"); 55 | Console.WriteLine($"{transferDeadLetterMessageCount} messages in '{inputTransferDeadLetterPath}'"); 56 | Console.WriteLine($"{activeMessageCountDestination} messages in '{destination}'"); 57 | Console.WriteLine(); 58 | } 59 | } -------------------------------------------------------------------------------- /TransferDLQ/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Transactions; 4 | using Azure.Messaging.ServiceBus; 5 | 6 | namespace TransferDLQ; 7 | 8 | internal class Program 9 | { 10 | private static readonly string connectionString = Environment.GetEnvironmentVariable("AzureServiceBus_ConnectionString"); 11 | 12 | private static readonly string inputQueue = "queue"; 13 | private static readonly string destinationQueue = "destination"; 14 | 15 | private static async Task Main(string[] args) 16 | { 17 | await Prepare.Infrastructure(connectionString, inputQueue, destinationQueue); 18 | 19 | var options = new ServiceBusClientOptions { EnableCrossEntityTransactions = true }; 20 | var client = new ServiceBusClient(connectionString, options); 21 | 22 | var initiator = client.CreateSender(inputQueue); 23 | await initiator.SendMessageAsync(new ServiceBusMessage("Deep Dive")); 24 | 25 | var receiver = client.CreateReceiver(inputQueue); 26 | var sender = client.CreateSender(destinationQueue); 27 | 28 | var receivedMessage = await receiver.ReceiveMessageAsync(); 29 | Console.WriteLine($"Received message from '{inputQueue}"); 30 | await Prepare.ReportNumberOfMessages(connectionString, inputQueue, destinationQueue); 31 | 32 | using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) 33 | { 34 | await sender.SendMessageAsync(new ServiceBusMessage("Message for destination")); 35 | 36 | Console.WriteLine($"Sent message to '{destinationQueue}'"); 37 | await Prepare.ReportNumberOfMessages(connectionString, inputQueue, destinationQueue); 38 | 39 | await receiver.CompleteMessageAsync(receivedMessage); 40 | 41 | Console.WriteLine("Completed incoming message"); 42 | await Prepare.ReportNumberOfMessages(connectionString, inputQueue, destinationQueue); 43 | 44 | Console.WriteLine($"Make '{destinationQueue}' queue unreachable"); 45 | await Prepare.DisableDestination(connectionString, destinationQueue); 46 | 47 | Console.WriteLine("Complete scope"); 48 | ts.Complete(); 49 | } 50 | 51 | await Prepare.ReportNumberOfMessages(connectionString, inputQueue, destinationQueue); 52 | 53 | await receiver.CloseAsync(); 54 | await sender.CloseAsync(); 55 | } 56 | } -------------------------------------------------------------------------------- /TransferDLQ/TransferDLQ.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /delete.obj.bin.cmd: -------------------------------------------------------------------------------- 1 | for /d /r . %%d in (packages, .vs, obj, bin) do @if exist "%%d" rd /s/q "%%d" --------------------------------------------------------------------------------