Service Bus - krzysiek861/AvanadeWorkshop GitHub Wiki

Checkout branch (optional)

You can continue working on the branch where you currently are. In case of difficulties, you can start from the following branch: git checkout feature/service-bus

Azure Service Bus send message and add a web job

It is a time to add a web job to the project. Right click on WebApp project and add New Azure WebJob Project.

Important! Make sure that newly created project is in the same .Net Framework version. They should be for instance 4.8 like in our example:

Install necessary packages. You can copy the following content to the packages.config file:

packages.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="6.4.0" targetFramework="net48" />
<package id="Azure.Core" version="1.24.0" targetFramework="net48" />
<package id="Azure.Core.Amqp" version="1.2.0" targetFramework="net48" />
<package id="Azure.Identity" version="1.6.0" targetFramework="net48" />
<package id="Azure.Messaging.ServiceBus" version="7.8.1" targetFramework="net48" />
<package id="Azure.Security.KeyVault.Secrets" version="4.3.0" targetFramework="net48" />
<package id="Azure.Storage.Blobs" version="12.12.0" targetFramework="net48" />
<package id="Azure.Storage.Common" version="12.11.0" targetFramework="net48" />
<package id="Microsoft.ApplicationInsights" version="2.20.0" targetFramework="net48" />
<package id="Microsoft.Azure.Amqp" version="2.5.12" targetFramework="net48" />
<package id="Microsoft.Azure.KeyVault.Core" version="3.0.5" targetFramework="net48" />
<package id="Microsoft.Azure.Storage.Blob" version="11.2.3" targetFramework="net48" />
<package id="Microsoft.Azure.Storage.Common" version="11.2.3" targetFramework="net48" />
<package id="Microsoft.Azure.WebJobs" version="3.0.33" targetFramework="net48" />
<package id="Microsoft.Azure.WebJobs.Core" version="3.0.33" targetFramework="net48" />
<package id="Microsoft.Azure.WebJobs.Extensions" version="4.0.1" targetFramework="net48" />
<package id="Microsoft.Azure.WebJobs.Extensions.ServiceBus" version="5.5.1" targetFramework="net48" />
<package id="Microsoft.Azure.WebJobs.Host.Storage" version="4.0.4" targetFramework="net48" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Data.Edm" version="5.8.5" targetFramework="net48" />
<package id="Microsoft.Data.OData" version="5.8.5" targetFramework="net48" />
<package id="Microsoft.Data.Services.Client" version="5.8.5" targetFramework="net48" />
<package id="Microsoft.Extensions.Azure" version="1.2.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Configuration" version="6.0.1" targetFramework="net48" />
<package id="Microsoft.Extensions.Configuration.Abstractions" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Configuration.Binder" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Configuration.CommandLine" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Configuration.EnvironmentVariables" version="6.0.1" targetFramework="net48" />
<package id="Microsoft.Extensions.Configuration.FileExtensions" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Configuration.Json" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Configuration.UserSecrets" version="6.0.1" targetFramework="net48" />
<package id="Microsoft.Extensions.DependencyInjection" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.FileProviders.Abstractions" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.FileProviders.Physical" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.FileSystemGlobbing" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Hosting" version="6.0.1" targetFramework="net48" />
<package id="Microsoft.Extensions.Hosting.Abstractions" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Logging" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="6.0.1" targetFramework="net48" />
<package id="Microsoft.Extensions.Logging.Configuration" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Logging.Console" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Logging.Debug" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Logging.EventLog" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Logging.EventSource" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Options" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Options.ConfigurationExtensions" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.Primitives" version="6.0.0" targetFramework="net48" />
<package id="Microsoft.Identity.Client" version="4.44.0" targetFramework="net48" />
<package id="Microsoft.Identity.Client.Extensions.Msal" version="2.21.0" targetFramework="net48" />
<package id="Microsoft.IdentityModel.Abstractions" version="6.19.0" targetFramework="net48" />
<package id="Microsoft.IdentityModel.JsonWebTokens" version="6.19.0" targetFramework="net48" />
<package id="Microsoft.IdentityModel.Logging" version="6.19.0" targetFramework="net48" />
<package id="Microsoft.IdentityModel.Tokens" version="6.19.0" targetFramework="net48" />
<package id="Microsoft.NETCore.Platforms" version="6.0.3" targetFramework="net48" />
<package id="Microsoft.NETCore.Targets" version="5.0.0" targetFramework="net48" />
<package id="Microsoft.Rest.ClientRuntime" version="2.3.24" targetFramework="net48" />
<package id="Microsoft.Rest.ClientRuntime.Azure" version="3.3.19" targetFramework="net48" />
<package id="Microsoft.Web.WebJobs.Publish" version="17.1.361" targetFramework="net48" />
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.2.3" targetFramework="net48" />
<package id="NCrontab.Signed" version="3.3.2" targetFramework="net48" />
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net48" />
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
<package id="System.ComponentModel.Annotations" version="5.0.0" targetFramework="net48" />
<package id="System.Diagnostics.DiagnosticSource" version="6.0.0" targetFramework="net48" />
<package id="System.Diagnostics.TraceSource" version="4.3.0" targetFramework="net48" />
<package id="System.IdentityModel.Tokens.Jwt" version="6.19.0" targetFramework="net48" />
<package id="System.IO" version="4.3.0" targetFramework="net48" />
<package id="System.IO.Hashing" version="6.0.1" targetFramework="net48" />
<package id="System.Memory" version="4.5.5" targetFramework="net48" />
<package id="System.Memory.Data" version="6.0.0" targetFramework="net48" />
<package id="System.Net.Http" version="4.3.4" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
<package id="System.Private.Uri" version="4.3.2" targetFramework="net48" />
<package id="System.Runtime" version="4.3.1" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net48" />
<package id="System.Security.Cryptography.Algorithms" version="4.3.1" targetFramework="net48" />
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net48" />
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net48" />
<package id="System.Security.Cryptography.ProtectedData" version="6.0.0" targetFramework="net48" />
<package id="System.Security.Cryptography.X509Certificates" version="4.3.2" targetFramework="net48" />
<package id="System.Spatial" version="5.8.5" targetFramework="net48" />
<package id="System.Text.Encodings.Web" version="6.0.0" targetFramework="net48" />
<package id="System.Text.Json" version="6.0.4" targetFramework="net48" />
<package id="System.Threading.Tasks.Dataflow" version="6.0.0" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
</packages>

