Note this is not an Orleans post, not exactly — it’s just something I wanted to enhance on my Orleans Project, prior to moving on to demonstrating even more Orleans features! (Also, I need to learn more Orleans features to demonstrate!)

Prior to continuing with my Microsoft Orleans series, I wanted to make the Console app calling my Orleans Grains a bit simpler to use under the various examples. How can I do that? Polymorphism to the rescue!

In the previous few Orleans posts (links at bottom) I was swapping in/out calls to various Orleans methods in my Console app’s Program.Main.

In the above, as I added new Orleans functionality, I would simply tack on additional calls and/or comment out previously used functions. I’m not sure how far I will go with these Orleans examples, but since this is going on my third or fourth sample, I wanted a better way to manage these calls. In addition, give the user of the console application the option of choosing which function to play with, without having to require them to change code in between runs.

How can we do this? With a few simple steps:

Add a “menu” system to the console application

Have the menu system content consist of Orleans functionality that’s been implemented (The different Orleans features I’ve demonstrated thus far).

What does this mean? Basically — use interfaces and/or other abstractions (like abstract classes). How does it fit in with what I’m trying to accomplish? Well, I’m implementing a bunch of different examples that call into a Orleans cluster to demonstrate a feature of Orleans. So what kind of interface definition could I use? Let’s start with:

publicinterfaceIOrleansFunctions{TaskPerformFunction();}

In the above we have a method that does nothing but PerformFunction (whatever that ends up meaning), and returns a task.

Hmm, one other thing that’s going to be needed is to use a IClusterClient. There are a few ways I could accomplish this, keep it separate from the abstraction, and make it available to the implementing class, or just make it a parameter of the interface method. In the way I’m going to use the implemented classes later, I’m opting for making the IClusterClient a parameter on the interface method. Updated to look like:

Now, we can provide a description for each implementation — something we’ll be using as the “choices” within the console app menu.

So why go through all this trouble? Prior to going this route, I had a harder time implementing new functionality, without having to “move around a bunch of stuff” and continual editing of already existing classes. How can I avoid this now? Well, the only thing I need to do now (ideally, if I did this right), would be add a new implementation of IOrleansFunction and the system would pick it up without any fuss.

Why is polymorphism the neat? Because you can interact with interface methods without caring about the actual implementation. This makes your code more loosely coupled, and things like unit testing, and code maintainability are simpler; this is touched on a few posts elsewhere I’ve done (links at bottom).

The Implementations

I’ve done a few separate Orleans examples:

Hello World

Multiple Instantiations

Stateful Grains

Those seem perfect for new implementations IOrleansExamples!

This is going to mostly be copy/pasting from previous posts, just into their new abstraction, and providing a description.

HelloWorld:

publicclassHelloWorld:IOrleansFunction{publicstringDescription=>"Demonstrates the most basic Orleans function of 'Hello World'.";publicasyncTaskPerformFunction(IClusterClientclusterClient){vargrain=clusterClient.GetGrain<IHelloWorld>(Guid.NewGuid());Console.WriteLine("Hello! What should I call you?");varname=Console.ReadLine();Console.WriteLine(awaitgrain.SayHello(name));ConsoleHelpers.ReturnToMenu();}}

MultipleInstantiations:

publicclassMultipleInstantiations:IOrleansFunction{publicstringDescription=>"Demonstrates multiple instances of the same grain.";publicasyncTaskPerformFunction(IClusterClientclusterClient){vargrain=clusterClient.GetGrain<IHelloWorld>(Guid.NewGuid());vargrain2=clusterClient.GetGrain<IHelloWorld>(Guid.NewGuid());Console.WriteLine($"{awaitgrain.SayHello("1")}");Console.WriteLine($"{awaitgrain2.SayHello("2")}");Console.WriteLine($"{awaitgrain.SayHello("3")}");ConsoleHelpers.ReturnToMenu();}}

StatefulWork:

publicclassStatefulWork:IOrleansFunction{publicstringDescription=>"Demonstrates using stateful grains with numerous instantiations.";publicasyncTaskPerformFunction(IClusterClientclusterClient){varkritnerGrain=clusterClient.GetGrain<IVisitTracker>("[kritner@gmail.com](mailto:kritner@gmail.com)");varnotKritnerGrain=clusterClient.GetGrain<IVisitTracker>("[notKritner@gmail.com](mailto:notKritner@gmail.com)");awaitPrettyPrintGrainVisits(kritnerGrain);awaitPrettyPrintGrainVisits(notKritnerGrain);ConsoleHelpers.LineSeparator();Console.WriteLine("Ayyy some people are visiting!");awaitkritnerGrain.Visit();awaitkritnerGrain.Visit();awaitnotKritnerGrain.Visit();ConsoleHelpers.LineSeparator();awaitPrettyPrintGrainVisits(kritnerGrain);awaitPrettyPrintGrainVisits(notKritnerGrain);ConsoleHelpers.LineSeparator();Console.Write("ayyy kritner's visiting even more!");for(inti=0;i<5;i++){awaitkritnerGrain.Visit();}ConsoleHelpers.LineSeparator();awaitPrettyPrintGrainVisits(kritnerGrain);awaitPrettyPrintGrainVisits(notKritnerGrain);ConsoleHelpers.ReturnToMenu();}privatestaticasyncTaskPrettyPrintGrainVisits(IVisitTrackergrain){Console.WriteLine($"{grain.GetPrimaryKeyString()} has visited {awaitgrain.GetNumberOfVisits()} times");}}

The Menu

Now that our implementations of IOrleansFunction are complete, we simply need to plug them into our to be created menu system!

With the menu system, the user should be able to enter a number that corresponds to a specific Orleans feature, then that feature should execute.

A few things we’ll need for the menu:

