Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NServiceBus step by step tutorial cleanup #6903

Merged
merged 10 commits into from
Nov 18, 2024
Merged
3 changes: 2 additions & 1 deletion transports/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ Unicast-only transports do not have the notion of topics, exchanges, or similar

Unicast-only transports include:

- [Azure Storage Queues](/transports/azure-storage-queues/)
- [MSMQ](/transports/msmq/)
- [SQL Server version 4 and below](/transports/sql/)
- [Amazon SQS version 4 and below](/transports/sqs/)
- [Azure Storage Queues version 9 and below](/transports/azure-storage-queues/)

## Multicast-enabled transports

Expand All @@ -56,3 +56,4 @@ Multicast-enabled transports include:
- [RabbitMQ](/transports/rabbitmq/)
- [SQL Server version 5 and above](/transports/sql/)
- [Amazon SQS version 5 and above](/transports/sqs/)
- [Azure Storage Queues version 10 and above](/transports/azure-storage-queues/)
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,28 @@
using NServiceBus;

namespace Core_9;
#pragma warning disable 1998

#region EmptyProgram
class Program
{
static async Task Main(string[] args)
{

}
}
#endregion
#pragma warning disable 1998

class StepByStep
{
#region Main

static async Task Main(string[] args)
static async Task Program(string[] args)
{
#region Program
Console.Title = "ClientUI";

var builder = Host.CreateApplicationBuilder(args);

var endpointConfiguration = new EndpointConfiguration("ClientUI");
endpointConfiguration.UseSerialization<SystemJsonSerializer>();

var transport = endpointConfiguration.UseTransport(new LearningTransport());

builder.UseNServiceBus(endpointConfiguration);

await builder.Build().RunAsync();
#endregion
}
#endregion

static async Task Steps(string[] args)
{
Expand Down Expand Up @@ -66,4 +56,4 @@ static async Task Steps(string[] args)
}
}

#pragma warning restore 1998
#pragma warning restore 1998
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting;
using NServiceBus;
using System;

namespace ClientUI;
class Program
{
static async Task Main(string[] args)
{
Console.Title = "ClientUI";
Console.Title = "ClientUI";

var builder = Host.CreateApplicationBuilder(args);
var builder = Host.CreateApplicationBuilder(args);

var endpointConfiguration = new EndpointConfiguration("ClientUI");
var endpointConfiguration = new EndpointConfiguration("ClientUI");

endpointConfiguration.UseSerialization<SystemJsonSerializer>();
endpointConfiguration.UseSerialization<SystemJsonSerializer>();

var transport = endpointConfiguration.UseTransport(new LearningTransport());
var transport = endpointConfiguration.UseTransport(new LearningTransport());

builder.UseNServiceBus(endpointConfiguration);
builder.UseNServiceBus(endpointConfiguration);

await builder.Build().RunAsync();
}
}
await builder.Build().RunAsync();
23 changes: 10 additions & 13 deletions tutorials/nservicebus-step-by-step/1-getting-started/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ In this first lesson, which should take 10-15 minutes, you will create your firs

Let's build something simple to give NServiceBus a try.