Open the console in Visual Studio using the Tools > NuGet Package Manager > Package Manager Console and run the command:

Update-Package -ProjectName 'Avanade.AzureWorkshop.Topics' -Reinstall

Add reference to Avanade.AzureWorkshop.WebApp.

Create a new class IocConfig in the main project directory and add this content:

click here to see usings
  using Autofac;
  using Avanade.AzureWorkshop.WebApp.BusinessLogic;
  using Avanade.AzureWorkshop.WebApp.Services;
    public class IocConfig
    {
        private static IContainer _container;

        public IocConfig()
        {
            var builder = new ContainerBuilder();

            builder.RegisterType<GamesService>();
            builder.RegisterType<PlayersService>();
            builder.RegisterType<TeamsRepository>();
            builder.RegisterType<ImagesService>();
            builder.RegisterType<BinaryFilesRepository>();
            builder.RegisterType<SendgridService>();
            builder.RegisterType<TelemetryService>();

            _container = builder.Build();
        }

        public IContainer GetConfiguredContainer()
        {
            return _container;
        }
    }

Create a new class DelayedConfigurationSource in the main project directory and add this content:

public class DelayedConfigurationSource : IConfigurationSource
{
    private IConfigurationProvider Provider { get; } = new DelayedConfigurationProvider();
    public IConfigurationProvider Build(IConfigurationBuilder builder) => Provider;
    public void Set(string key, string value) => Provider.Set(key, value);

    private class DelayedConfigurationProvider : ConfigurationProvider
    {
        public override void Set(string key, string value)
        {
            base.Set(key, value);
            OnReload();
        }
    }
}

Paste the following code to Program.cs file:

click here to see usings
    using Autofac;
    using Avanade.AzureWorkshop.WebApp.Models;
    using Avanade.AzureWorkshop.WebApp.Services;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    using System.Threading.Tasks;
internal class Program
{
    public static IContainer Container { get; private set; }

