Creating a Running Game in VueJS – Part 3 – Component Files and Installing Vue

This is the third part in a series on replicating a game similar (in some respects) to the ZX Spectrum game “Daley Thompson’s Decathlon”.

To start from the beginning of the series, go here.

In this particular post, we’ll be continuing our refactor by moving out components into their own files.

If you’d like to see the code from this post then it’s on GitHub here.

Install Vue and Create a New App

The first thing you’ll need to do here is to install Vue:

npm install vue

Once this is done, we’ll just create a brand new templated Vue project:

vue create vue-app

What we’re essentially going to do here is to copy our existing program from part 2 into the new application, but using separate files for components. All we actually have now is a single file (index.html).

Copying Code

Your new app should look broadly like this:

Let’s start by creating a new component called Timer, this will be the successor for our seconds-remaining component:

Now, let’s take the code from our component and paste it in. We’ll start with the template; the current template is:

template: `
    <p>Seconds remaining: {{ seconds }}</p>
`

And that will become:

<template>
    <p>Seconds remaining: {{ seconds }}</p>
</template>

Next we need the props; so the following:

props: ['seconds'],

Will become:

<script>
export default {
  name: 'Timer',
  props: {
    seconds: Number
  }
}
</script>

Since we have no style, that’s done:

Next, we’ll bring across the player component in the same way:

The next stage is to update App.vue component. The template will now look like this:

<template>
  <Player
      v-bind:location="location">
  </Player>
  <Timer 
      v-bind:seconds="secondsRemaining">
  </Timer>            
</template>

Inside the script tag, we’ll bring in the Player and Timer components:

<script>
    import Player from './components/Player.vue'
    import Timer from './components/Timer.vue'

Next, we need to define which components we’re using:

export default {
  name: 'App',
  components: {    
    Player,
    Timer
  },

We then need our data tag, which now needs to be a function:

  data: function() {                    
    return {
      playerX: 100,
      playerY: 100,
      speed: 0,
      toggleSpeed: 1,                    
      startTime: 0,
      timeNow: 0
    };
  },

The rest can come across as is (please see the GitHub repo for these).

Finally, you can remove HelloWorld.vue.

Now that we have the code inside components, in the next part, we’ll add a finish line, and a way for the player to win or lose.

Creating a Running Game in VueJS – Part 2 – Components

This is the second post in a series that started here. If you’d like some context, then please visit the first post. In this post, I’ll be refactoring the small amount of code that we currently have, into Vue Components.

You can find the code for this post here.

Refactoring into Global Components

Let’s start with a very simple component, and that’s the display of “Seconds Remaining”. For the time being, we’ll create this in the same file, so all we’re doing here is shuffling code around. Instead of the following code:

<p>{{ secondsRemaining }}</p>

We’ll add this:

<div id="app">
    <div v-bind:style="location"></div>
    <seconds-remaining 
        v-bind:seconds="secondsRemaining">
    </seconds-remaining>            
</div>

A quick note on this, you may see the following syntax:

<div id="app">
    <div v-bind:style="location"></div>
    <seconds-remaining 
        :seconds="secondsRemaining">
    </seconds-remaining>            
</div>

Essentially, here the v-bind is implicit. Both work.

We can then define this inside the script tag like so:

<script>
    Vue.component('seconds-remaining', {
        props: ['seconds'],
        template: `
            <p>Seconds remaining: {{ seconds }}</p>
        `
    });
    new Vue({

    . . .

You’ll notice the use of the back-tick (`); you need this if the template spans multiple lines (in our case, we could probably get away with a single line – if we had, it would look like this:

<script>
    Vue.component('seconds-remaining', {
        props: ['seconds'],
        template: '<p>Seconds remaining: {{ seconds }}</p>'        
    });
    new Vue({

    . . .

One other important thing to note here, you are not permitted to have uppercase characters in the component name. You won’t get an error as such, it just automatically lower cases all the text, meaning that it won’t find your component (and you won’t know why!)

Let’s move the player into a component, too:

<div id="app">
    <player
        v-bind:location="location">
    </player>
    <seconds-remaining 
        v-bind:seconds="secondsRemaining">
    </seconds-remaining>            
</div>

And the script:

<script>
    Vue.component('seconds-remaining', {
        props: ['seconds'],
        template: `
            <p>Seconds remaining: {{ seconds }}</p>
        `
    });
    Vue.component('player', {
        props: ['location'],
        template: `
            <div v-bind:style="location"></div>
        `
    });

    . . .

In the next post, we’ll move these components out into their own files, which will involve actually installing Vue, rather than just importing the library.

References

https://www.taniarascia.com/getting-started-with-vue/

Vue.js Tutorial From Scratch – e07 – NPM, Webpack & Single File Component

Read the Dead Letter Queue

I’ve been writing about and speaking about Azure Service Bus a lot recently.

In this post, I’m going to focus on the Dead Letter Queue in more detail.

What is the Dead Letter Queue, and what has it ever done for me?

To describe what the dead letter queue does, I invite you to think about an assembly line for a car. The car in question has just come through to have a bonnet fitted (hood for any American readers). However, the guy that’s fitting the bonnet can’t get it to sit right in the hinges; he tries and tries, but it won’t fit. After a while, he goes to get his superviser, and they both try. They draft the workers in from all over the plant, but can’t get the bonnet fitted.

Meanwhile, the entire assembly line has stopped. The person that fits the steering wheel is behind the bonnet fitter, and there’s no space for him to move the car that he’s just fitted the wheel to; the dashboard fitter can’t pass onto the steering wheel, and so on.

(I have no knowledge of what a car assembly line looks like, outside of the film Christine, so apologies if this is incorrect).

A message that can’t be processed is often called a poison message, and it causes exactly this problem. The Service Bus can’t deliver any messages until this message has gone, and this message can’t go, because there’s something wrong with it. The solution is to have a dedicated queue that holds these messages: it’s called a Dead Letter Queue – it’s kind of like a holding bay for the car.

Why would a message be “poison”

There are a few reasons that a message can be considered “poison” and dead lettered; some of the most common are:

– Each queue has a maximum delivery count, if it’s exceeded – that is, we’ve tried too many times to process it
– The message can be explicitly marked as bad by the client
– The size of the message is bigger than the allocated maximum size
– The message has been “auto-forwarded” too many times

Essentially, the system tries to work out whether this message is staying around too long and causing issues with the system. It’s important to know, though, that the dead letter queue is just another queue. The message isn’t lost – just side-lined.

Dead Lettering

Let’s see how we can force a message into a dead letter queue. The easiest way to do this is to explicitly just Dead Letter the message; for example:

            var messageReceiver = new MessageReceiver(connectionString, QUEUE_NAME);
            var message = await messageReceiver.ReceiveAsync();

            await messageReceiver.DeadLetterAsync(message.SystemProperties.LockToken, "Really bad message");

Here, we’ve read the message, and then told Service Bus to just Dead Letter it. In real life, you may choose to do this on rare occasions, but I imagine its main use is for testing.

Abandon the Message

Another way to cause a message to be dead lettered is to exceed the Max Delivery Count. You can do this by “abandoning” the message multiple times; for example:

var messageReceiver = new MessageReceiver(connectionString, QUEUE_NAME);
var message = await messageReceiver.ReceiveAsync();

string messageBody = Encoding.UTF8.GetString(message.Body);

Console.WriteLine($"Message {message.MessageId} ({messageBody}) had a delivery count of {message.SystemProperties.DeliveryCount}");
await messageReceiver.AbandonAsync(message.SystemProperties.LockToken);

Here, we’re reading the message, and rather than completing it, we’re abandoning it. It’s worth bearing in mind that this is what happens when you abandon a message. It’s also what happens when you read a message and just implicitly abandon it (i.e., you read it on a PeekLock and then do nothing): the AbandonAsync method doesn’t actually change the functionality of the code above – it does change the speed, though.

Reading The Dead Letter Queue

Now that we’ve dead-lettered a message, we can read the Dead Letter Queue.

            var deadletterPath = EntityNameHelper.FormatDeadLetterPath(QUEUE_NAME);
            var deadLetterReceiver = new MessageReceiver(connectionString, deadletterPath, ReceiveMode.PeekLock);
            
            var message = await deadLetterReceiver.ReceiveAsync();

            string messageBody = Encoding.UTF8.GetString(message.Body);

            Console.WriteLine("Message received: {0}", messageBody);
            if (message.UserProperties.ContainsKey("DeadLetterReason"))
            {
                Console.WriteLine("Reason: {0} ", message.UserProperties["DeadLetterReason"]);
            }
            if (message.UserProperties.ContainsKey("DeadLetterErrorDescription"))
            {
                Console.WriteLine("Description: {0} ", message.UserProperties["DeadLetterErrorDescription"]);
            }

The code above sets up a MessageReceiver for the dead letter queue. The delivery count inside the dead letter queue does not increase, but it does retain the number that it had from the original queue. Effectively, all you can do with a Dead Letter message is to complete it.

DeadLetterReason

When a message is dead lettered, the properties DeadLetterReason and DeadLetterErrorDescription may get added to the message. If you forcibly dead letter the message then you have the option to add this: if you choose not to then it will not be present (hence the checks around the properties), but mostly, these will be available.

Re-submitting a Message and Transactions

We’ve now seen how to cause a message to Dead Letter, and read the Dead Letter queue; next we’re going to investigate re-submitting the message.

As a quick side not – you can’t really re-submit a message – as you’ll see, what we actually do is to complete the dead letter message, and send a copy back to the queue.

            var serviceBusConnection = new ServiceBusConnection(connectionString);

            var deadletterPath = EntityNameHelper.FormatDeadLetterPath(QUEUE_NAME);
            var deadLetterReceiver = new MessageReceiver(serviceBusConnection, deadletterPath, ReceiveMode.PeekLock);
            
            var queueClient = new QueueClient(serviceBusConnection, QUEUE_NAME, ReceiveMode.PeekLock, RetryPolicy.Default);

            var deadLetterMessage = await deadLetterReceiver.ReceiveAsync();

            using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);

            var resubmitMessage = deadLetterMessage.Clone();

            resubmitMessage.UserProperties.Remove("DeadLetterReason");
            resubmitMessage.UserProperties.Remove("DeadLetterErrorDescription");
            
            await queueClient.SendAsync(resubmitMessage);
            await deadLetterReceiver.CompleteAsync(deadLetterMessage.SystemProperties.LockToken);            

            scope.Complete();            

There’s a few points to note in the above code:

FormatDeadLetterPath

FormatDeadLetterPath gives you the entity path for the dead letter queue, based on an entity.

Transaction Scope

The scope ensures that everything between its creation and completion happens as a single transaction. That is, if part of that fails, the whole thing fails. For example, you could add a throw new exception between the send and the complete, and the new message will not send.

We’re using the new C# 8 using statement – that is, it will apply to everything between it, and the end of the method.

ServiceBusConnection

There are several overloads for most of these methods, and typically, you can pass a connection string into the constructor – for example, MessageReceiver could be called like this:

new MessageReceiver(connectionString, QUEUE_NAME);

Typically, you can use this and it works exactly the same as if you established your own connection and passed that through; however, with a transaction, everything needs to share a connection. If they do not, then you may see an error such as this:

Transaction hasn’t been declared yet, or has already been discharged

Hence we’re creating the connection upfront.

References

https://blogs.infosupport.com/implementing-a-retry-pattern-for-azure-service-bus-with-topic-filters/

https://stackoverflow.com/questions/38784331/how-to-peek-the-deadletter-messages

https://github.com/Azure/azure-service-bus/pull/91

Creating a Running Game in VueJS – Part 1 – Moving

If, like me, you’re over the age of 35, you may have owned a Spectrum, Amstrad, or Commodore 64. One of the games, especially on the Spectrum, that was guaranteed to ruin your keyboard, and make your family think you were having some kind of fit, was Daley Thomson’s Decathlon.

In this post, I introduced the basics of writing a game in VueJS. In this series of posts, I’m going to try to recreate that game, at least in spirit. I’m afraid I won’t have the amazing graphics, but hopefully we can create something vaguely similar.

I’ll be putting the various parts in GitHub here. I made a similar series of posts using React; you can find them here.

In this first section, we’ll create a player (it’ll just be a box on the screen for now), and a timer. To get the code, look here.

Let’s start by seeing the HTML:

<div id="app">
    <div v-bind:style="location"></div>
    <p>{{ secondsRemaining }}</p>
</div>

We’re simply binding the style of the div to one computed property, and secondsRemaining is another. Let’s see what the computed section looks like next:

computed: {                    
    location: function() {
        return 'width:10px; height:10px; left:' + this.playerX + 'px; top:' + this.playerY + 'px; border:1px solid #000; position: absolute;';
    },
    secondsRemaining: function() {
        const diff = (this.startTime - this.timeNow) / 1000;
        return Math.round(diff);
    }
},

Remember that this section will cause a screen refresh when the variables it uses change; it does not know that the function will return a different value otherwise; which is why I’m not getting the current time inside the secondsRemaining function.

The location will force an update when the playerX or playerY properties change. Let’s see the data:

data: {                    
     playerX: 100,
     playerY: 100,
     speed: 0,
     toggleSpeed: 1,                    
     startTime: 0,
     timeNow: 0
},

There’s just two more set-up points – the first is the mounted() method:

mounted() {
    this.init();
}

This is another lifecycle event; it’s called late on in the start-up process, after everything is created. We’ll come back to Init() shortly.

Next we have created(): this is early on in the lifecycle, and here we’re just going to create an event listener:

created() {
    window.addEventListener('keydown', this.onKeyDown);
},

That’s pretty much all the set-up. Everything else is in the methods() section. Let’s start with the init() method that we saw earlier:

init() {
    setInterval(this.update, 10);
    this.startTime = new Date();
    this.startTime.setSeconds(this.startTime.getSeconds() + 20);
},

All we’re doing here is initialising the timer, and creating an update loop. Let’s see the update() method next:

update() {
    this.playerX += this.speed;
    if (this.speed > 0) {
        this.speed -= 0.01;
    } else {
        this.speed = 0;
    }
    this.timeNow = new Date().getTime();
}

This is the update method, but it’s more akin to a draw method: it’s limited to updating the variables that force a screen update. It also slows the runner down naturally (so if the player does nothing, the character will stop).

There’s just one more thing we have yet to see, and that’s the method that handles the keypress; which, in fact, is two methods:

onKeyDown(e) {                        
    switch (e.which) {
        case 37: // Left
            this.playerRun(1); 
            break;
        case 39: // Right
            this.playerRun(2); 
            break;
        default:
            break;
    }
},
                    
playerRun(toggle) {                        
    if (this.toggleSpeed !== toggle) {                           
        this.speed += 0.12;
        this.toggleSpeed = toggle;
    }
},                  

One of the defining features of the original game was that you ran by pressing the left key, and then the right in quick succession; we’ve emulated this by simply setting a flag, and comparing them.

That’s it, the first part complete:

I know you’re probably wondering how I managed to great such amazing graphics!

The code works (after a fashion), but it’s messy. In Part 2, we’ll refactor this code into components.

Vue Basics – Moving Objects Around the Screen

I’ve recently been playing around with Vue. One of the things that I typically do when I’m learning some kind of front-end technology, is to work out how I might write a game in that technology – that usually tests the boundaries of what you can do. If you’re interested, this is a car game that I created in React.

Let’s start by having a look at what a bog standard “Hello World” app looks like in Vue:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Testing Vue</title>
    </head>
    <body>
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
        
        <div id="app">
            <h1>{{ title }}</h1>
        </div>
        <script>
            new Vue({
                el: '#app',
                data: {
                    title: 'Hello World'
                }
            });
        </script>        
    </body>
</html>

Another way that you can write that, would be like this:

<p v-text="testdata"></p>

This kind of synthax actually lets you bind any property, for example:

<h1 v-bind:title="testdata"></h1>

That’s pretty much the bit that I was looking for; if you can bind the title, then you can bind the style; for example:

<div v-bind:style="location">test</div>

Inside the data section, I can expose a location; for example:

new Vue({
    el: '#app',
    data: {
        testdata: 'some data',
        location: 'width: 500px; height: 100px; left: 20px;border: 1px solid #000; position: absolute;'
                    
    },

Okay, so now we’re binding the style to a piece of data, so can we now do something like this:

data: {
    testdata: 'some data',
    posx: 100,
    location: 'width: 500px; height: 100px; left:' + this.posx + 'px; border: 1px solid #000; position: absolute;'
},

Unfortunately (or perhaps fortunately, I haven’t decided yet) not. Anything that changes needs to be in a separate section, called computed:

data: {
    testdata: 'some data',
    posx: 100                    
},
computed: {                    
    location: function() {
        return 'width:500px; height:100px; left:' + this.posx + 'px; border:1px solid #000; position: absolute;';
    } 
},

We’re actually nearly there now; all that’s missing is the thing that actually updates the variable, and therefore moves the div across the screen. The next thing we come across is the mounted method – this is a lifecycle event for each component, so we’ll need that to initialise our timer; now we can do this:

methods: {
    init() {
        setInterval(this.moveObject, 10);
    },
    moveObject() {
        this.posx += 1;                        
    }
},
mounted() {
    this.init();
}

It’s not hugely exciting, I’ll grant you, but moving an object around a screen is the basis for any game. For completeness, here’s the full code:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Testing Vue</title>
    </head>
    <body>
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
        <div id="app">
            <h1>{{ testdata }}</h1>
            <p v-text="testdata"></p>
            <div v-bind:style="location">test</div>
        </div>
        <script>
            new Vue({
                el: '#app',
                data: {
                    testdata: 'some data',
                    posx: 100                    
                },
                computed: {                    
                    location: function() {
                        return 'width:500px;height:100px; left:' + this.posx + 'px;border:1px solid #000; position: absolute;';
                    } 
                },
                methods: {
                    init() {
                        setInterval(this.moveObject, 10);
                    },
                    moveObject() {
                        this.posx += 1;                        
                    }
                },
                mounted() {
                    this.init();
                }
            });
            
        </script>        
    </body>
</html>

References

Vue.js Tutorial From Scratch – e01 – Introduction, Installation & Outputting Data

Viewing Server Variables in Asp.Net Core

In Asp.Net Core, you can view a list of data known as Server Variables. These are available when hosting inside IIS, and they give information about the request call and security.

You can view any one of these by adding some middleware to your Asp.Net Core project:

    app.Use(async (context, next) =>
    {
        var url = context.Features.Get<IServerVariablesFeature>()["URL"];

        await next.Invoke();
    }

You’ll need to be hosting inside IIS, and the code should go in the routing zone.

There is also a helper method for this, meaning you can do this instead:

    app.Use(async (context, next) =>
    {
        string a = context.GetServerVariable("URL");

        await next.Invoke();
    }

I then thought, why not just enumerate all the variables. You can see the list here.

I created a list of these:

    public static class ServerVariables
    {
        public static string[] Variables = new[]
        {
            "UNENCODED_URL",
            "URL",
            "QUERY_STRING",
            "REMOTE_ADDR",
            "ALL_HTTP",
            "AUTH_USER",
            "AUTH_TYPE",
            "REMOTE_USER",
            "SERVER_ADDR",
            "LOCAL_ADDR",
            "SERVER_PROTOCOL",
            "ALL_RAW",
            "REMOTE_HOST",
            "SERVER_SOFTWARE",
            "HTTP_RAW",
            "HTTP_COOKIE"
        };
    }

And then, in the middleware, just looped through them:

            app.Use(async (context, next) =>
            {                
                await context.Response.WriteAsync("<div>");
                foreach (var variable in ServerVariables.Variables)
                {
                    string result = context.GetServerVariable(variable);
                    await context.Response.WriteAsync($"<p>{variable}:    <b>{result}</b></p><br />");
                }

                await context.Response.WriteAsync("</div>");
                await next.Invoke();
            });

Remember that if you run this hosted in Kestrel, you’ll get nothing back.

References

https://docs.microsoft.com/en-us/previous-versions/iis/6.0-sdk/ms524602(v=vs.90)

Azure Service Bus – Scheduled Message Delivery

Azure Service Bus sets itself apart from other message brokers by the dizzying array of additional and useful features that it provides out of the box. This particular one is really useful for things like scheduled e-mails. Let’s say, for example, that you’re an event organiser, and you want to notify people a few days before the event. This feature enables you to tell Service Bus to simply send a message at that time (you could have a simple Azure function that then picked up the message and sent an e-mail).

If you’re new to Service Bus, or using it with .Net, then start here.

NuGet

The basic NuGet package you’ll need is here:

Microsoft.Azure.ServiceBus

Reading the Service Bus Message

For the purpose of this post, we’ll just set-up a basic console application that sends and receives the message; let’s start with the read:

private static Task ReadMessageEvent(string connectionString)
{
    var queueClient = new QueueClient(connectionString, QUEUE_NAME);

    var messageHandlerOptions = new MessageHandlerOptions(ExceptionHandler);
    queueClient.RegisterMessageHandler(handleMessage, messageHandlerOptions);

    return Task.CompletedTask;
}

private static Task ExceptionHandler(ExceptionReceivedEventArgs arg)
{
    Console.WriteLine("Something bad happened!");
    return Task.CompletedTask;
}

private static Task handleMessage(Message message, CancellationToken cancellation)
{
    string messageBody = Encoding.UTF8.GetString(message.Body);
    Console.WriteLine("Message received: {0}", messageBody);

    return Task.CompletedTask;
}

There’s not much to say here – this event will simply print a message to the console when it’s received.

Schedule the Service Bus Message

Now that we’ve set up a method to receive the messages, let’s send one. You could add this to the same console app (obviously it would have to occur after the Read!)

var queueClient = new QueueClient(connectionString, QUEUE_NAME);

string messageBody = $"{DateTime.Now}: Happy New Year! ({Guid.NewGuid()}) You won't get this until {dateTime}";
var message = new Message(Encoding.UTF8.GetBytes(messageBody));

long sequenceNumber = await queueClient.ScheduleMessageAsync(message, dateTime);
//await queueClient.CancelScheduledMessageAsync(sequenceNumber);

await queueClient.CloseAsync();

dateTime is simply the time that you wish to send the message; for example:

var dateTime = DateTime.UtcNow.AddSeconds(10)

Will send the message in 10 seconds.

The commented line above will then cancel the message from being sent – you only need to provide the sequence number (which you get from setting up the schedule in the first place).

References and A GitHub Example

For a working sample of this, please see here.

https://stackoverflow.com/questions/60437666/how-to-defer-a-azure-service-bus-message

Add certificate into WSL

I’ve recently been playing with WSL2, and one of the things that quickly bites you, is trying to move between your Linux distribution, and the main Windows system. In this particular instance, I had a certificate that I needed to apply to the WSL system.

The process turns out to be as follows

Copy cert to /usr/local/share/ca-certificates

For example, say your certificate was here:

c:\tmp\mycert.cer

You can copy this inside the WSL:

cp /mnt/c/tmp/mycert.cer /usr/local/share/ca-certificates

Once you’ve copied it here, run the update-ca-certificates command:

sudo update-ca-certificates

If it works, your certificate will be here:

/etc/ssl/certs

Notes

This was done on an Ubuntu distribution, but I’m not aware it would change based on that.

References

https://github.com/microsoft/WSL/issues/3161

Receiving Messages in Azure Service Bus

In this post I covered the basics of setting up a queue and sending a message to it. Here, I’m going to cover the options around receiving that message.

Essentially, there are two possibilities here: you can either set-up an event listener, or you can poll the queue directly, and receive the messages one at a time.

Option 1 – Events

The events option seems to be the one that Microsoft now prefer – essentially, you register a handler and then as the messages come in, you simply handle them inside an event. The code here looks something like this:

            var queueClient = new QueueClient(connectionString, "test-queue");

            var messageHandlerOptions = new MessageHandlerOptions(ExceptionHandler);
            queueClient.RegisterMessageHandler(handleMessage, messageHandlerOptions);

The event handlers:

        private static Task ExceptionHandler(ExceptionReceivedEventArgs arg)
        {
            Console.WriteLine("Something bad happened!");
            return Task.CompletedTask;
        }

        private static Task handleMessage(Message message, CancellationToken cancellation)
        {
            string messageBody = Encoding.UTF8.GetString(message.Body);
            Console.WriteLine("Message received: {0}", messageBody);

            return Task.CompletedTask;
        }

Option 2 – Polling

With this option, you simply ask for a message. You’ll need to use the approach for things like deferred messages (which I hope to cover in a future post):

            var messageReceiver = new MessageReceiver(connectionString, "test-queue", ReceiveMode.ReceiveAndDelete);            
            var message = await messageReceiver.ReceiveAsync();

            string messageBody = Encoding.UTF8.GetString(message.Body);            
            Console.WriteLine("Message received: {0}", messageBody);

Option 3 – Option 1, but cruelly force it into option 2

I thought I’d include this, although I would strongly advise against using it in most cases. If you wish, you can register an event, but force the event into a procedural call, so that you can await it finishing. You can do this by using the TaskCompletionSource. First, declare a TaskCompletionSource in your code (somewhere accessible):

private static TaskCompletionSource<bool> _taskCompletionSource;

Then, in handleMessage (see above), when you’ve received the message you want, set the result:

            if (message.CorrelationId == correlationId)
            {
                await client.CompleteAsync(message.SystemProperties.LockToken);

                _taskCompletionSource.SetResult(true);
            }

Finally, after you’ve registered the message handler, just await this task:

queueClient.RegisterMessageHandler(
                (message, cancellationToken) => handleMessage(correlationId, queueClient, message, cancellationToken), 
                messageHandlerOptions);

await _taskCompletionSource.Task;

References

Advanced Features with Azure Service Bus

Partial HTML

In Asp.Net Core server-side rendered HTML, you have the ability to use something called Partial Views. Essentially, these give you the ability to separate your View into, what some JS frameworks may call, components.

The way it works is incredibly simple. In your main page, you define some HTML, and reference the partial view – like so:

<div class="text-center">
    <div>
        <p>Merry Christmas - here's my partial view:</p>
    </div>
    <div>
        @await Html.PartialAsync("_MyPartial", Model.MyData)
    </div>
</div>

In your partial view, which is simply another cshtml file, you define the HTML fragment, along with the model; for example:

@model IEnumerable<MyDataType>

@foreach (var item in Model)
{
    <p>item.Data</p>
    <br />
}

References

https://docs.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-5.0