I have this formatter in my .NET Core 3.1 project (which I recently upgraded from 2.1):

public class JilOutputFormatter : TextOutputFormatter {


    public JilOutputFormatter() => 
        JilFormatterConfig.AddSupportedHeaders(SupportedMediaTypes, SupportedEncodings);

    public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) {
        using (var writer = new StreamWriter(context.HttpContext.Response.Body)) {
            JSON.Serialize(context.Object, writer, MyOptions);
            writer.Flush();
        }

        return Task.FromResult(true);
    }

}

And I'm adding it to pipeline with this snippet:

services.AddMvcCore(o => {
    o.OutputFormatters.Insert(0, new JilOutputFormatter());
}).AddOthersBlahBlah();

It was working like a charm when the application was on 2.1. But now on 3.1 I'm getting this error:

An unhandled exception occurred while processing the request. InvalidOperationException: Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead.

I tried to async the write operation, but can't find the method on Jil. Do you have any idea please?

NOTE: I know there are some answers - like this one - that are saying how to AllowSynchronousIO. But I'm interested on how to async write in Jil.

3

There are 3 best solutions below

0
On

You'll have to use the 3.0 alpha versions. Jil doesn't even include the word Task in the source code in the latest stable version, 2.17 (or Github search is having some issues).

Version 3.0 uses Pipelines directly. You can use the SerializeAsync(T, PipeWriter , Encoding, Options, CancellationToken). Maybe you can work with HttpContext.Response.BodyWriter. I haven't tested this though.

Eg :

public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context,
                                                  Encoding selectedEncoding) 
{
    var data=context.Object;
    var writer=contest.Response.BodyWriter;
    await JSON.SerializeAsync(data,writer,selectedEncoding);
}
1
On

Errors can revolve around ReadAsync, WriteAsync, and FlushAsync with outputs similar to what is listed below.

Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead.

Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead.

Synchronous operations are disallowed. Call FlushAsync or set AllowSynchronousIO to true instead.

As a temporary workaround, you can set the value of AllowSynchronousIO in your ConfigureServices method found in your Startup class.

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<KestrelServerOptions>(options =>
    {
        options.AllowSynchronousIO = true;
    });

    // If using IIS:
    services.Configure<IISServerOptions>(options =>
    {
        options.AllowSynchronousIO = true;
    });

    // other services
}

It isn’t a great workaround, but it will keep you moving forward. The better solution is to upgrade your libraries and perform all your actions asynchronously.

See the detailed post .NET Core 3.0 AllowSynchronousIO Workaround by Khalid Abuhakmeh

0
On

TLDR: As of Dotnet Core 5.0, the default web-server (Kestral) is designed to perform only Async Level work to be the most performant. Enable Sync within Kestral.

Rational: Due to to the majority of software being more IO Dependent than CPU Dependent, Async Programming allows for the system to perform other work, while waiting for the IO to complete (IE; Writing to Disk, Reading something from the network).

Place this within Startup.cs within the ConfigurationService function.

   services.Configure<KestrelServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });