After living under a rock for 2 years employment wise, I am now confronted with Blazor at my new Workplace and have a lot of catch up to do after doing mostly ASP.NET Framework MVC prior to the 2 Years.
Trying myself on Blazor server side, I tried to apply my past knowledge which included cancellationtokens for async operations and i couldn't find much information about them in combination with Blazor.
Are they still a Best Practice or did they became Obsolete at some point?
I did found this previously asked question which recommends creating a tokensource on the OnInitializedAsync() method and cancelling it on Dispose() which i honestly find a bit crude.
(I would need to implement this for each page and you know... DRY)
I also found this Article about advanced Scenarios on Microsoft Docs that explains how to implement a Circuit Handler, which honestly is a bit beyond me right now and most likely way out of scope for my little home-project.
In comparison, in asp.net Framework MVC i would build a Controller like this:
namespace SampleWebsite.Controllers
{
public class SampleController : ApiController
{
private readonly MyEntities _entities = new MyEntities();
public async Task<IHttpActionResult> MyAsyncApi(CancellationToken cancellationToken)
{
var result = _entities.MyModel.FirstOrDefault(e => e.Id == 1, cancellationToken: cancellationToken);
return OK(result);
}
}
}
The CancellationToken will be injected by asp.net Framework / Core and is directly linked to the current context connection-pipe.
Hence, if the user closes the connection, the token becomes invalid.
I would have assumed that for asp.net core and blazor where dependency-injections is a big part of it, this would be the case here too, but i could not find any documentation about this here.
So, should cancellationtokens still be used at this point or does Microsoft do some magic in the background for asynchronous tasks? And if yes, what would be the best implementation?
EDIT:
Here would be my Setup to clarify:
The Blazor-Component:
#page "/Index"
#inject IIndexService Service
#* Some fancy UI stuff *#
#code {
private IEnumerable<FancyUiValue> _uiValues;
protected override async Task OnInitializedAsync()
{
_uiValues = await Service.FetchCostlyValues();
}
}
And the Injected Service-Class that does the heavy lifting:
public interface IIndexService
{
Task<IEnumerable<FancyUiValue>> FetchCostlyValues();
}
public class IndexService : IIndexService
{
public async Task<IEnumerable<FancyUiValue>> FetchCostlyValues()
{
var uiValues = await heavyTask.ToListAsync(); // <-- Best way to get a cancellationtoken here?
return uiValues;
}
}
My question is, what would be the best way to get a token in the specificed part of the code or would it be irrelevant because the Server would kill all running tasks when the connection (as example) ends?
After 2 years of experience with Blazor, i figured that the only reliable way to pass an CancellationToken to a Task within an Object of a longer Lifetime (e.g. Singleton or Scoped Service) is the combination of IDisposeable and CancellationTokenSource
#page "/"
#implements IDisposable
*# Razor Stuff *#
#code
{
private CancellationTokenSource _cts = new();
protected override async Task OnInitializedAsync()
{
await BusinessLogicSingleton.DoExpensiveTask(_cts.Token);
}
#region IDisposable
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
#endregion
}
On repeated use or just to comply to the DRY-Rule, you can also inherit from the ComponentBase Class and then use that Class for your Components that require to pass a CancellationToken:
public class CancellableComponent : ComponentBase, IDisposable
{
internal CancellationTokenSource _cts = new();
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
}
#page "/"
#inherits CancellableComponent
#* Rest of the Component *#
I also found that while you could Inject the IHttpContextAccessor and use the HttpContext.RequestAborted token which is the same that will be generated and injected in your ASP.Net MVC Method Calls, as of the current .Net6 Version it will never fire even after the Connection to the Client is severed and the providing HttpContext is disposed.
This may be a case for the Developer-Team on Github as i do see UseCases for it where the User is allowed to exit the Component while the Task keeps on going until the User leaves the Website completely.
(For such cases, my recommended Workaround would be to write your own CircuitHandler that will give you Events for when a Circuit is removed.)
Instead of adding a CancellationTokenSource to all components manually, you can create a base component that expose a CancellationToken and use this base component automatically in all components of the project
Implement your ApplicationComponentBase
public abstract class ApplicationComponentBase: ComponentBase, IDisposable
{
private CancellationTokenSource? cancellationTokenSource;
protected CancellationToken CancellationToken => (cancellationTokenSource ??= new()).Token;
public virtual void Dispose()
{
if (cancellationTokenSource != null)
{
cancellationTokenSource.Cancel();
cancellationTokenSource.Dispose();
cancellationTokenSource = null;
}
}
}
Then add #inherits ApplicationComponentBase to _Imports.razor file
In page a call:
await Task.Delay(50000, CancellationToken);
Then try to navigate to another page, the task you called will be cancelled
Related
I'd like to be able to pass cancellation tokens via dependency injection instead of as parameters every time. Is this a thing?
We have an asp.net-core 2.1 app, where we pass calls from controllers into a maze of async libraries, handlers and other services to fulfil the byzantine needs of the fintech regulatory domain we service.
At the top of the request, I can declare that I want a cancellation token, and I'll get one:
[HttpPost]
public async Task<IActionResult> DoSomeComplexThingAsync(object thing, CancellationToken cancellationToken) {
await _someComplexLibrary.DoThisComplexThingAsync(thing, cancellationToken);
return Ok();
}
Now, I want to be a good async programmer and make sure my cancellationToken gets passed to every async method down through the call chain. I want to make sure it gets passed to EF, System.IO streams, etc. We have all the usual repository patterns and message passing practices you'd expect. We try to keep our methods concise and have a single responsibility. My tech lead gets visibly aroused by the word 'Fowler'. So our class sizes and function bodies are small, but our call chains are very, very deep.
What this comes to mean is that every layer, every function, has to hand off the damn token:
private readonly ISomething _something;
private readonly IRepository<WeirdType> _repository;
public SomeMessageHandler(ISomething<SomethingElse> something, IRepository<WeirdType> repository) {
_something = something;
_repository = repository;
}
public async Task<SomethingResult> Handle(ComplexThing request, CancellationToken cancellationToken) {
var result = await DoMyPart(cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
result.SomethingResult = await _something.DoSomethingElse(result, cancellationToken);
return result;
}
public async Task<SomethingResult> DoMyPart(ComplexSubThing request, CancellationToken cancellationToken) {
return await _repository.SomeEntityFrameworkThingEventually(request, cancellationToken);
}
This goes on ad infinitum, as per the needs of our domain complexity. It seems like CancellationToken appears more times in our codebase than any other term. Our arg lists are often already too long (i.e. more than one) as it is, even though we declare a million object types. And now we have this extra little cancellation token buddy hanging around in every arg list, every method decl.
My question is, since Kestrel and/or the pipeline gave me the token in the first place, it'd be great if I could just have something like this:
private readonly ISomething _something;
private readonly IRepository<WeirdType> _repository;
private readonly ICancellationToken _cancellationToken;
public SomeMessageHandler(ISomething<SomethingElse> something, ICancellationToken cancellationToken) {
_something = something;
_repository = repository;
_cancellationToken = cancellationToken;
}
public async Task<SomethingResult> Handle(ComplexThing request) {
var result = await DoMyPart(request);
_cancellationToken.ThrowIfCancellationRequested();
result.SomethingResult = await _something.DoSomethingElse(result);
return result;
}
public async Task<SomethingResult> DoMyPart(ComplexSubThing request) {
return await _repository.SomeEntityFrameworkThingEventually(request);
}
This would then get passed around via DI composition, and when I had something that needs the token explicitly I could do this:
private readonly IDatabaseContext _context;
private readonly ICancellationToken _cancellationToken;
public IDatabaseRepository(IDatabaseContext context, ICancellationToken cancellationToken) {
_context = context;
_cancellationToken = cancellationToken;
}
public async Task<SomethingResult> DoDatabaseThing() {
return await _context.EntityFrameworkThing(_cancellationToken);
}
Am I nuts? Do I just pass the damn token, every damn time, and praise the async gods for the bounty that has been given? Should I just retrain as a llama farmer? They seem nice. Is even asking this some kind of heresy? Should I be repenting now? I think for async/await to work properly, the token has to be in the func decl. So, maybe llamas it is
First of all, there are 3 injection scopes: Singleton, Scoped and Transient. Two of those rule out using a shared token.
DI services added with AddSingleton exist across all requests, so any cancellation token must be passed to the specific method (or across your entire application).
DI services added with AddTransient may be instantiated on demand and you may get issues where a new instance is created for a token that is already cancelled. They'd probably need some way for the current token to be passed to [FromServices] or some other library change.
However, for AddScoped I think there is a way, and I was helped by this answer to my similar question - you can't pass the token itself to DI, but you can pass IHttpContextAccessor.
So, in Startup.ConfigureServices or the extension method you use to register whatever IRepository use:
// For imaginary repository that looks something like
class RepositoryImplementation : IRepository {
public RepositoryImplementation(string connection, CancellationToken cancellationToken) { }
}
// Add a scoped service that references IHttpContextAccessor on create
services.AddScoped<IRepository>(provider =>
new RepositoryImplementation(
"Repository connection string/options",
provider.GetService<IHttpContextAccessor>()?.HttpContext?.RequestAborted ?? default))
That IHttpContextAccessor service will be retrieved once per HTTP request, and that ?.HttpContext?.RequestAborted will return the same CancellationToken as if you had called this.HttpContext.RequestAborted from inside a controller action or added it to the parameters on the action.
I think you are thinking in a great way, I do not think you need to regret or repent.
This is a great idea, I also thought about it, and I implement my own solution
public abstract class RequestCancellationBase
{
public abstract CancellationToken Token { get; }
public static implicit operator CancellationToken(RequestCancellationBase requestCancellation) =>
requestCancellation.Token;
}
public class RequestCancellation : RequestCancellationBase
{
private readonly IHttpContextAccessor _context;
public RequestCancellation(IHttpContextAccessor context)
{
_context = context;
}
public override CancellationToken Token => _context.HttpContext.RequestAborted;
}
and the registration should be like this
services.AddHttpContextAccessor();
services.AddScoped<RequestCancellationBase, RequestCancellation>();
now you can inject RequestCancellationBase wherever you want, and the better thing is that you can directly pass it to every method that expects CancellationToken this is because of public static implicit operator CancellationToken(RequestCancellationBase requestCancellation)
this solution helped me, hope it is helpful for you also
I just discovered IHostedService and .NET Core 2.1 BackgroundService class. I think idea is awesome. Documentation.
All examples I found are used for long running tasks (until application die).
But I need it for short time. Which is the correct way of doing it?
For example:
I want to execute a few queries (they will take approx. 10 seconds) after application starts. And only if in development mode. I do not want to delay application startup so IHostedService seems good approach. I can not use Task.Factory.StartNew, because I need dependency injection.
Currently I am doing like this:
public class UpdateTranslatesBackgroundService: BackgroundService
{
private readonly MyService _service;
public UpdateTranslatesBackgroundService(MyService service)
{
//MService injects DbContext, IConfiguration, IMemoryCache, ...
this._service = service;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await ...
}
}
startup:
public static IServiceProvider Build(IServiceCollection services, ...)
{
//.....
if (hostingEnvironment.IsDevelopment())
services.AddSingleton<IHostedService, UpdateTranslatesBackgroundService>();
//.....
}
But this seems overkill. Is it? Register singleton (that means class exists while application lives). I don't need this. Just create class, run method, dispose class. All in background task.
There's no need to do any magic for this to work.
Simply:
Register the service you need to run in ConfigureServices
Resolve the instance you need in Configure and run it.
To avoid blocking, use Task.Run.
You must register the instance, or dependency injection won't work. That's unavoidable; if you need DI, then you have to do it.
Beyond that, it's trivial to do what you ask, like this:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddTransient<MyTasks>(); // <--- This
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// Blocking
app.ApplicationServices.GetRequiredService<MyTasks>().Execute();
// Non-blocking
Task.Run(() => { app.ApplicationServices.GetRequiredService<MyTasks>().Execute(); });
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
public class MyTasks
{
private readonly ILogger _logger;
public MyTasks(ILogger<MyTasks> logger)
{
_logger = logger;
}
public void Execute()
{
_logger.LogInformation("Hello World");
}
}
BackgroundService exists specifically for long running processes; if it's a once of, don't use it.
Well I think there is more then one question here.
First let me point out something you are probably aware of async != multithreaded.
So BackgroundService will not make you app "multithreaded" it can run inside a single thread without no problem. And if you are doing blocking operations on that thread it will still block startup. Lets say in the class you implement all the sql queries in a not real async way something similar to
public class StopStartupService : BackgroundService
{
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
System.Threading.Thread.Sleep(1000);
return Task.CompletedTask;
}
}
This will still block startup.
So there is another question.
How should you run background jobs?
For this in simple cases Task.Run(Try to avoid Task.Factory.StartNew if you are not sure how to configure it) should do the job, but that is not to say this is the best or a good way to do it. There are a bunch of open source libraries that will do this for you and it might be good to have a look at what they provide. There are a lot of problems you might not be aware of , that can create frustrating bugs if you just use Task.Run
The second question I can see is.
Should I do fire and forget in c#?
For me this is a definite NO(but XAML people might not agree). No matter what you do, you need to keep track of when the thing you are doing is done. In your case you might want to do a rollback in the database if someone stops the app before the queries are done. But more than that you would want to know when you can start using the data that the queries provided. So BackgroundService helps you to simplify the execution but is difficult to keep track of completion.
Should you use a singleton?
As you already mentioned using singletons can be a dangerous thing especially if you don't clean things properly, but more than that the context of the service you are using will be the same for the life time of the object. So with this all depends on your implementation of the service if there will be problems.
I do something like this to do what you want.
public interface IStartupJob
{
Task ExecuteAsync(CancellationToken stoppingToken);
}
public class DBJob : IStartupJob
{
public Task ExecuteAsync(CancellationToken stoppingToken)
{
return Task.Run(() => System.Threading.Thread.Sleep(10000));
}
}
public class StartupJobService<TJob> : IHostedService, IDisposable where TJob: class,IStartupJob
{
//This ensures a single start of the task this is important on a singletone
private readonly Lazy<Task> _executingTask;
private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
public StartupJobService(Func<TJob> factory)
{
//In order for the transient item to be in memory as long as it is needed not to be in memory for the lifetime of the singleton I use a simple factory
_executingTask = new Lazy<Task>(() => factory().ExecuteAsync(_stoppingCts.Token));
}
//You can use this to tell if the job is done
public virtual Task Done => _executingTask.IsValueCreated ? _executingTask.Value : throw new Exception("BackgroundService not started");
public virtual Task StartAsync(CancellationToken cancellationToken)
{
if (_executingTask.Value.IsCompleted)
{
return _executingTask.Value;
}
return Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
if (_executingTask == null)
{
return;
}
try
{
_stoppingCts.Cancel();
}
finally
{
await Task.WhenAny(_executingTask.Value, Task.Delay(Timeout.Infinite,
cancellationToken));
}
}
public virtual void Dispose()
{
_stoppingCts.Cancel();
}
public static void AddService(IServiceCollection services)
{
//Helper to register the job
services.AddTransient<TJob, TJob>();
services.AddSingleton<Func<TJob>>(cont =>
{
return () => cont.GetService<TJob>();
});
services.AddSingleton<IHostedService, StartupJobService<TJob>>();
}
}
There is a library called Communist.StartupTasks that handles this exact scenario. It's available on Nuget.
It's designed specifically to run tasks during application launch in a .NET Core App. It fully supports dependency injection.
Please note that it executes tasks sequentially and it blocks until all tasks are complete (i.e. your app won't accept requests until startup tasks complete).
In my web application (ASP.NET Core), I want to run a job in the background that is listening to a remote server, calculating some results and pushing it to the client on Pusher (a websocket).
I'm not sure where I'm supposed to start this task. Currently I start it at the end of
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
in Startup.cs
but I think there is something wrong about that, it doesn't make sense to start background jobs in a method called "Configure". I was expecting to find a Start method somewhere
Also, when I try to use EF Core to generate initial database migration file, it actually executes that method and starts my tasks.. which clearly doesn't make any sense:
dotnet ef migrations add InitialCreate
running that from console creates migration code which will be used to create the database on SQL Server based on my data models.
Why isn't there a method where I can start some a Task? I don't want this to be on a separate process, it really doesn't need its own process and it is essentially a part of the web server because it does communicate with the client (browser) via a websocket, so it makes sense to run it as part of the web server.
I believe you're looking for this
https://blogs.msdn.microsoft.com/cesardelatorre/2017/11/18/implementing-background-tasks-in-microservices-with-ihostedservice-and-the-backgroundservice-class-net-core-2-x/
And i did a 2 hour self-proclaimed-award-winning hackathon against myself to learn abit of that.
https://github.com/nixxholas/nautilus
You can refer the injections here and implement the abstracts from there too.
Many MVC projects are not really required to operate persistent background tasks. This is why you don't see them baked into a fresh new project via the template. It's better to provide developers an interface to tap on and go ahead with it.
Also, with regards to opening that socket connection for such background tasks, I have yet to establish a solution for that. As far as I know/did, I was only able to broadcast payload to clients that are connected to my own socketmanager so you'll have to look elsewhere for that. I'll definitely beep if there is anything regarding websockets in an IHostedService.
Ok anyway here's what happens.
Put this somewhere in your project, its more of an interface for you to overload with to create your own task
/// Copyright(c) .NET Foundation.Licensed under the Apache License, Version 2.0.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
protected readonly IServiceScopeFactory _scopeFactory;
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts =
new CancellationTokenSource();
public BackgroundService(IServiceScopeFactory scopeFactory) {
_scopeFactory = scopeFactory;
}
protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
public virtual Task StartAsync(CancellationToken cancellationToken)
{
// Store the task we're executing
_executingTask = ExecuteAsync(_stoppingCts.Token);
// If the task is completed then return it,
// this will bubble cancellation and failure to the caller
if (_executingTask.IsCompleted)
{
return _executingTask;
}
// Otherwise it's running
return Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// Stop called without start
if (_executingTask == null)
{
return;
}
try
{
// Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
cancellationToken));
}
}
public virtual void Dispose()
{
_stoppingCts.Cancel();
}
}
Here's how you can actually use it
public class IncomingEthTxService : BackgroundService
{
public IncomingEthTxService(IServiceScopeFactory scopeFactory) : base(scopeFactory)
{
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using (var scope = _scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<NautilusDbContext>();
Console.WriteLine("[IncomingEthTxService] Service is Running");
// Run something
await Task.Delay(5, stoppingToken);
}
}
}
}
If you noticed, there's a bonus there. You'll have to use a servicescope in order to access db operations because its a singleton.
Inject your service in
// Background Service Dependencies
services.AddSingleton<IHostedService, IncomingEthTxService>();
I have created an Interface
public interface ICurrentUser
{
Task<bool> Set(UserAuth user);
User Get();
}
and a class
public class CurrentUserSvc : Interface.ICurrentUser
{
private User _u;
private UserAuth _ua;
private AppDbContext db;
public CurrentUserSvc(AppDbContext db) {
this.db = db;
}
public User Get()
{
return _u;
}
public async Task<bool> Set(UserAuth ua)
{
_ua = ua; // this is the default EntityFramework IdentityUser
_u = await db.AppUsers // this is my applicaiton's 'extra settings'
// user used to ensure passowrd fields are
// not passed about everywhere
.Where(u => u.UserID == _ua.UserID)
.SingleAsync();
return true;
}
}
In Startup.cs I set
services.AddScoped<ICurrentUser, CurrentUserSvc>();
// I also add a service which will be used later in a scoped
// lifecycle (though I've also tried transient on that one)
services.AddScoped<IProductDbSvc, ProductDbSvc>();
Later I call to a piece of middleware:
public async Task<Task> Invoke(HttpContext hc)
{
if (hc.User.Identity.IsAuthenticated) {
UserAuth iu = await _um.FindByIdAsync(hc.User.GetUserId());
await _cus.Set(iu);
}
// the values are definitely set correctly here.
// I have inspected them during debug
return _next(hc);
}
Later still I try to access the content of the CurrentUserSvc I try to access the current user via the GET
public ProductDbSvc(AppDbContext db, ICurrentUser cu){
this.db = db;
this.cu = cu;
// the values in cu are NULL here. Get() returns null
this.CurrentUser = cu.Get();
}
but the result of Get() is null I was expecting that a Scoped param would retain the values set earlier in the request lifecycle.
What am I missing? Is there some other way to ensure the scoped-singleton retains the user data throughout the application's lifecycle.
UPDATE: I've created a generic project that illustrates this problem generically. https://github.com/AlexChesser/AspnetIdentitySample
check out the repo
build and run in visualstudio or DNX
register a local user
try to view the service on http://localhost:5000/api/currentuser
You'll notice that within the DEBUG output you can see that the correct user details are set, but within the actual controller itself the values returned are null.
UPDATE 2 the working sample is on this branch in github https://github.com/AlexChesser/AspnetIdentitySample/tree/dependencyinjectionscoped
UPDATE 3 turns out scoped parameters can be injected into the INVOKE method of custom middleware as well. https://github.com/AlexChesser/AspnetIdentitySample/commit/25b010a5ae45678c137b2ad05c53ccd659a29101 altering the invoke method will allow for scoped parameters to be injected correctly.
public async Task Invoke(HttpContext httpContext,
ICurrentUserService cus,
UserManager<ApplicationUser> um)
{
if (httpContext.User.Identity.IsAuthenticated)
{
ApplicationUser au = await um.FindByIdAsync(httpContext.User.GetUserId());
await cus.Set(au);
}
await _next(httpContext);
}
UPDATE 4 - I discovered an issue with my middleware signature last night which is pretty important. Code above has been edited to the correct form. Specifically the method was Task<Task> and return _next(...)
This was resulting in a "whitescreen" death on certain page loads (async called badly will not throw a stack trace)
By altering to a Task and using await next(...) the code functions properly and eliminates the intermittent whitescreen death caused by badly implemented async in dotnet5.
DbContext is a scoped service and as well as your CurrentUserSvc is a scoped service. Middlewares are instantiated only once for the whole running time of the app, so they are singleton essentially. So you need to remove both DbContext and CurrentUserSvc from being constructor injected here.
Instead you can use HttpContext's RequestServices property (which returns a IServiceProvider) to resolve both the DbContext and CurrentUserSvc services.
In the middleware, inject a dependency to IServiceProvider, rather than ICurrentUser. Then in the Invoke get the current user via serviceProvider.GetRequiredService<ICurrentUser>();
I'm attempting to create an initial 'Super User' in an ASP.NET 5 application. Using the latest template files with MVC 6 / EF7.
I can follow the examples set out here:
http://wildermuth.com/2015/3/17/A_Look_at_ASP_NET_5_Part_3_-_EF7
This works fine - until I try to execute an async method. For example:
await _userManager.CreateAsync(user, "P#55w0rd!");
or even:
await _context.SaveChangesAsync();
Synchronous methods work without a problem and this code executes outside of the Startup.cs Configure{...} as well.
I get the 'White Screen of Death' on application start. I would do it all without async but I don't think the UserManager has a Create()in Identity 3.0.
Is this me not understanding asynchronous programming or should it be possible?
EDIT: Added the entire call:
Define a Seeder class and a method to create the user:
public class Seeder
{
private ApplicationDbContext _context;
private UserManager<ApplicationUser> _userManager;
public Seeder(
ApplicationDbContext context,
UserManager<ApplicationUser> userManager)
{
_context = context;
_userManager = userManager;
}
public async Task Seed()
{
await CreateUsersAsync();
}
public async Task CreateUsersAsync()
{
var user = await _userManager.FindByEmailAsync("superuser#superuser.com");
if (user == null)
{
var company = _context.Company.First(x => x.Name == "Acme Ltd");
user = new ApplicationUser
{
UserName = "superuser#superuser.com",
Email = "superuser#superuser.com",
CreatedDate = DateTime.Now,
IsActive = true,
CompanyID = company.CompanyId
};
await _userManager.CreateAsync(user, "P#55w0rd!!");
}
}
}
Configure the service:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<Seeder>();
...
}
Call it:
public async void Configure(Seeder seeder)
{
...
await seeder.Seed();
...
}
Funny thing is; it does actually create the User - it just doesn't continue. So the second time it executes user != null and it executes fine.
I think the problem is because a deadlock is formed by the signature
public async void Configure(Seeder seeder).
Since it returns void, the awaited Task is never returned to the caller which creates a dead lock. Can you make it:
public async Task Configure(Seeder seeder).
I haven't gotten to work with MVC 6 yet, :( so I may be missing something. But that's why there is a deadlock.
Edit:
Since you can't change the signature of Configure, create a method called ConfigureAsync that returns type Task. Now await as per usual inside of it with your user manager code and call ConfigureAsync from Configure but wait on it.
ConfigureAsync.ConfigureAwait(false).Wait()
ConfigureAwait (false) is used to prevent potential dead locks of waiting on the async method to complete.
Full example:
public void Configure(Seeder seeder)
{
//Edited due to typo/bad syntax.
ConfigureAsync(seeder).Wait();
}
public async Task ConfigureAsync(Seeder seeder)
{
//Now treat this like true async/await.
await seeder.Seed().ConfigureAwait(false);
}
If you run into deadlocks here, perhaps your seeder should just be synchronous? I would avoid using Task.Run() in an ASP.NET context because that will totally defeat the purpose of async/await in the first place by taking up two request threads that would have just been done on one if done synchronously.
Sometimes, you need to implement a sync interface but you only have async APIs available. There is no perfect solution.
Fortunately, this code is only called once so performance concerns don't matter. You can just do the dirty sync over async bridge:
public void ConfigureServices(IServiceCollection services)
{
ConfigureServicesImpl(services).Wait(); //bridge
}
public async Task ConfigureServicesImpl(IServiceCollection services)
{
await ...;
}
You might need to insert a deadlock protection such as ConfigureAwait(false) or Task.Run(() => ...).Wait().