-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added Counter Interceptor Sample #77
Changes from 8 commits
d678366
f428229
26fdbee
c68e75d
ec9da55
31c0743
ca90698
7ac67a9
34c83af
7682937
37692a7
d548089
d113bd3
c0131fd
4557ffe
c7b6e31
f3a82cc
078bbe6
fc4fada
c6f5e55
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,87 @@ | ||||||||||||||||||||||||
namespace TemporalioSamples.CounterInterceptor; | ||||||||||||||||||||||||
public record ClientCounts | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should not have a top-level type declared in any non- There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. Fixed. |
||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
public uint Executions { get; internal set; } | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public uint Signals { get; internal set; } | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public uint Queries { get; internal set; } | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public override string ToString() | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
return | ||||||||||||||||||||||||
"\n\tTotal Number of Workflow Exec: " + Executions + | ||||||||||||||||||||||||
"\n\tTotal Number of Signals: " + Signals + | ||||||||||||||||||||||||
"\n\tTotal Number of Queries: " + Queries; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Can break up long line, but might as well use interpolation, and might as well use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public class ClientCounter | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why have this separate class? Why not just use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refactored. |
||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
private const string NumberOfWorkflowExecutions = "numOfWorkflowExec"; | ||||||||||||||||||||||||
private const string NumberOfSignals = "numOfSignals"; | ||||||||||||||||||||||||
private const string NumberOfQueries = "numOfQueries"; | ||||||||||||||||||||||||
private static Dictionary<string, ClientCounts> perWorkflowIdDictionary = | ||||||||||||||||||||||||
new(); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public static string Info() | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
return string.Join( | ||||||||||||||||||||||||
"\n", | ||||||||||||||||||||||||
perWorkflowIdDictionary.Select(kvp => $"** Workflow ID: {kvp.Key} {kvp.Value}")); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public static uint NumOfWorkflowExecutions(string workflowId) | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
return perWorkflowIdDictionary[workflowId].Executions; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public static uint NumOfSignals(string workflowId) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
return perWorkflowIdDictionary[workflowId].Signals; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public static uint NumOfQueries(string workflowId) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
return perWorkflowIdDictionary[workflowId].Queries; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public void AddStartInvocation(string workflowId) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
Add(workflowId, NumberOfWorkflowExecutions); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public void AddSignalInvocation(string workflowId) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
Add(workflowId, NumberOfSignals); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public void AddQueryInvocation(string workflowId) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
Add(workflowId, NumberOfQueries); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
private void Add(string workflowId, string type) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
if (!perWorkflowIdDictionary.TryGetValue(workflowId, out ClientCounts? value)) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
value = new ClientCounts(); | ||||||||||||||||||||||||
perWorkflowIdDictionary.Add(workflowId, value); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
switch (type) | ||||||||||||||||||||||||
{ | ||||||||||||||||||||||||
case NumberOfWorkflowExecutions: | ||||||||||||||||||||||||
value.Executions++; | ||||||||||||||||||||||||
break; | ||||||||||||||||||||||||
case NumberOfQueries: | ||||||||||||||||||||||||
value.Queries++; | ||||||||||||||||||||||||
break; | ||||||||||||||||||||||||
case NumberOfSignals: | ||||||||||||||||||||||||
value.Signals++; | ||||||||||||||||||||||||
break; | ||||||||||||||||||||||||
default: | ||||||||||||||||||||||||
throw new NotImplementedException("Unknown type: " + type); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
rross marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,19 @@ | ||||||||||||
namespace TemporalioSamples.CounterInterceptor; | ||||||||||||
|
||||||||||||
using System.Diagnostics; | ||||||||||||
using Temporalio.Activities; | ||||||||||||
|
||||||||||||
public class MyActivities | ||||||||||||
{ | ||||||||||||
[Activity] | ||||||||||||
public string SayHello(string name, string title) | ||||||||||||
{ | ||||||||||||
return "Hello " + title + " " + name; | ||||||||||||
} | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Use string interpolation and single-statement syntax (will stop commenting on this, but it applies project wide) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump, this was not addressed |
||||||||||||
|
||||||||||||
[Activity] | ||||||||||||
public string SayGoodBye(string name, string title) | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are inconsistent with your single-line methods, this should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed |
||||||||||||
{ | ||||||||||||
return "Goodbye " + title + " " + name; | ||||||||||||
} | ||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
namespace TemporalioSamples.CounterInterceptor; | ||
|
||
using Temporalio.Workflows; | ||
|
||
[Workflow] | ||
public class MyChildWorkflow | ||
{ | ||
private readonly ActivityOptions activityOptions = new() | ||
{ | ||
StartToCloseTimeout = TimeSpan.FromSeconds(10), | ||
}; | ||
|
||
[WorkflowRun] | ||
public async Task<string> RunAsync(string name, string title) => | ||
await Workflow.ExecuteActivityAsync((MyActivities act) => act.SayHello(name, title), activityOptions) + | ||
await Workflow.ExecuteActivityAsync((MyActivities act) => act.SayGoodBye(name, title), activityOptions); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
namespace TemporalioSamples.CounterInterceptor; | ||
|
||
using Temporalio.Workflows; | ||
|
||
[Workflow] | ||
public class MyWorkflow | ||
{ | ||
private bool exit; // Automatically defaults to false | ||
|
||
[WorkflowRun] | ||
public async Task<string> RunAsync() | ||
{ | ||
// Wait for greeting info | ||
await Workflow.WaitConditionAsync(() => Name != null && Title != null); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These properties can never be null, so this statement has no value, it will always pass immediately There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump, this was never addressed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump again, this is still not addressed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
|
||
// Execute Child Workflow | ||
var result = await Workflow.ExecuteChildWorkflowAsync( | ||
(MyChildWorkflow wf) => wf.RunAsync(Name, Title), | ||
new() { Id = "counter-interceptor-child" }); | ||
|
||
// Wait for exit signal | ||
await Workflow.WaitConditionAsync(() => exit); | ||
|
||
return result; | ||
} | ||
|
||
[WorkflowSignal] | ||
public async Task SignalNameAndTitleAsync(string name, string title) | ||
{ | ||
Name = name; | ||
Title = title; | ||
} | ||
|
||
[WorkflowQuery] | ||
public string Name { get; private set; } = string.Empty; | ||
|
||
[WorkflowQuery] | ||
public string Title { get; private set; } = string.Empty; | ||
|
||
[WorkflowSignal] | ||
public async Task ExitAsync() => exit = true; | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,84 @@ | ||||||||||
namespace TemporalioSamples.CounterInterceptor; | ||||||||||
|
||||||||||
using Temporalio.Client; | ||||||||||
using Temporalio.Worker; | ||||||||||
|
||||||||||
internal class Program | ||||||||||
{ | ||||||||||
private static async Task Main(string[] args) | ||||||||||
{ | ||||||||||
var client = await TemporalClient.ConnectAsync( | ||||||||||
options: new("localhost:7233") | ||||||||||
{ | ||||||||||
Interceptors = new[] | ||||||||||
{ | ||||||||||
new SimpleClientCallsInterceptor(), | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should store this as a field and access the counters off of it instead of making counters static |
||||||||||
}, | ||||||||||
}); | ||||||||||
|
||||||||||
using var tokenSource = new CancellationTokenSource(); | ||||||||||
Console.CancelKeyPress += (_, eventArgs) => | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This program is a run then complete, you don't need ctrl+c support as if it was a long running worker. I would recommend just removing cancel token and shutting down the worker manually if you must have the entire program self-contained (i.e. combining client and worker). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump, this was never addressed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump again, this is still not addressed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||||||||||
{ | ||||||||||
tokenSource.Cancel(); | ||||||||||
eventArgs.Cancel = true; | ||||||||||
}; | ||||||||||
|
||||||||||
var activities = new MyActivities(); | ||||||||||
|
||||||||||
var taskQueue = "CounterInterceptorTaskQueue"; | ||||||||||
|
||||||||||
var workerOptions = new TemporalWorkerOptions(taskQueue). | ||||||||||
AddAllActivities(activities). | ||||||||||
AddWorkflow<MyWorkflow>(). | ||||||||||
AddWorkflow<MyChildWorkflow>(); | ||||||||||
|
||||||||||
workerOptions.Interceptors = new[] { new SimpleCounterWorkerInterceptor() }; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should consider combining these interceptors. Make the one interceptor implement both |
||||||||||
|
||||||||||
using var worker = new TemporalWorker( | ||||||||||
client, | ||||||||||
workerOptions); | ||||||||||
|
||||||||||
// Run worker until cancelled | ||||||||||
Console.WriteLine("Running worker..."); | ||||||||||
try | ||||||||||
{ | ||||||||||
// Start the workers | ||||||||||
var workerResult = worker.ExecuteAsync(tokenSource.Token); | ||||||||||
|
||||||||||
// Start the workflow | ||||||||||
var handle = await client.StartWorkflowAsync( | ||||||||||
(MyWorkflow wf) => wf.RunAsync(), | ||||||||||
new(id: Guid.NewGuid().ToString(), taskQueue: taskQueue)); | ||||||||||
|
||||||||||
Console.WriteLine("Sending name and title to workflow"); | ||||||||||
await handle.SignalAsync(wf => wf.SignalNameAndTitleAsync("John", "Customer")); | ||||||||||
|
||||||||||
var name = await handle.QueryAsync(wf => wf.Name); | ||||||||||
var title = await handle.QueryAsync(wf => wf.Title); | ||||||||||
|
||||||||||
// Send exit signal to workflow | ||||||||||
await handle.SignalAsync(wf => wf.ExitAsync()); | ||||||||||
|
||||||||||
var result = await handle.GetResultAsync(); | ||||||||||
|
||||||||||
Console.WriteLine($"Workflow result is {result}", result); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent with whether you use string concatenation or string interpolation There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
or
Suggested change
But not both. This mistake is made in other lines below. Probably the former approach is best. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump this was never addressed. If you are using interpolation, you don't also pass as a param. |
||||||||||
|
||||||||||
Console.WriteLine("Query results: "); | ||||||||||
Console.WriteLine($"\tName: {name}", name); | ||||||||||
Console.WriteLine($"\tTitle: {title}", title); | ||||||||||
|
||||||||||
// Print worker counter info | ||||||||||
Console.WriteLine("Collected Worker Counter Info: "); | ||||||||||
Console.WriteLine(WorkerCounter.Info()); | ||||||||||
|
||||||||||
// Print client counter info | ||||||||||
Console.WriteLine(); | ||||||||||
Console.WriteLine("Collected Client Counter Info:"); | ||||||||||
Console.WriteLine(ClientCounter.Info()); | ||||||||||
} | ||||||||||
catch (OperationCanceledException) | ||||||||||
{ | ||||||||||
Console.WriteLine("Worker cancelled"); | ||||||||||
} | ||||||||||
} | ||||||||||
} |
rross marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,28 @@ | ||||||||||||||
# dotnet-counter-interceptor | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really the best title, but that's ok |
||||||||||||||
The sample demonstrates: | ||||||||||||||
- the use of a Worker Workflow Interceptor that counts the number of Workflow Executions, Child Workflow Executions, and Activity Executions and the number of Signals and Queries. It is based | ||||||||||||||
off of the [Java sample](https://github.com/temporalio/samples-java/tree/main) located [here](https://github.com/temporalio/samples-java/tree/main/core/src/main/java/io/temporal/samples/countinterceptor) | ||||||||||||||
- the use of a Client Workflow Interceptor that counts the number of Workflow Executions and the number of Signals and Queries. | ||||||||||||||
|
||||||||||||||
## Start local Temporal Server | ||||||||||||||
```bash | ||||||||||||||
# run only once | ||||||||||||||
temporal server start-dev | ||||||||||||||
``` | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
We like to keep the starting of the local server in one place since it can change and there are multiple options. This can be seen in other samples. (in fact, we will make these run easy on cloud too soon, so we don't want to have to come back and update these) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump, this was never addressed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump again, this is still not addressed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Finally addressed :) |
||||||||||||||
|
||||||||||||||
## Run Worker Locally | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is more than the worker, it's the worker and the client calls mixed into one. Many samples split these, but don't have to here, but it's not just "worker" as the title suggests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump, this was never addressed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump again, this is still not addressed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||||||||||||||
```bash | ||||||||||||||
# make sure you have temporal server running (see section above) | ||||||||||||||
dotnet run | ||||||||||||||
``` | ||||||||||||||
|
||||||||||||||
## Run Worker using Temporal Cloud | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this section applies anymore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump, this was never addressed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deleted. |
||||||||||||||
```bash | ||||||||||||||
# set up environment variables | ||||||||||||||
export TEMPORAL_NAMESPACE=<namespace>.<accountId> | ||||||||||||||
export TEMPORAL_ADDRESS=<namespace>.<accountId>.tmprl.cloud:7233 | ||||||||||||||
export TEMPORAL_TLS_CERT=/path/to/cert | ||||||||||||||
export TEMPORAL_TLS_KEY=/path/to/key | ||||||||||||||
# run the worker | ||||||||||||||
dotnet run | ||||||||||||||
``` |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,59 @@ | ||||||
namespace TemporalioSamples.CounterInterceptor; | ||||||
|
||||||
using Temporalio.Client; | ||||||
using Temporalio.Client.Interceptors; | ||||||
|
||||||
public class SimpleClientCallsInterceptor : IClientInterceptor | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO for simplicity you should make one interceptor that implements both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
{ | ||||||
private ClientCounter clientCounter; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
And get rid of the constructor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump |
||||||
|
||||||
public SimpleClientCallsInterceptor() | ||||||
{ | ||||||
this.clientCounter = new ClientCounter(); | ||||||
} | ||||||
|
||||||
public ClientOutboundInterceptor InterceptClient(ClientOutboundInterceptor nextInterceptor) => | ||||||
new ClientOutbound(this, nextInterceptor); | ||||||
|
||||||
private class ClientOutbound : ClientOutboundInterceptor | ||||||
{ | ||||||
private SimpleClientCallsInterceptor root; | ||||||
|
||||||
public ClientOutbound(SimpleClientCallsInterceptor root, ClientOutboundInterceptor next) | ||||||
: base(next) => this.root = root; | ||||||
|
||||||
public override Task<WorkflowHandle<TWorkflow, TResult>> StartWorkflowAsync<TWorkflow, TResult>( | ||||||
StartWorkflowInput input) | ||||||
{ | ||||||
var id = CheckId(input.Options.Id); | ||||||
root.clientCounter.AddStartInvocation(id); | ||||||
return base.StartWorkflowAsync<TWorkflow, TResult>(input); | ||||||
} | ||||||
|
||||||
public override Task SignalWorkflowAsync(SignalWorkflowInput input) | ||||||
{ | ||||||
var id = CheckId(input.Id); | ||||||
root.clientCounter.AddSignalInvocation(id); | ||||||
return base.SignalWorkflowAsync(input); | ||||||
} | ||||||
|
||||||
public override Task<TResult> QueryWorkflowAsync<TResult>(QueryWorkflowInput input) | ||||||
{ | ||||||
var id = CheckId(input.Id); | ||||||
root.clientCounter.AddQueryInvocation(id); | ||||||
return base.QueryWorkflowAsync<TResult>(input); | ||||||
} | ||||||
|
||||||
public override Task<WorkflowUpdateHandle<TResult>> StartWorkflowUpdateAsync<TResult>( | ||||||
StartWorkflowUpdateInput input) | ||||||
{ | ||||||
// Not tracking this | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then can remove this method entirely There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed |
||||||
return base.StartWorkflowUpdateAsync<TResult>(input); | ||||||
} | ||||||
|
||||||
private static string CheckId(string? id) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider just using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bump, only three places have this, no need to make a whole new method for a simple |
||||||
{ | ||||||
return id ??= "None"; | ||||||
} | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit surprised this passes CI, maybe we don't have a
dotnet format
check. Usually we want there to be a blank line between namespace declaration and type declarataionThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.