Problem
When you try to do a synchronous IO operation in ASP.NET Core (such as writing to a file), you get the following exception:
System.InvalidOperationException: ‘Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true
The reason you’re getting this is because server option AllowSynchronousIO is false. Starting in ASP.NET Core 3.0, this is false by default.
Solution
Microsoft turned this setting off by default because doing sync IO is known to cause thread starvation and app hangs. You can either turn the setting back on, or use the async versions of the IO calls instead. I’ll show both options below.
Option 1 – Use async IO methods
The long-term solution is to use async IO calls. For example, instead of WriteLine(), use WriteLineAsync().
In order to use async/await effectively, you need to use it throughout the entire call chain, starting from the API entry method.
As a simple example, here is an endpoint that sets up an SSE stream, and then uses WriteLine() to write a message to the subscriber. This results in the Synchronous operations are disallowed exception.
[HttpGet]
[Route("messages/subscribe/{id}")]
public void Subscribe(string id)
{
Response.ContentType = "text/event-stream";
Response.StatusCode = 200;
StreamWriter streamWriter = new StreamWriter(Response.Body);
streamWriter.WriteLine($"{DateTime.Now} Hello subscriber");
streamWriter.Flush();
}
Code language: C# (cs)
Here’s how to refactor the code to be entirely async:
- Change the Subscribe() method to use async Task.
- Replace WriteLine() with WriteLineAsync() and await it.
- Replace Flush() with FlushAsync() and await it.
[HttpGet]
[Route("messages/subscribe/{id}")]
public async Task Subscribe(string id)
{
Response.ContentType = "text/event-stream";
Response.StatusCode = 200;
StreamWriter streamWriter = new StreamWriter(Response.Body);
await streamWriter.WriteLineAsync($"{DateTime.Now} Hello subscriber");
await streamWriter.FlushAsync();
}
Code language: C# (cs)
Now when I call this endpoint it no longer throws the exception.
Option 2 – Set AllowSynchronousIO=true
If you need a quick, short-term solution to this problem, then set AllowSynchronousIO=true. That allows you to keep using synchronous IO method calls. Here’s an example of how to set this option in the initialization code:
//If IIS
builder.Services.Configure<IISServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
//If Kestrel
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.AllowSynchronousIO = true;
});
Code language: C# (cs)
Note: Before .NET 6, do this in Startup.ConfigureServices().