    static async Task Main()
    { 
        var container = new IocConfig();
        Container = container.GetConfiguredContainer();

        var delayedConfigurationSource = new DelayedConfigurationSource();

        var builder = new HostBuilder()
            .ConfigureHostConfiguration(configurationBuilder =>
            {
                configurationBuilder.Add(delayedConfigurationSource);
            });
        builder.UseEnvironment(Environments.Development);
        builder.ConfigureLogging((context, b) =>
        {
            b.AddConsole();
        });
        builder.ConfigureWebJobs(b =>
        {
            b.AddAzureStorageCoreServices();
            b.AddServiceBus();
        });
        var host = builder.Build();
        using (host)
        {
            var manager = new SecretsManager();
            manager.Initialize();

            delayedConfigurationSource.Set("ConnectionStrings:AzureWebJobsServiceBus", GlobalSecrets.ServiceBusConnectionString);
            delayedConfigurationSource.Set("ConnectionStrings:AzureWebJobsDashboard", GlobalSecrets.StorageAccountConnectionString);
            delayedConfigurationSource.Set("ConnectionStrings:AzureWebJobsStorage", GlobalSecrets.StorageAccountConnectionString);

            await host.RunAsync().ConfigureAwait(false);
        }
    }
}

And the following code to Functions.cs file:

public class Functions
{
    private const string SubscriptionName = "webjobssubscription";

    public async Task ProcessGameMessage([ServiceBusTrigger(nameof(GameMessageModel), SubscriptionName, Connection = "AzureWebJobsServiceBus")] GameMessageModel message, TextWriter textWriter)
    {
        await ProcessMessage(textWriter, message, async (scope, model) =>
        {
            var gamesService = scope.Resolve<GamesService>();
            var telemetryService = scope.Resolve<TelemetryService>();
            await gamesService.SaveGameResult(message);
            await WriteMessage(message.CorrelationId, textWriter);
            telemetryService.Log("Succesfully saved game result", message.CorrelationId);
        });
    }

    private static async Task ProcessMessage<TMessage>(TextWriter textWriter, TMessage message, Func<ILifetimeScope, TMessage, Task> action)
        where TMessage : BaseMessageModel
    {
        using (var scope = Program.Container.BeginLifetimeScope())
        {
            await WriteMessage($"Processing topic message {typeof(TMessage).Name}. Body: {JsonConvert.SerializeObject(message)}", textWriter);

            try
            {
                await action(scope, message);
            }
            catch (Exception ex)
            {
                textWriter.WriteLine($"Unexpected error {ex.Message} {ex.StackTrace} {ex.InnerException}");
                throw;
            }

        }
    }

    private static async Task WriteMessage(string message, TextWriter writer)
    {
        await writer.WriteLineAsync(message);
    }
}

Configure application settings

You should add whole appSettings section.

<appSettings>
    <add key="KeyVaultUri" value="" />
    <add key="StorageConnStrSecretName" value="storageConnectionString" />
    <add key="ServiceBusConnStrSecretName" value="serviceBusConnectionString" />
    <add key="SendgridApiKeySecretName" value="sendgridApiKey" />
</appSettings>

App.config (Topics project)

Copy your KeyVault URI and paste it to the KeyVaultUri app setting.

Check if all compiles and publish the app. At this point you should be able to consume messages.

Now, you can play a game, simply click "play a game" link under group stadings. Open Web Jobs logs to check logs.

At this commit you get a logic that saves played game when the message is processed. Play some games, analyze the code and see which teams advance to the next stage of championships.

Obstacles

Please check that option Always On is set to On in application settings.

If you cannot see new logs, please restart the application.

packages.config to PackageReference migration

If you generated Topics project with the latest version of Visual Studio, it doesn't use packages.config file anymore. Please instead update your .csproj file with the following package references:

