To better explain what we did with the CreateUsers() method of the DbSeeder class, we should spend a few words about the concept async tasks.
Whoever knows the basics of thread programming within legacy ASP.NET knows well that you should never even think about blocking an async task. The reason isn't always immediate to explain and understand, yet we'll try to give it a shot. One of the first thing you should learn when working with sync methods invoking async tasks in ASP.NET is that when the top-level method awaits a task, its current execution context gets blocked until the task completes. This won't be a problem, unless that context allows only one thread to run at a time, which is precisely the case of the AspNetSynchronizationContext. If we combine these two things together, we can easily see that blocking an async method returning a task will expose our application to a high risk of deadlock. A deadlock, from a software development perspective, is a dreadful situation that occurs whenever a process or thread enters a waiting state indefinitely, usually because the resource it's waiting for is held by another waiting process. In any legacy ASP.NET web application, we'll face a deadlock every time we're blocking a task, simply because that task, in order to complete, will require the same execution context of the invoking method, which is kept blocked by that method until the task completes!
After hearing this terrifying story, there's definitely something that we should ask ourselves: why in the hell have we chosen to block the CreateUsers() method in the first place! Are we nuts or what?
As a matter of fact, we're not using legacy ASP.NET here; we're using .NET Core. Luckily enough, .NET Core dropped the former pattern based upon the SynchronizationContext for a contextless approach layered upon a versatile, deadlock-resilient thread pool. To keep it simple, we have nothing to worry about when using GetAwaiter().GetResult(), .Wait, .Result, or any other blocker; the only real downside in doing so is that we will lose all the benefits brought by asynchronous programming, such as performances and scalability. However, we can definitely live without them within the single request that will trigger the execution of our DbSeeder class in the whole application's lifetime.
https://blogs.msdn.microsoft.com/pfxteam/2012/04/12/asyncawait-faq/.
http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html.
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx.
https://blog.stephencleary.com/2017/03/aspnetcore-synchronization-context.html.