Tutorial: Create a minimal Orleans application

This tutorial provides step-by-step instructions for creating a basic functioning Orleans application. It is designed to be self-contained and minimalistic, with the following traits:

  • It relies only on NuGet packages.
  • It has been tested in Visual Studio 2022 using Orleans 7.0.
  • It has no reliance on external storage.

Keep in mind this is a tutorial and lacks appropriate error handling and other essential code that would be useful for a production environment. However, it should help readers get a hands-on understanding of the common app structure for Orleans and allow them to focus their continued learning on the parts most relevant to them.

Project setup

For this tutorial we're going to create four projects:

  • 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

You will replace the default code with the code given for each project, below. You will also probably need to add using statements.

  1. Start by creating a Console App project in a new solution. Call the project part Silo and name the solution OrleansHelloWorld.
  2. Add another Console App project and name it Client.
  3. Add a Class Library and name it GrainInterfaces.
  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 meta-packages that bring dependency that you will most likely need on the Silo and client-side. For more information on adding package references, see dotnet add package or Install and manage packages in Visual Studio using the NuGet Package Manage.

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

At this step, we add code to initialize a server that will host and run our grains—a silo. We will use the development clustering provider here, so that we can run everything locally, without a dependency on external storage systems. For more information, see Local Development Configuration. In this example, you'll 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;

try
{
    using IHost host = await StartSiloAsync();
    Console.WriteLine("\n\n Press Enter to terminate...\n\n");
    Console.ReadLine();

    await host.StopAsync();

    return 0;
}
catch (Exception ex)
{
    Console.WriteLine(ex);
    return 1;
}

static async Task<IHost> StartSiloAsync()
{
    var builder = new HostBuilder()
        .UseOrleans(silo =>
        {
            silo.UseLocalhostClustering()
                .ConfigureLogging(logging => logging.AddConsole());
        });

    var host = builder.Build();
    await host.StartAsync();

    return host;
}

Create the client

Finally, we need to configure a client for communicating with our grains, connect it to the cluster (with a single silo in it), and invoke the grain. Note that the clustering configuration must match the one we 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;

try
{
    using IHost host = await StartClientAsync();
    var client = host.Services.GetRequiredService<IClusterClient>();

    await DoClientWorkAsync(client);
    Console.ReadKey();

    await host.StopAsync();

    return 0;
}
catch (Exception e)
{
    Console.WriteLine($$"""
        Exception while trying to run client: {{e.Message}}
        Make sure the silo the client is trying to connect to is running.
        Press any key to exit.
        """);
    
    Console.ReadKey();
    return 1;
}

static async Task<IHost> StartClientAsync()
{
    var builder = new HostBuilder()
        .UseOrleansClient(client =>
        {
            client.UseLocalhostClustering();
        })
        .ConfigureLogging(logging => logging.AddConsole());

    var host = builder.Build();
    await host.StartAsync();

    Console.WriteLine("Client successfully connected to silo host \n");

    return host;
}

static async Task DoClientWorkAsync(IClusterClient client)
{
    var friend = client.GetGrain<IHello>(0);
    var response = await friend.SayHello("Good morning, HelloGrain!");

    Console.WriteLine($"\n\n{response}\n\n");
}

Run the application

Build the solution and run the Silo. After you get the confirmation message that the Silo is running ("Press enter to terminate..."), run the Client. For more information, see How to: Set multiple startup projects.

See also