Avanade.AzureWorksop.Topics.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net48</TargetFramework>
    <IsWebJobProject>true</IsWebJobProject>
    <WebJobName>Topics</WebJobName>
    <WebJobType>Continuous</WebJobType>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Autofac" Version="6.4.0" />
    <PackageReference Include="Microsoft.WindowsAzure.ConfigurationManager" Version="3.2.1" />
    <PackageReference Include="WindowsAzure.Storage" Version="7.0.0" />
    <PackageReference Include="Azure.Core" Version="1.24.0" />
    <PackageReference Include="Azure.Core.Amqp" Version="1.2.0" />
    <PackageReference Include="Azure.Identity" Version="1.6.0" />
    <PackageReference Include="Azure.Messaging.ServiceBus" Version="7.8.1" />
    <PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.3.0" />
    <PackageReference Include="Azure.Storage.Blobs" Version="12.12.0" />
    <PackageReference Include="Azure.Storage.Common" Version="12.11.0" />
    <PackageReference Include="Microsoft.ApplicationInsights" Version="2.20.0" />
    <PackageReference Include="Microsoft.Azure.Amqp" Version="2.5.12" />
    <PackageReference Include="Microsoft.Azure.KeyVault.Core" Version="3.0.5" />
    <PackageReference Include="Microsoft.Azure.Storage.Blob" Version="11.2.3" />
    <PackageReference Include="Microsoft.Azure.Storage.Common" Version="11.2.3" />
    <PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.33" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Core" Version="3.0.33" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="4.0.1" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="5.5.1" />
    <PackageReference Include="Microsoft.Azure.WebJobs.Host.Storage" Version="4.0.4" />
    <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
    <PackageReference Include="Microsoft.Data.Edm" Version="5.8.5" />
    <PackageReference Include="Microsoft.Data.OData" Version="5.8.5" />
    <PackageReference Include="Microsoft.Data.Services.Client" Version="5.8.5" />
    <PackageReference Include="Microsoft.Extensions.Azure" Version="1.2.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.EventSource" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
    <PackageReference Include="Microsoft.Extensions.Primitives" Version="6.0.0" />
    <PackageReference Include="Microsoft.Identity.Client" Version="4.44.0" />
    <PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="2.21.0" />
    <PackageReference Include="Microsoft.IdentityModel.Abstractions" Version="6.19.0" />
    <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="6.19.0" />
    <PackageReference Include="Microsoft.IdentityModel.Logging" Version="6.19.0" />
    <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.19.0" />
    <PackageReference Include="Microsoft.NETCore.Platforms" Version="6.0.3" />
    <PackageReference Include="Microsoft.NETCore.Targets" Version="5.0.0" />
    <PackageReference Include="Microsoft.Rest.ClientRuntime" Version="2.3.24" />
    <PackageReference Include="Microsoft.Rest.ClientRuntime.Azure" Version="3.3.19" />
    <PackageReference Include="Microsoft.Web.WebJobs.Publish" Version="17.1.361" />
    <PackageReference Include="Microsoft.WindowsAzure.ConfigurationManager" Version="3.2.3" />
    <PackageReference Include="NCrontab.Signed" Version="3.3.2" />
    <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
    <PackageReference Include="System.Buffers" Version="4.5.1" />
    <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
    <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="6.0.0" />
    <PackageReference Include="System.Diagnostics.TraceSource" Version="4.3.0" />
    <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.19.0" />
    <PackageReference Include="System.IO" Version="4.3.0" />
    <PackageReference Include="System.IO.Hashing" Version="6.0.1" />
    <PackageReference Include="System.Memory" Version="4.5.5" />
    <PackageReference Include="System.Memory.Data" Version="6.0.0" />
    <PackageReference Include="System.Net.Http" Version="4.3.4" />
    <PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
    <PackageReference Include="System.Private.Uri" Version="4.3.2" />
    <PackageReference Include="System.Runtime" Version="4.3.1" />
    <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
    <PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" />
    <PackageReference Include="System.Security.Cryptography.Algorithms" Version="4.3.1" />
    <PackageReference Include="System.Security.Cryptography.Encoding" Version="4.3.0" />
    <PackageReference Include="System.Security.Cryptography.Primitives" Version="4.3.0" />
    <PackageReference Include="System.Security.Cryptography.ProtectedData" Version="6.0.0" />
    <PackageReference Include="System.Security.Cryptography.X509Certificates" Version="4.3.2" />
    <PackageReference Include="System.Spatial" Version="5.8.5" />
    <PackageReference Include="System.Text.Encodings.Web" Version="6.0.0" />
    <PackageReference Include="System.Text.Json" Version="6.0.4" />
    <PackageReference Include="System.Threading.Tasks.Dataflow" Version="6.0.0" />
    <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
    <PackageReference Include="System.ValueTuple" Version="4.5.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Avanade.AzureWorkshop.WebApp\Avanade.AzureWorkshop.WebApp.csproj" />
  </ItemGroup>
</Project>
⚠️ **GitHub.com Fallback** ⚠️