Tutorial: Create a minimal Orleans application

In this tutorial, you follow step-by-step instructions to create foundational moving parts common to most Orleans applications. It's designed to be self-contained and minimalistic.

This tutorial lacks appropriate error handling and other essential code that would be useful for a production environment. However, it should help you get a hands-on understanding of the common app structure for Orleans and allow you to focus your continued learning on the parts most relevant to you.

Prerequisites

Project setup

For this tutorial you're going to create four projects as part of the same solution:

  • Library to contain the grain interfaces.
  • Library to contain the grain classes.
  • Console app to host the Silo.
  • Console app to host the Client.

Create the structure in Visual Studio

Replace the default code with the code given for each project.

  1. Start by creating a Console App project in a new solution. Call the project part silo and name the solution OrleansHelloWorld. For more information on creating a console app, see Tutorial: Create a .NET console application using Visual Studio.
  2. Add another Console App project and name it Client.
  3. Add a Class Library and name it GrainInterfaces. For information on creating a class library, see Tutorial: Create a .NET class library using Visual Studio.
  4. Add another Class Library and name it Grains.

Delete default source files

  1. Delete Class1.cs from Grains.
  2. Delete Class1.cs from GrainInterfaces.

Add references

  1. Grains references GrainInterfaces.
  2. Silo references Grains.
  3. Client references GrainInterfaces.

Add Orleans NuGet packages

Project NuGet package
Silo Microsoft.Orleans.Server
Microsoft.Extensions.Logging.Console
Microsoft.Extensions.Hosting
Client Microsoft.Orleans.Client
Microsoft.Extensions.Logging.Console
Microsoft.Extensions.Hosting
Grain Interfaces Microsoft.Orleans.Sdk
Grains Microsoft.Orleans.Sdk
Microsoft.Extensions.Logging.Abstractions

Microsoft.Orleans.Server, Microsoft.Orleans.Client and Microsoft.Orleans.Sdk are metapackages that bring dependencies that you'll most likely need on the Silo and client. For more information on adding package references, see dotnet add package or Install and manage packages in Visual Studio using the NuGet Package Manager.

Define a grain interface

In the GrainInterfaces project, add an IHello.cs code file and define the following IHello interface in it:

namespace GrainInterfaces;

public interface IHello : IGrainWithIntegerKey
{
    ValueTask<string> SayHello(string greeting);
}

Define a grain class

In the Grains project, add a HelloGrain.cs code file and define the following class in it:

using GrainInterfaces;
using Microsoft.Extensions.Logging;

namespace Grains;

public class HelloGrain : Grain, IHello
{
    private readonly ILogger _logger;

    public HelloGrain(ILogger<HelloGrain> logger) => _logger = logger;

    ValueTask<string> IHello.SayHello(string greeting)
    {
        _logger.LogInformation("""
            SayHello message received: greeting = "{Greeting}"
            """,
            greeting);
        
        return ValueTask.FromResult($"""

            Client said: "{greeting}", so HelloGrain says: Hello!
            """);
    }
}

Create the Silo

To create the Silo project, add code to initialize a server that hosts and runs the grains—a silo. You use the localhost clustering provider, which allows you to run everything locally, without a dependency on external storage systems. For more information, see Local Development Configuration. In this example, you run a cluster with a single silo in it.

Add the following code to Program.cs of the Silo project:

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

IHostBuilder builder = Host.CreateDefaultBuilder(args)
    .UseOrleans(silo =>
    {
        silo.UseLocalhostClustering()
            .ConfigureLogging(logging => logging.AddConsole());
    })
    .UseConsoleLifetime();

using IHost host = builder.Build();

await host.RunAsync();

The preceding code:

Create the client

Finally, you need to configure a client for communicating with the grains, connect it to the cluster (with a single silo in it), and invoke the grain. The clustering configuration must match the one you used for the silo. For more information, see Clusters and Clients

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using GrainInterfaces;

IHostBuilder builder = Host.CreateDefaultBuilder(args)
    .UseOrleansClient(client =>
    {
        client.UseLocalhostClustering();
    })
    .ConfigureLogging(logging => logging.AddConsole())
    .UseConsoleLifetime();

using IHost host = builder.Build();
await host.StartAsync();

IClusterClient client = host.Services.GetRequiredService<IClusterClient>();

IHello friend = client.GetGrain<IHello>(0);
string response = await friend.SayHello("Hi friend!");

Console.WriteLine($"""
    {response}

    Press any key to exit...
    """);

Console.ReadKey();

await host.StopAsync();

Run the application

Build the solution and run the Silo. After you get the confirmation message that the Silo is running, run the Client.

To start the Silo from the command line, run the following command from the directory containing the Silo's project file:

dotnet run

You'll see numerous outputs as part of the startup of the Silo. After seeing the following message you're ready to run the client:

Application started. Press Ctrl+C to shut down.

From the client project directory, run the same .NET CLI command in a separate terminal window to start the client:

dotnet run

For more information on running .NET apps, see dotnet run. If you're using Visual Studio, you can configure multiple startup projects.

See also