EF MigrateAsync() makes the controllers producing 404 Not Found - c#

In my Startup.cs I perform migrations like this. There's some weird issue in the private method I can't explain and it seems to be related to the asynchronous version of the invokation. The following works just as expected (although the asynchornicity in pointless.
public async void Configure(
IApplicationBuilder app, IWebHostEnvironment env)
{
...
await MigrateDb(app);
...
}
private static async Task MigrateDb(IApplicationBuilder app)
{
using IServiceScope scope = app.ApplicationServices
.GetService<IServiceScopeFactory>()?.CreateScope();
if (scope == null)
throw new OperationCanceledException();
Context context = scope.ServiceProvider.GetRequiredService<Context>();
context.Database.Migrate();
}
However, when I apply the asynchoronous migration, stuff stop to work. FOr some reason, the controllers are not reachable and I get 404's on every endpoint, including the ping that only returns a fixed string form the service without any contact with the DB.
private static async Task MigrateDb(IApplicationBuilder app)
{
using ...
Context context = ...
await context.Database.MigrateAsync();
}
Googling gave absolutely nothing related. I tried explicitly killing the scope by scope.Dispose();. At the moment I have no idea what to google for more or how to approach the trouble-shooting. It's not a critical production issue - I'm only doing a test (and this part isn't the aim of it). Still, it would be very interesting to understand why this happens.

I am somewhat skeptical about the Configure method. There is difference between async task and async void. There is one answer async/await - when to return a Task vs void? will help to understand that.
Overall async void not handling async of MigrateAsnyc properly and that may leads to problem.
If I have to try another solution then I will program.cs and you can try that. You have to use your own context.
public class Program
{
public static async Task Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
//Migrate database
var scope = host.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<MyContext>();
await context.Database.MigrateAsync();
await host.RunAsync();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}

Related

asp.net core: What is the best practice to execute code in a container application for kubernetes

I created a container application for kubernetes project pattern. I want to run there a service that listens to messages and do work in an endless loop. I execute it from Configure method Startup.cs. From some references this is the place to do so. Before that i register all services to ConfigureServices method like a normal asp.net app.
Also, I want to use IApplicationLifetime.ApplicationStopping to be triggered.
I didn't find a way to achieve both things: execute the code of the task listener and controlling the callback of ApplicationStopping.
this is my Configure method
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
var cancellationToken = new CancellationTokenSource();
var tasksListener = app.ApplicationServices.GetRequiredService<ITaskListener>();
var appLifetime = app.ApplicationServices.GetRequiredService<IApplicationLifetime>();
appLifetime.ApplicationStopping.Register(() =>
{
Console.WriteLine("ApplicationStopping is stopping");
});
tasksListener.Listen(cancellationToken.Token).Wait();
app.Run(async (context) =>
{
await tasksListener.Listen(cancellationToken.Token);
});
}
I'm able to stop the application and see the print, but the code inside app.Run isn't executed.
If I switch this code:
app.Run(async (context) =>
{
await tasksListener.Listen(cancellationToken.Token);
});
with this:
tasksListener.Listen(cancellationToken.Token).Wait();
so the code is executed but then IApplicationLifetime.ApplicationStopping won't be executed.
Any idea?
for those who encounter same problem I'll update that the solution for me was simply delete Startup.cs and do everything from Program.cs, so it will look like this
public class Program
{
public static void Main(string[] args)
{
IWorker worker = new Worker();
CancellationTokenSource cancellationToken = new CancellationTokenSource();
CreateWebHostBuilder(args, worker, cancellationToken).Build().RunAsync();
worker.Work(cancellationToken.Token).Wait();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args, IWorker worker, CancellationTokenSource cancellationToken) =>
WebHost.CreateDefaultBuilder(args)
.Configure((app) =>
{
var appLifetime = app.ApplicationServices.GetRequiredService<IApplicationLifetime>();
appLifetime.GracefullyTerminate(worker, cancellationToken);
});
}
what i found out is that Startup.cs fit to implement app service that get calls from outside. app.Run refer to what will happen when a request will come.
In addition, microsoft launched in .net core 3.0 a Worker Service project template that fit exactly to this purpose. you can find reference here https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio

Asp.net Core: Can not access a disposed object (different errors)

I know that this problem is here a lot, but I have to say I read everything I could found for like two days and don't get my error.
I created a ASP.net Core REST API and get always different errors:
"Can not access a disposed object.."
"An exception occurred while iterating over the results of a query
for context type.."
"A second operation started on this context before a previous
operation completed"..
Maybe someone of you sees my error or can explain to me, what I'm doing wrong.
Rest-API:
// POST api/events
[HttpPost("create")]
public async Task<IActionResult> CreateAsync([FromBody] EventDTO eventDTO)
{
var newEvent = _mapper.Map<Event>(eventDTO);
try
{
await _eventService.CreateEventAsync(newEvent);
return Ok(newEvent);
}
catch (AppException ex)
{
return BadRequest(new { message = ex.Message });
}
}
Interface:
public interface IEventService
{
Task<IEnumerable<Event>> GetAllEventsAsync();
Task<Event> GetEventByIDAsync(int id);
Task<IEnumerable<Event>> GetEventByCityAsync(string city);
Task<Event> CreateEventAsync(Event newEvent);
void UpdateEventAsync(Event newEvent, Event existing, int eventId);
void DeleteEventAsync(Event existing);
}
Eventservice:
public class EventService : IEventService
{
private MeMeContext _dbContext;
public EventService(MeMeContext dbContext)
{
_dbContext = dbContext;
}
public async Task<Event> CreateEventAsync(Event newEvent)
{
_dbContext.Events.Add(newEvent);
await _dbContext.SaveChangesAsync();
return newEvent;
}
...
}
Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddMvc().
SetCompatibilityVersion(CompatibilityVersion.Version_2_2).
AddJsonOptions(opts => opts.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
services.AddDbContext<MeMeContext>(opts => opts.UseNpgsql(Configuration.GetConnectionString(DATABASE)));
services.AddScoped<MeMeContext>();
// configure DI for application services
services.AddScoped<IUserService, UserService>();
services.AddScoped<IEventService, EventService>();
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new AutoMapperProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
...
}
One thing that I don't understand also, is that I get different errors, when I start my application with Visual Studio or with "dotnet run". One thing that also happens from time to time is, that sometimes my code works, when I do other things on the REST API.
When you need more information, just ask. I'm happy with every hint that you can give me :)
Thanks in advance!
You're not awaiting an async method. As such, the code in the action moves on while that CreateEventAsync logic is running. When the response returns, the context goes away, since its lifetime is that scope.
In other words, you have essentially a race condition. If the CreateEventAsync logic happens to finish before the response returns, everything is fine. However, if it takes longer than returning the response, then the context is gone (along with your other scoped services), and you start throwing exceptions.
Long and short, use the await keyword:
await _eventService.CreateEventAsync(newEvent);
Async is not the same as running something in the background. If you want the action to be able to return before this logic completes, then you should schedule this to run on a background service instead. See: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2&tabs=visual-studio

Error in Seeding IdentityRole in Asp.net core

I am having trouble seeding data into the identity role table. I always get the error
System.NullReferenceException: 'Object reference not set to an
instance of an object.'
<>4__this._roleManager was null
I am not sure why this is happening and why it's not seeding data into the table.How do I fix this?
Below is my code
public class UserRoleSeed
{
private readonly RoleManager<IdentityRole> _roleManager;
private string[] _roleArray = { "Admin, TerminalManager, Dispatcher, Driver, Mechanic, Recruiter, MechanicManger" };
public UserRoleSeed(RoleManager<IdentityRole> roleManager)
{
_roleManager = roleManager;
}
public async void Seed()
{
foreach (string index in _roleArray)
{
if ((await _roleManager.FindByNameAsync(index)) == null)
{
await _roleManager.CreateAsync(new IdentityRole { Name = index });
}
}
}
}
for my Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TransportDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
services.AddIdentity<ApplicationUser, IdentityRole<int>>()
.AddEntityFrameworkStores<TransportDbContext>()
.AddDefaultTokenProviders();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
//app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(
routes =>
{
routes.MapRoute("Default", "{controller=Home}/{action=Index}/{id?}");
});
}
// seeds data into the identity role table
new UserRoleSeed(app.ApplicationServices.GetService<RoleManager<IdentityRole>>()).Seed();
}
}
}
You're using an async method to seed your roles, but you're not awaiting it. That means that your code keeps moving on, eventually taking variables you're depending on in your async method along with it when branches go out of scope. Hence, NullReferenceExceptions.
Additionally, services like RoleManager<TRole> are "scoped" services, meaning they can only be retrieved from a particular active scope. In an actual request, a scope would be created for the request, allowing these services to be injected into anything within the request pipeline. However, here, you have no active scope, and therefore must create one.
Instead of attempting to seed as part of your Configure method, you should move this code out into your Program class. The code below addresses both of the above concerns:
public class Program
{
public static void Main(string[] args) =>
MainAsync(args).GetAwaiter().GetResult();
public static async Task MainAsync(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
await new UserRoleSeed(scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>()).Seed();
}
await host.RunAsync();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
Essentially you'll use an async Main to run your app, which then gives you the ability to await additional things like your seed. For what it's worth, this can be shortened somewhat in C# 7.2 with an actual async Main, i.e.:
public static async Task Main(string[] args)
Without having to proxy from Main to a MainAsync, but under the hood the compiler just sets up this same construction for you.
That's the shortest path to get this code working, but you still have a couple of minor issues. First, you should avoid using async void, which is an antipattern. You're essentially swallowing the async output with that, including any exceptions that may be thrown. You should virtually always use async Task as the return when you don't care about the actual return. The few situations where async void is appropriate are known to individuals who need to use it. In other words, if you don't know when you should use async void, then you shouldn't be using async void.
Also, while there's nothing technically wrong with newing up a class and passing the dependency into the constructor, it's more appropriate in this case to make the class static and pass the required dependencies into the seed method:
await UserRoleSeed.Seed(roleManager);
Finally, again, while not critical, it's convention to name async methods with an Async suffix. This makes it clear that the method is async and prevents accidentally not awaiting the method simply because it's not obvious that it needs to be awaited (which may have been the case here). In short, change the name from Seed to SeedAsync, since it does async work.
Ok guys I figured it out, Below is my solution.
I basically modified the class for seeding the data and renamed it DbInitializer.cs
public class DbInitializer
{
private static readonly string[] _roleArray = { "Admin", "Terminal Manager", "Dispatcher", "Driver", "Mechanic", "Recruiter", "Mechanic Manger" };
public static async Task InitializeAync (TransportDbContext context, IServiceProvider serviceProvider)
{
var roleManager = serviceProvider.GetRequiredService<RoleManager<Role>>();
foreach (string index in _roleArray)
{
if ((await roleManager.FindByNameAsync(index)) == null)
{
await roleManager.CreateAsync(new Role { Name = index });
}
}
}
}}
then I called the function in my Program.cs file as suggested by #Chris Pratt.
public class Program
{
public static void Main(string[] args) =>
MainAsync(args).GetAwaiter().GetResult();
public static async Task MainAsync(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<TransportDbContext>();
await DbInitializer.InitializeAync(context, services);
}
await host.RunAsync();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
} }
thanks to everyone who tried to help me

