If I have an application that uses a pipeline with many stages to be executed using foreach
on all the stages and call:
CanExecute
Execute
Interface is this:
public interface IService
{
bool CanExecute(IContext subject);
IContext Execute(IContext subject);
}
It basically takes in a context and returns a context where it has become richer.
Within one of the stages Execute
method I need to call a service, and want to do async.
So now the Execute
method needs to change to e.g.
Task<IContext> ExecuteAsync(IContext subject);
with await
for the call to the service.
All the other stages have no async code but need to change now as best practice is "async all the way".
Is this normal to have to make these changes when you bring in async code?
C# 8 offers multiple ways to avoid modifying the synchronous services. C# 7 can also handle this with pattern matching statements.
Default Implementation Members
Interface versioning is one of the main use cases for default interface members. They can be used to avoid changing existing classes when an interface changes. You can add a default implementation for
ExecuteAsync
that returns the result ofExecute
as a ValueTask.Let's say you have these interfaces :
To create an asynchronous service without modifying the synchronous ones, you can add a default implementation to IService and override it in new services :
ServiceB.Execute
still needs a body and one thing that makes sense is to callExecuteAsync()
and block, as ugly as that looks. Another possibility would be to throw ifExecute
is called :Pattern Matching
Another option would be to create a second interface just for the asynchronous services :
Both service implementations would remain the same. The pipeline code would change to make different calls based on the service's type :
The pattern matching expression calls a different branch based on the current service's type. Natching on a type produces a strongly typed instance (a or b) which can be used to call the appropriate method.
Switch expressions are exhaustive - the compiler will generate a warning if it can't verify that all options are matched by the patterns.
C# 7
C# 7 doesn't have switch expressions, so the more verbose pattern matching switch statement is needed :
Switch statements aren't exhaustive so we need to add the
default
section to catch errors at runtime.