When you create a new Durable Function in C#, you get some code which looks like this:
[FunctionName("Function1")]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var outputs = new List<string>();
// Replace "hello" with the name of your Durable Activity Function.
outputs.Add(await context.CallActivityAsync<string>("Function1_Hello", "Tokyo"));
outputs.Add(await context.CallActivityAsync<string>("Function1_Hello", "Seattle"));
outputs.Add(await context.CallActivityAsync<string>("Function1_Hello", "London"));
// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
return outputs;
}
[FunctionName("Function1_Hello")]
public static string SayHello([ActivityTrigger] string name, ILogger log)
{
log.LogInformation($"Saying hello to {name}.");
return $"Hello {name}!";
}
For brevity, I didn't include the HttpStart method here, it's not relevant to this discussion.
As you can see here, the execution of the program relies on some strings.
- The
FunctionName
attribute which decorates the functionSayHello
uses a string as the function's identifier. - When the function is called by the
context.CallActivityAsync
method, the first argument of the method is the magic string identifying the function.
About Magic strings
The term "Magic string" in programming refers to identifiers which are stored in a string format. They are deemed "magic" because they have a specific meaning critical to the execution of the program.
One thing I learned when I was learning programming is that in general, it's not a great idea to rely on "magic strings". They are dangerous, because a typo will not be detected by the compiler (compilers don't normally check the format of string variables). So the risk of getting problems at some point is higher.
Using the nameof
operator
In order to minimize the need of magic string, C# introduced the nameof
operator.
This can be used on a number of identifiers in the code. For example you can consider this code:
public class MyClass
{
public int MyProperty
{
get;
set;
}
public void MyMethod()
{
}
public void ShowNames()
{
Console.WriteLine(nameof(MyClass)); // prints "MyClass"
Console.WriteLine(nameof(MyProperty)); // prints "MyProperty"
Console.WriteLine(nameof(MyMethod)); // prints "MyMethod"
}
}
So what's the point?
The point of the nameof
operator is that it makes it easier to detect typos or changes in the code. For example, if you change the name of MyClass
to MyNewClass
, then the code won't compile unless you also change it inside the nameof
operator.
If you use refactoring tools, for example inside of Visual Studio, you can easily rename all the instances of the MyClass
identifier without having to search-and-replace.
Finally, it also makes it easier to navigate the code. If you are inside the nameof
operator, place your cursor on MyClass
and press F12 (in Visual Studio), this will take you directly inside the MyClass
class, even if it is located in another file.
Next steps for Durable Functions
For Durable Functions, my first step was always to modify the code like this:
[FunctionName(nameof(Function1))]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var outputs = new List<string>();
// Replace "hello" with the name of your Durable Activity Function.
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Tokyo"));
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Seattle"));
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "London"));
// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
return outputs;
}
[FunctionName(nameof(SayHello))]
public static string SayHello([ActivityTrigger] string name, ILogger log)
{
log.LogInformation($"Saying hello to {name}.");
return $"Hello {name}!";
}
These few changes (using nameof
) minimize the usage of magic strings and make it easier for me to navigate the code.
Changing the templates
In order to make this easier for everyone, I proposed a change to the C# templates for Durable Functions which, if accepted, should promote the usage of nameof
in the generated code.
Happy coding!
Laurent