Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XElement to file save with async/await in C#

I tried to write xml file from XElement object with async/await feature. But I realized the XElement.Save() does not operate with async/await.

Maybe the solution can be to use XElement.Save(Stream) with FileStream object...

So, I write some code as below but hard to treat with filestream things.

public async Task SaveAsync(XElement xml, string filename)
{
    using (var fs = new FileStream(filename, FileMode.Create))
    {
        xml.Save(fs);
        await fs.WriteAsync(**please_help_me**);
    }
}

how to do with this approach or is there any other solution?

like image 753
Youngjae Avatar asked Dec 01 '25 16:12

Youngjae


2 Answers

If you really don't want to block on IO (and hang a ThreadPool thread/your thread), you'll probably need to use a temporary MemoryStream:

using(var ms = new MemoryStream())
using(var fs = new FileStream("myFile", FileMode.Create))
{
    xml.Save(ms);
    ms.Position = 0;
    await ms.CopyToAsync(fs)
}
like image 192
spender Avatar answered Dec 03 '25 04:12

spender


As of .NET Core 2.0 and .NET Standard 2.1, XElement and XDocument both have SaveAsync() methods. Thus you can now do:

public static async Task SaveAsync(this XElement xml, string filename, SaveOptions options = default, CancellationToken cancellationToken = default)
{
    // "await using" introduced in c# 8 / .NET Core 3.0+
    await using var stream =
        new FileStream(
        filename, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, 
        useAsync: true);

    await xml.SaveAsync(stream, options, cancellationToken);
}

Notes:

  • In .NET Core 3, XDocument.SaveAsync() (and probably XElement.SaveAsync() also) still contained at least one blocking call. See XDocument.SaveAsync has a blocking call #29464. The issue should be fixed in .NET 5.

  • According to the docs, the useAsync argument to the FileStream constructor

    Specifies whether to use asynchronous I/O or synchronous I/O. However, note that the underlying operating system might not support asynchronous I/O, so when specifying true, the handle might be opened synchronously depending on the platform. When opened asynchronously, the BeginRead(Byte[], Int32, Int32, AsyncCallback, Object) and BeginWrite(Byte[], Int32, Int32, AsyncCallback, Object) methods perform better on large reads or writes, but they might be much slower for small reads or writes. If the application is designed to take advantage of asynchronous I/O, set the useAsync parameter to true. Using asynchronous I/O correctly can speed up applications by as much as a factor of 10, but using it without redesigning the application for asynchronous I/O can decrease performance by as much as a factor of 10.

    Since you know you want to write your XElement asynchronously, presumably you have designed your application for asynchronous I/O. However, you might want to benchmark with and without asynchronous IO to confirm you are not harming performance.

Demo fiddle here.

like image 39
dbc Avatar answered Dec 03 '25 04:12

dbc