title: Tutorial One

Tutorial One - Creating 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 2017 using Orleans 2.2.0
  • It has no reliance on external storage

Keep in mind that this is only a tutorial and lacks appropriate error handling and other goodies that would be useful for a production environment. However, it should help the readers get a real hands-on with regards to the structure of 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 4 projects:

  • a library to contain the grain interfaces
  • a library to contain the grain classes
  • a console application to host our Silo
  • a console application to host our Client

After following this tutorial, the complete Solution should look like this:

Tutorial One - Creating a Minimal Orleans Application - 图1

Create the structure in Visual Studio

Note: You can use the default project types in c# for each of these projects. 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 (.NET Core) project in a new solution. Call the project part Silo and name the solution OrleansHelloWorld.
  2. Add another Console App (.NET Core) project and name it Client.
  3. Add a Class Library (.NET Standard) and name it GrainInterfaces.
  4. Add another Class Library (.NET Standard) 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 GrainInterfaces and Grains.
  3. Client references GrainInterfaces.

Add Orleans NuGet Packages

Project Nuget Package
Silo Microsoft.Orleans.Server
Silo Microsoft.Extensions.Logging.Console
Client Microsoft.Extensions.Logging.Console
Client Microsoft.Orleans.Client
Grain Interfaces Microsoft.Orleans.Core.Abstractions
Grain Interfaces Microsoft.Orleans.CodeGenerator.MSBuild
Grains Microsoft.Orleans.CodeGenerator.MSBuild
Grains Microsoft.Orleans.Core.Abstractions
Grains Microsoft.Extensions.Logging.Abstractions

Microsoft.Orleans.Server and Microsoft.Orleans.Client are meta-packages that bring dependency that you will most likely need on the Silo and Client side.

Microsoft.Orleans.Core.Abstractions is needed everywhere. It included in both Microsoft.Orleans.Server and Microsoft.Orleans.Client.

Microsoft.Orleans.CodeGenerator.MSBuild automatically generates code that is needed to make calls to grains across machine boundaries. So it is needed in both GrainInterfaces and Grains projects.

Define a Grain Interface

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

  1. using System.Threading.Tasks;
  2. namespace OrleansBasics
  3. {
  4. public interface IHello : Orleans.IGrainWithIntegerKey
  5. {
  6. Task<string> SayHello(string greeting);
  7. }
  8. }

Define a Grain Class

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

  1. using Microsoft.Extensions.Logging;
  2. using System.Threading.Tasks;
  3. namespace OrleansBasics
  4. {
  5. public class HelloGrain : Orleans.Grain, IHello
  6. {
  7. private readonly ILogger logger;
  8. public HelloGrain(ILogger<HelloGrain> logger)
  9. {
  10. this.logger = logger;
  11. }
  12. Task<string> IHello.SayHello(string greeting)
  13. {
  14. logger.LogInformation($"\n SayHello message received: greeting = '{greeting}'");
  15. return Task.FromResult($"\n Client said: '{greeting}', so HelloGrain says: Hello!");
  16. }
  17. }
  18. }

Create the Silo – Program.cs

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. You can find more information about that in the Local Development Configuration page of the Orleans documentation. We will run a cluster with a single silo in it.

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

  1. using System;
  2. using System.Threading.Tasks;
  3. using Microsoft.Extensions.Logging;
  4. using Orleans;
  5. using Orleans.Configuration;
  6. using Orleans.Hosting;
  7. namespace OrleansBasics
  8. {
  9. public class Program
  10. {
  11. public static int Main(string[] args)
  12. {
  13. return RunMainAsync().Result;
  14. }
  15. private static async Task<int> RunMainAsync()
  16. {
  17. try
  18. {
  19. var host = await StartSilo();
  20. Console.WriteLine("\n\n Press Enter to terminate...\n\n");
  21. Console.ReadLine();
  22. await host.StopAsync();
  23. return 0;
  24. }
  25. catch (Exception ex)
  26. {
  27. Console.WriteLine(ex);
  28. return 1;
  29. }
  30. }
  31. private static async Task<ISiloHost> StartSilo()
  32. {
  33. // define the cluster configuration
  34. var builder = new SiloHostBuilder()
  35. .UseLocalhostClustering()
  36. .Configure<ClusterOptions>(options =>
  37. {
  38. options.ClusterId = "dev";
  39. options.ServiceId = "OrleansBasics";
  40. })
  41. .ConfigureApplicationParts(parts => parts.AddApplicationPart(typeof(HelloGrain).Assembly).WithReferences())
  42. .ConfigureLogging(logging => logging.AddConsole());
  43. var host = builder.Build();
  44. await host.StartAsync();
  45. return host;
  46. }
  47. }
  48. }

Create the Client – Program.cs

Finally, we need to configure a client for communicating with our grains, connect it to the 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. There is more information about the client in the Clusters and Clients section of the Orleans documentation.

  1. using Microsoft.Extensions.Logging;
  2. using Orleans;
  3. using Orleans.Configuration;
  4. using System;
  5. using System.Threading.Tasks;
  6. namespace OrleansBasics
  7. {
  8. public class Program
  9. {
  10. static int Main(string[] args)
  11. {
  12. return RunMainAsync().Result;
  13. }
  14. private static async Task<int> RunMainAsync()
  15. {
  16. try
  17. {
  18. using (var client = await ConnectClient())
  19. {
  20. await DoClientWork(client);
  21. Console.ReadKey();
  22. }
  23. return 0;
  24. }
  25. catch (Exception e)
  26. {
  27. Console.WriteLine($"\nException while trying to run client: {e.Message}");
  28. Console.WriteLine("Make sure the silo the client is trying to connect to is running.");
  29. Console.WriteLine("\nPress any key to exit.");
  30. Console.ReadKey();
  31. return 1;
  32. }
  33. }
  34. private static async Task<IClusterClient> ConnectClient()
  35. {
  36. IClusterClient client;
  37. client = new ClientBuilder()
  38. .UseLocalhostClustering()
  39. .Configure<ClusterOptions>(options =>
  40. {
  41. options.ClusterId = "dev";
  42. options.ServiceId = "OrleansBasics";
  43. })
  44. .ConfigureLogging(logging => logging.AddConsole())
  45. .Build();
  46. await client.Connect();
  47. Console.WriteLine("Client successfully connected to silo host \n");
  48. return client;
  49. }
  50. private static async Task DoClientWork(IClusterClient client)
  51. {
  52. // example of calling grains from the initialized client
  53. var friend = client.GetGrain<IHello>(0);
  54. var response = await friend.SayHello("Good morning, HelloGrain!");
  55. Console.WriteLine($"\n\n{response}\n\n");
  56. }
  57. }
  58. }

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. Success looks like this:

Tutorial One - Creating a Minimal Orleans Application - 图2

Further Reading