Hi Syed,
if I were to review this in a pull request, I would strongly encourage you to refactor this.
Frankly, to call this code a "best practice" is dangerous advice to anyone seeking out knowledge in the realm of async programming in C#. Let me explain to you why I think that way:
Sending off the async method `SendUserCreationMail` in a Task.Run and then not awaiting the corresponding task is as bad as having an `async void` signature in your code base and pretending your code would be async.
The reason for this is that any exception happening inside of that method is lost in the CLR's nirvana and will never be caught by the outside try-catch because the task is not awaited. This is dangerous and can be the root cause for numerous bugs:
- The consuming code will not know that there ever was an error. This code might work 99% of the time. The remaining 1% will be users complaining about emails that were not received and you were not even aware of this.
- There is no chance of knowing that something was off from outside of SendUserCreationEmail, because nothing is logged. If you are lucky, it will pop up as an UnhandledTaskException, but the stacktrace information is still lost.
- There is no chance of retrying to send the email, because of the above reasons.
That being said, your ultimate goal is to do something (potentially long-running) without affecting the primary business process (add-user). There are many ways to do this properly:
- Using an IHostedService and a simple mechanism to schedule work on a background thread (e.g., channels, queues, etc.)
- Using a proper messaging solution (MassTransit, NServiceBus) on top of a message bus. Even in-memory would be a good fit, giving you the chance to configure retries for the actual sending out-of-the-box.
TLDR, using an observed Task.Run is nothing more than a workaround and I would not encourage anyone to use this in a production environment.
Cheers,
Dennis