Cannot access a disposed object in ASP.NET Core when injecting DbContext

On an ASP.NET Core project I have the following on Startup:
services.AddDbContext<Context>(x => x.UseSqlServer(connectionString));
services.AddTransient<IValidationService, ValidationService>();
services.AddTransient<IValidator<Model>, ModelValidator>();
The ValidationService is as follows:
public interface IValidationService {
Task<List<Error>> ValidateAsync<T>(T model);
}
public class ValidationService : IValidationService {
private readonly IServiceProvider _provider;
public ValidationService(IServiceProvider provider) {
_provider = provider;
}
public async Task<List<Error>> ValidateAsync<T>(T model) {
IValidator<T> validator = _provider.GetRequiredService<IValidator<T>>();
return await validator.ValidateAsync(model);
}
}
And the ModelValidator is as follows:
public class ModelValidator : AbstractValidator<Model> {
public ModelValidator(Context context) {
// Some code using context
}
}
When I inject a IValidationService in a controller and use it as:
List<Error> errors = await _validator.ValidateAsync(order);
I get the error:
System.ObjectDisposedException: Cannot access a disposed object. A
common cause of this error is disposing a context that was resolved
from dependency injection and then later trying to use the same
context instance elsewhere in your application. This may occur is you
are calling Dispose() on the context, or wrapping the context in a
using statement. If you are using dependency injection, you should
let the dependency injection container take care of disposing context
instances. Object name: 'Context'.
Any idea why I am having this error when using Context inside ModelValidator.
How to fix this?
UPDATE
So I changed the code to:
services.AddScoped<IValidationService, ValidationService>();
services.AddScoped<IValidator<Model>, ModelValidator>();
But I get the same error ...
UPDATE - Seed Data Code inside Configure method on Startup
So on Configure method I have:
if (hostingEnvironment.IsDevelopment())
applicationBuilder.SeedData();
And the SeedData extension is:
public static class DataSeedExtensions {
private static IServiceProvider _provider;
public static void SeedData(this IApplicationBuilder builder) {
_provider = builder.ApplicationServices;
_type = type;
using (Context context = (Context)_provider.GetService<Context>()) {
await context.Database.MigrateAsync();
// Insert data code
}
}
What am I missing?
UPDATE - A possible solution
Changing my Seed method to the following seems to work:
using (IServiceScope scope =
_provider.GetRequiredService<IServiceScopeFactory>().CreateScope()) {
Context context = _provider.GetService<Context>();
// Insert data in database
}
Just a guess in what causes your error:
You are using DI and async calls. If, somewhere in your call stack, you return a void instead of Task, you get the described behavior. At that point, the call is ended and the context disposed. So check if you have an async call that returns a void instead of Task. If you change the return value, the ObjectDisposedException is probably fixed.
public static class DataSeedExtensions {
private static IServiceProvider _provider;
public static async Task SeedData(this IApplicationBuilder builder) { //This line of code
_provider = builder.ApplicationServices;
_type = type;
using (Context context = (Context)_provider.GetService<Context>()) {
await context.Database.MigrateAsync();
// Insert data code
}
}
And in configure:
if (hostingEnvironment.IsDevelopment()){
await applicationBuilder.SeedData();
}
Blog post on how to fix this error: Cannot access a disposed object in ASP.NET Core when injecting DbContext
I had a similar issue working with asp.net core. I have an async POST method in my controller and when it returns void I will have this exception. After I changed the POST method return a TASK the problem was solved.
Change from:
public async void PostAsync([FromBody] Model yourmodel)
To
public async Task PostAsync([FromBody] Model yourmodel)
Update for ASP.NET Core 2.1
In ASP.NET Core 2.1 the methods changed slightly. The general method is similar to the 2.0, just the methods name and return types have been changed.
public static void Main(string[] args)
{
CreateWebHostBuilder(args)
.Build()
.Seed();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
return new WebHostBuilder()
...; // Do not call .Build() here
}
Applies for ASP.NET Core 2.0
With ASP.NET Core 2.0 there have been some changes in how EF Core tools (dotnet ef migrations etc.) determine the DbContext and connection string at design time.
The below answer leads that the migrations and seeding are applied when calling any of the dotnet ef xxx commands.
The new pattern for getting a design time instance for the EF Core tools is by using an BuildHostWeb static method.
As per this announcement, EF Core will now use the static BuildWebHost method which configures the whole application, but doesn't run it.
public class Program
{
public static void Main(string[] args)
{
var host = BuildWebHost(args);
host.Run();
}
// Tools will use this to get application services
public static IWebHost BuildWebHost(string[] args) =>
new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
}
Replace this in your old Main method
public static void Main(string[] args)
{
var host = BuildWebHost(args)
.Seed();
host.Run();
}
Where Seed is an extension method:
public static IWebHost Seed(this IWebHost webhost)
{
using (var scope = webhost.Services.GetService<IServiceScopeFactory>().CreateScope())
{
// alternatively resolve UserManager instead and pass that if only think you want to seed are the users
using (var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
{
SeedData.SeedAsync(dbContext).GetAwaiter().GetResult();
}
}
}
public static class SeedData
{
public static async Task SeedAsync(ApplicationDbContext dbContext)
{
dbContext.Users.Add(new User { Id = 1, Username = "admin", PasswordHash = ... });
}
}
Old Answer, still applies to ASP.NET Core 1.x
There is a semi-official pattern on how to seed Entity Framework Core in ASP.NET Core application you should apply, because during application startup there is no Request and hence no RequestServices (which resolves scoped services).
In essence it boils down to creating a new scope, resolve the types you need and dispose the scope again once you're finished.
// serviceProvider is app.ApplicationServices from Configure(IApplicationBuilder app) method
using (var serviceScope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var db = serviceScope.ServiceProvider.GetService<AppDbContext>();
if (await db.Database.EnsureCreatedAsync())
{
await SeedDatabase(db);
}
}
One of the reasons directly resolving a service via app.ApplicationServices.GetService<MyService>() is that ApplicationServices is the application (or lifetime) scope provider and the services resolved here stay alive until the application is shut down.
Usually the scoped container will resolve from it's parent container, if the object already exists there. So if you instantiate the DbContext this way in the application, it will be available in ApplicationServices container and when a request happens, a child container will be created.
Now when resolving the DbContext it won't be resolved as scoped, because it already exists in the parent container, so the instance of the parent container will be returned instead. But since it has been disposed during the seeding, it won't be accessible.
A scope container is nothing else then a singleton container with limited lifetime.
So never resolve scoped services in Application startup w/o using the pattern above of first creating a scope and resolving from it.
If you are using any async void please replace it with async Task
Had the same issue. Hope this helps someone. In addition to making the method async and return a Task, you need to make sure that the method will also be awaited wherever you are calling it.
the problem is that DBContext is scoped per request by default, but you have things that depend on it scoped as transient, so they do not have the same scope and DBContext may be disposed before you are done using it
Similar to Yang Zhang, I had to change my controller function
From:
public IActionResult MyFunc([FromBody]string apiKey)
To:
public async Task<IActionResult> MyFunc([FromBody]string apiKey)
I'd like to share my solution for those who are trying to start a background task in their controllers. That means you want to start a task and don't want to wait for the result like audit logging to database. If you are creating a task and try to do database operations in that task you will receive this error;
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.\r\nObject name: 'DBContext'.
Already explained in details. Find it here
In my case, it wasn't an Async problem, but the code had a
using (DataContext dc=dataContext) {}
block, and of course, the context was disposed after that.
In my case the controller method was async and it was returning a task but inside that I had 2 await calls. First await calls gets some data from a service and second await call writes to the DB using EF. I had to remove the await from this second call and only then it worked. I didn't remove async/await from method signatures. I just called the second method without await.
I was facing a similar error and later was able to resolve it.
I was calling the async method without using await.
old code
var newUser = _repo.Register(newUserToCreate);
with the fix made
var newUser = await _repo.Register(newUserToCreate);

How to seed Users in ASP.NET 5 Startup.cs with Async

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().

Categories

Resources