This tutorial uses NServiceBus version 8, .NET 6, and assumes an up-to-date installation of [Visual Studio 2022](https://www.visualstudio.com/downloads/).
This tutorial uses NServiceBus version 9, .NET 8, and assumes an up-to-date installation of [Visual Studio 2022](https://www.visualstudio.com/downloads/).

> [!NOTE]
andreasohlund marked this conversation as resolved.
Show resolved Hide resolved
> NServiceBus 8 also [supports .NET Framework 4.7.2 or higher](/nservicebus/operations/dotnet-framework-version-requirements.md), but new applications should use NServiceBus 9 and [.NET 8 or higher](https://devblogs.microsoft.com/dotnet/net-core-is-the-future-of-net/).
Expand All @@ -47,20 +47,18 @@ This adds an NServiceBus.Core and NSeviceBus.Extension.Hosting assembly referenc

We're ready to create a [**messaging endpoint**](/nservicebus/endpoints/). A messaging endpoint (or just **endpoint**) is a logical component capable of sending and receiving messages. An endpoint is hosted within a process, which in this case is a simple console application, but could be a web application or other .NET process.

In the **Program.cs** file, modify the code to look like the following:

snippet: EmptyProgram

> [!NOTE]
> For the sake of brevity, code snippets in this tutorial do not contain the `using` statements needed to import namespaces.
> If you're using Visual Studio, unknown references such as `Task` or NServiceBus types will generate a "red squiggly" underline effect.
> If you hover or click on the red squiggly, you can click on the "light bulb" icon or press <span style="white-space: nowrap"><kbd>Ctrl</kbd> + <kbd>.</kbd></span> to see the available fixes and insert the appropriate `using` statements for the missing namespaces.
>
> Alternatively, in the code snippet's **Copy/Edit** menu you will find a **Copy usings** item that will copy the namespaces used by the snippet to your clipboard.

Now that we have a `Main` method, let's discuss the importance of each line we're going to add to it. First, add the following code to the `Main` method:
We're going to use [the top-level statements feature for this tutorial](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/top-level-statements) to minimise the boilerplate code we have to write.
andreasohlund marked this conversation as resolved.
Show resolved Hide resolved

First, add the following code to the `Program.cs`:

snippet: Main
snippet: Program

Now, let's go line-by-line and find out exactly what each step is doing.

Expand All @@ -74,7 +72,7 @@ When running multiple console apps in the same solution, giving each one a name

snippet: Setup

The host is typically configured, built, and run by code in the `Program` class. The `CreateApplicationBuilder` method creates and configures a builder object.
The host is typically configured, built, and run by code in `Program.cs`. The `CreateApplicationBuilder` method creates and configures a builder object.

#### EndpointConfiguration

Expand All @@ -93,18 +91,17 @@ Capturing the `transport` settings in a variable as shown will make things easie

### Starting up

At the end of the `Main` method, after the configuration code, the following lines will start the endpoint and keep it running until you press <kbd>Ctrl+C</kbd> to shut it down.
At the end of the file, after the configuration code, the following lines will start the endpoint and keep it running until you press <kbd>Ctrl+C</kbd> to shut it down.

snippet: Startup

The endpoint is initialized according to the settings defined by the `EndpointConfiguration` class. Once the endpoint starts, any changes made to the configuration won't be applied until you restart the endpoint.

When you run the endpoint for the first time, the endpoint will:

* Display its logging information, which is [written to a file, Trace, and the Console](/nservicebus/logging/#default-logging). NServiceBus also logs to multiple levels, so you can [change the log level](/nservicebus/logging/#default-logging-changing-the-defaults-changing-the-logging-level) from `INFO` to `DEBUG` in order to get more information.
* Display the [status of your license](/nservicebus/licensing/).
* Attempt to add the current user to the "Performance Monitor Users" group so that it can write [performance counters](/monitoring/metrics/performance-counters.md) to track its health and progress.
* Create file-based "queues" in the `.learningtransport` directory inside your solution directory. We recommend adding `.learningtransport` to your source control system's ignore file.
* Display its logging information via [Microsoft.Extensions.Logging](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging)
* Display the [status of your license](/nservicebus/licensing/).
* Create file-based "queues" in the `.learningtransport` directory inside your solution directory. We recommend adding `.learningtransport` to your source control system's ignore file.

## Summary

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,17 @@ static async Task Steps(string[] args)
await builder.Build().RunAsync();
#endregion
}

static Task RunLoop(IEndpointInstance endpoint)
{
return Task.CompletedTask;
}
}

#region InputLoopService

public class InputLoopService(IMessageSession messageSession, ILogger<InputLoopService> logger) : BackgroundService
andreasohlund marked this conversation as resolved.
Show resolved Hide resolved
public class InputLoopService(IMessageSession messageSession) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (true)
{
logger.LogInformation("Press 'P' to place an order, or 'Q' to quit.");
Console.WriteLine("Press 'P' to place an order, or 'Q' to quit.");
var key = Console.ReadKey();
Console.WriteLine();

Expand All @@ -72,16 +67,16 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
};

// Send the command
logger.LogInformation("Sending PlaceOrder command, OrderId = {OrderId}", command.OrderId);
await messageSession.SendLocal(command);
Console.WriteLine($"Sending PlaceOrder command, OrderId = {command.OrderId}");
await messageSession.SendLocal(command, stoppingToken);

break;

case ConsoleKey.Q:
return;

default:
logger.LogInformation("Unknown input. Please try again.");
Console.WriteLine("Unknown input. Please try again.");
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@

namespace ClientUI;

public class InputLoopService(IMessageSession messageSession, ILogger<InputLoopService> logger) : BackgroundService
public class InputLoopService(IMessageSession messageSession) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (true)
{
logger.LogInformation("Press 'P' to place an order, or 'Q' to quit.");
Console.WriteLine("Press 'P' to place an order, or 'Q' to quit.");
var key = Console.ReadKey();
Console.WriteLine();

Expand All @@ -28,16 +28,16 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
};

// Send the command
logger.LogInformation("Sending PlaceOrder command, OrderId = {OrderId}", command.OrderId);
await messageSession.SendLocal(command);
Console.WriteLine($"Sending PlaceOrder command, OrderId = {command.OrderId}");
await messageSession.SendLocal(command, stoppingToken);

break;

case ConsoleKey.Q:
return;

default:
logger.LogInformation("Unknown input. Please try again.");
Console.WriteLine("Unknown input. Please try again.");
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
using System;
using System.Threading.Tasks;
using ClientUI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NServiceBus;

namespace ClientUI;
Console.Title = "ClientUI";

class Program
{
static async Task Main(string[] args)
{
Console.Title = "ClientUI";
var builder = Host.CreateApplicationBuilder(args);

var builder = Host.CreateApplicationBuilder(args);
var endpointConfiguration = new EndpointConfiguration("ClientUI");

var endpointConfiguration = new EndpointConfiguration("ClientUI");
endpointConfiguration.UseSerialization<SystemJsonSerializer>();

endpointConfiguration.UseSerialization<SystemJsonSerializer>();
endpointConfiguration.UseTransport(new LearningTransport());

endpointConfiguration.UseTransport(new LearningTransport());
builder.UseNServiceBus(endpointConfiguration);

builder.UseNServiceBus(endpointConfiguration);
builder.Services.AddHostedService<InputLoopService>();

builder.Services.AddHostedService<InputLoopService>();

await builder.Build().RunAsync();
}
}
await builder.Build().RunAsync();
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ Sending and receiving messages is a central characteristic of any NServiceBus sy

In the next 15-20 minutes, you will learn how to define messages and message handlers, send and receive a message locally, and use the built-in logging capabilities.


## What is a message

A [**message**](/nservicebus/messaging/messages-events-commands.md) is a collection of data sent via one-way communication between two endpoints. In NServiceBus, we define messages via simple classes.
Expand Down Expand Up @@ -59,7 +58,6 @@ Additionally, message assemblies should have no dependencies other than librarie

Following these guidelines will make your message contracts easy to evolve in the future.


## Processing messages

To process a message, we create a [**message handler**](/nservicebus/handlers/), a class that implements `IHandleMessages<T>`, where `T` is a message type. A message handler looks like this:
Expand All @@ -86,7 +84,6 @@ Now let's take the solution we started in the [last lesson](../1-getting-started

![Exercise 2 Diagram](diagram.svg)


### Create a messages assembly

To share messages between endpoints, they need to be self-contained in a separate assembly. Let's create that assembly now.
Expand All @@ -97,7 +94,6 @@ To share messages between endpoints, they need to be self-contained in a separat
1. Add the NServiceBus NuGet package to the **Messages** project.
1. In the **ClientUI** project, add a reference to the **Messages** project.


### Create a message

Let's create our first command.
Expand All @@ -113,7 +109,6 @@ When complete, your `PlaceOrder` class should look like the following:

snippet: PlaceOrder


### Create a handler

Now that we've defined a message, we can create a corresponding message handler. For now, let's handle the message locally within the **ClientUI** endpoint.
Expand Down Expand Up @@ -153,11 +148,10 @@ Let's take a closer look at the case when we want to place an order. In order to

Because `SendLocal()` returns a `Task`, we need to be sure to `await` it properly.

Now let's modify the `Main` method and register the `InputLookService` in the host:
Now let's modify `Program.cs` method and register the `InputLookService` in the host:

snippet: AddInputLoopService


### Running the solution

Now we are ready to run the solution. Whenever we press <kbd>P</kbd> on the terminal, a command message is sent and then processed by a handler class in the same project.
Expand All @@ -183,7 +177,6 @@ You should see something similar to this on your console:

Note how after sending a message, the prompt from `ClientUI.Program` is displayed _before_ the `ClientUI.PlaceOrderHandler` acknowledges receipt of the message. This is because rather than calling the `Handle` method as a direct method call, the message is sent asynchronously, and then control immediately returns to the `RunLoop` which repeats the prompt. It isn't until a bit later, when the message is received and processed, that we see the `Received PlaceOrder` notification.


## Summary

In this lesson we learned about messages, message assemblies, and message handlers. We created a message and a handler and we used `SendLocal()` to send the message to the same endpoint.
Expand Down
Loading