dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.25k stars 4.73k forks source link

QueryXmlWriter does not support Async #61963

Open epatrick opened 2 years ago

epatrick commented 2 years ago

Description

When pairing an XSLT with an XmlWriter using XmlWriterSettings.Async = true, async operations throw a NotImplementedException. Details founds in this SO post, with this fiddle.

This looks unintentional, just an edge case not covered?

The use case is leveraging XSLT in an ASP.NET core pipeline using the recommend async IO.

Reproduction Steps

using System.IO;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Xsl;

public class Program
{
    public static void Main()
    {
        Test().GetAwaiter().GetResult();
    }

    public static async Task Test()
    {

        var transform = new XslCompiledTransform();
        using var reader = XmlReader.Create(new StringReader(@"
<xsl:stylesheet version=""1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"">
    <xsl:output method=""html"" indent=""yes"" doctype-system=""html""/>
    <xsl:template match=""/"">
        <bar/>
    </xsl:template>
</xsl:stylesheet>"));
        transform.Load(reader);

        var settings = transform.OutputSettings.Clone();
        settings.CloseOutput = false;
        settings.Async = true;

        var method = settings.OutputMethod;

        using var stream = new MemoryStream();
        using (var writer = XmlWriter.Create(stream, settings))
        {
            await writer.WriteStartDocumentAsync();
            await writer.WriteStartElementAsync(null, "foo", null);
            await writer.WriteEndElementAsync();
            await writer.WriteEndDocumentAsync();
        }
        stream.Position = 0;
        var content = new StreamReader(stream).ReadToEnd();
    }
}

Expected behavior

XmlWriter.Write*Async methods are supported.

Actual behavior

System.NotImplementedException is thrown.

Regression?

No response

Known Workarounds

Avoid the combination of XmlWriterSettings.OutputMethod = OutputMethod.Html and emitting a doctype of "html".

Working XSLT:

<xsl:stylesheet version="1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" indent="yes"/>
    <xsl:template match="/">
        <bar/>
    </xsl:template>
</xsl:stylesheet>

Failing XSLT:

<xsl:stylesheet version="1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" indent="yes" doctype-system="html"/>
    <xsl:template match="/">
        <bar/>
    </xsl:template>
</xsl:stylesheet>

Configuration

.net5.0, .net6 rc2.

Other information

No response

ghost commented 2 years ago

Tagging subscribers to this area: @dotnet/area-system-xml See info in area-owners.md if you want to be subscribed.

Issue Details
### Description When pairing an XSLT with an `XmlWriter` using `XmlWriterSettings.Async = true`, async operations throw a `NotImplementedException`. Details founds in [this SO post](https://stackoverflow.com/a/70075455/1088293), with [this fiddle](https://dotnetfiddle.net/COrx9M). This look unintentional, just an edge case not covered? The use case is leveraging XSLT in an ASP.NET core pipeline using the recommend async IO. ### Reproduction Steps ```csharp using System.IO; using System.Threading.Tasks; using System.Xml; using System.Xml.Xsl; public class Program { public static void Main() { Test().GetAwaiter().GetResult(); } public static async Task Test() { var transform = new XslCompiledTransform(); using var reader = XmlReader.Create(new StringReader(@" ")); transform.Load(reader); var settings = transform.OutputSettings.Clone(); settings.CloseOutput = false; settings.Async = true; var method = settings.OutputMethod; using var stream = new MemoryStream(); using (var writer = XmlWriter.Create(stream, settings)) { await writer.WriteStartDocumentAsync(); await writer.WriteStartElementAsync(null, "foo", null); await writer.WriteEndElementAsync(); await writer.WriteEndDocumentAsync(); } stream.Position = 0; var content = new StreamReader(stream).ReadToEnd(); } } ``` ### Expected behavior XmlWriter.Write*Async methods are supported. ### Actual behavior `System.NotImplementedException` is thrown. ### Regression? _No response_ ### Known Workarounds Avoid the combination of XmlWriterSettings.OutputMethod = OutputMethod.Html and emitting a doctype of "html". Working XSLT: ```xml ``` Failing XSLT: ```xml ``` ### Configuration .net5.0, .net6 rc2. ### Other information _No response_
Author: epatrick
Assignees: -
Labels: `area-System.Xml`, `untriaged`
Milestone: -
kronic commented 2 years ago

see

epatrick commented 2 years ago

@kronic I think this case is different; it's really about the XmlWriter rather than the XslCompiledTransform. The transform is quick, but the results are being written to the HttpResponse. My goal is to avoiding having to re-enable SyncIO in the ASP.NET core pipeline.

krwq commented 2 years ago

@epatrick assuming this is a small change would you be interested in sending a patch? We currently don't have cycles to actively look into non-regression bugs in XML. If it's a larger change it would be nice to get some initial idea on how much and types of changes are required to get us to this scenario to work

Misiu commented 1 year ago

I've found this when trying to add async to DataverseClient: https://github.com/Data8/DataverseClient/pull/36#issuecomment-1701021824 Any plans to add those missing methods? In my case WriteEndDocumentAsync and FlushAsync

epatrick commented 1 year ago

@Misiu apologies, but I've not had time to test a fix and submit a pull request. The SO post has pretty good detail around the need to override QueryOutputWriter, if you want to take a stab.