A collection of Orleans Features to list

A way to display the features

A way to execute a feature

A way for the user to try multiple features, and exit from the menu

A collection of Orleans Features to list

We’ll need to keep a collection of our OrleansFeatures and what better way to do that than with another interface! I’ve defined a new interface IOrleansFunctionProvider as:

In the above, we’re just newing up and returning a list of each one of our current OrleansFunctions that we created earlier.

“A way to display the features” and “a way to execute a feature”

Now we need a way to display features on our menu — luckily for us we thought ahead, and created a Description on our IOrleansFeature interface.

That coupled along with our IOrleansFeatureProvider, means we can enumerate what the provider returns us, printing out each description. We’ll be using a [slightly hacky(?)] method of assigning a feature to a number via the collection’s index, but /shrug, that’s ok right?

Let’s start a new class OrleansExamples:

publicclassOrleansExamples{privatereadonlyIOrleansFunctionProvider_orleansFunctionProvider;publicOrleansExamples(IOrleansFunctionProviderorleansFunctionProvider){_orleansFunctionProvider=orleansFunctionProvider;}publicasyncTaskChooseFunction(IClusterClientclusterClient){varorleansFunctions=_orleansFunctionProvider.GetOrleansFunctions();varinput=string.Empty;while(true){Console.WriteLine("Pick a function to use for Orleans demonstration:");ConsoleHelpers.LineSeparator();for(inti=0;i<orleansFunctions.Count;i++){Console.WriteLine($" {i} - {orleansFunctions[i].Description}");}ConsoleHelpers.LineSeparator();input=Console.ReadLine();if(!int.TryParse(input,outvarinputResult)){Console.WriteLine("Invalid Input. Please input a number.");continue;}try{awaitorleansFunctions[inputResult].PerformFunction(clusterClient);ConsoleHelpers.LineSeparator();}catch(ArgumentOutOfRangeException){Console.WriteLine("Invalid Input. Please ensure you pick a number/function from the provided list.");ConsoleHelpers.LineSeparator();}}return;}}

In the above, we’re using the results provided by our IOrleansFunctionProvider, enumerating them, printing out the IList<IOrleansFunction> index along with the IOrleansFunction.Description, parsing the user input, and attempting to invoke the appropriate method on the collection as per the index. Notice how we are only working with interfaces here as it pertains to IOrleansFunctionProvider and IOrleansFunction. This class has no idea what the implementors are, because it doesn’t really matter as to the scope of this class (loose coupling).

A way for the user to try multiple features, and exit from the menu

It’s a simple matter of adding an if conditional to check for a specific entry to exit the menu, as currently, our menu will loop indefinitely.

Add a new const to the class:

privateconststringESCAPE_STRING="-1";

and a new conditional within the while loop to check for that escape string, prior to executing the grain sample:

privatestaticasyncTask<int>RunMainAsync(){try{using(varclient=awaitStartClientWithRetries()){//await DoClientWork(client);awaitDoStatefulWork(client);Console.ReadKey();}return0;}catch(Exceptione){Console.WriteLine(e);Console.ReadKey();return1;}}privatestaticasyncTaskDoClientWork(IClusterClientclient){// example of calling grains from the initialized clientvargrain=client.GetGrain<IHelloWorld>(Guid.NewGuid());vargrain2=client.GetGrain<IHelloWorld>(Guid.NewGuid());Console.WriteLine($"{awaitgrain.SayHello("1")}");Console.WriteLine($"{awaitgrain2.SayHello("2")}");Console.WriteLine($"{awaitgrain.SayHello("3")}");PrintSeparatorThing();}privatestaticasyncTaskDoStatefulWork(IClusterClientclient){varkritnerGrain=client.GetGrain<IVisitTracker>("[kritner@gmail.com](mailto:kritner@gmail.com)");varnotKritnerGrain=client.GetGrain<IVisitTracker>("[notKritner@gmail.com](mailto:notKritner@gmail.com)");awaitPrettyPrintGrainVisits(kritnerGrain);awaitPrettyPrintGrainVisits(notKritnerGrain);PrintSeparatorThing();Console.WriteLine("Ayyy some people are visiting!");awaitkritnerGrain.Visit();awaitkritnerGrain.Visit();awaitnotKritnerGrain.Visit();PrintSeparatorThing();awaitPrettyPrintGrainVisits(kritnerGrain);awaitPrettyPrintGrainVisits(notKritnerGrain);PrintSeparatorThing();Console.Write("ayyy kritner's visiting even more!");for(inti=0;i<5;i++){awaitkritnerGrain.Visit();}PrintSeparatorThing();awaitPrettyPrintGrainVisits(kritnerGrain);awaitPrettyPrintGrainVisits(notKritnerGrain);}privatestaticasyncTaskPrettyPrintGrainVisits(IVisitTrackergrain){Console.WriteLine($"{grain.GetPrimaryKeyString()} has visited {awaitgrain.GetNumberOfVisits()} times");}privatestaticvoidPrintSeparatorThing(){Console.WriteLine($"{Environment.NewLine}-----{Environment.NewLine}");}

That’s a lot of removed code! (Granted, a lot of that code was refactored into the individual IOrleansFunctions. But we never have to look at all that code again!

So what does it all look like?

Application Start:

Application start

Choosing the “Hello World” example (choice 0):

Choosing the Hello World Example

Choosing the stateful grains example (choice 2):

Choosing the Stateful Grains example

Exiting (choice -1):

Exiting

This ended up being a longer post than I intended, but hopefully it will help convey how working with interfaces can help better abstract and break down the work you need to do. That coupled with other “features” of abstraction such as unit testing, loose coupling, and an easier “high level view” of an applications architecture, are why I enjoy working on abstractions so much.