Ticket T1283054
Visible to All Users

Excel Exporter - IAsyncDisposable

created 6 months ago

Hello,

The class:
DevExpress.Export.Xl.IXlDocument

Does not implement:
IAsyncDisposable

This causes an error when disposing the object from an async method. As the direction of travel with C# is everything is moving async, could I please request that the IAsyncDisposable is implemented for this class?

This issue is present on the latest version, 24.2.5.

Example code:

C#
public static async Task CreateExcelFileAsync(XlDocumentFormat format, Stream responseStream, IAsyncEnumerable<IDictionary<string, object?>> values) { IXlExporter exporter = XlExport.CreateExporter(format); using (IXlDocument document = exporter.CreateDocument(responseStream)) { // .. logic } // Error here on the dipose }
Show previous comments (1)
Sasha (DevExpress Support) 6 months ago

    Hi,

    I couldn't reproduce the issue on my side. Please refer to the attached project. As you can see, the XlExport library creates an XLSX file and the issue doesn't occur. Would you please modify the attached project to illustrate the issue?

    I look forward to your response.

      Hi Sasha,

      Please see attached.

      There is a workaround, as below code to allow synchronous IO, but this is using a sledgehammer to crack a walnut. I don't believe that there's a lot of work in implementing the IAsyncDisposable interface, which would be a much nicer solution.

      Code
      // Need this for: IXlDocument. It won't dispose otherwise, DevExpress issue services.Configure<KestrelServerOptions>(options => { options.AllowSynchronousIO = true; }); // Need this for: IXlDocument. It won't dispose otherwise, DevExpress issue services.Configure<IISServerOptions>(options => { options.AllowSynchronousIO = true; });

      Edit: I didn't realise the other project reference hadn't been removed. It's not required for the example. To clarify, the error occurs as below screenshot, if you just run it up and execute the endpoint in SwaggerClipboard-File-2.png

      Sasha (DevExpress Support) 6 months ago

        Hi,

        Thank you for the sample project. We need additional time to research it. I will update this thread once we have news.

        Answers approved by DevExpress Support

        created 6 months ago (modified 6 months ago)

        Hi,

        Thank you for your patience. I reviewed your project and found that the issue is not directly related to the fact that IXlExporter doesn't implement the IAsyncDisposable interface. The issue occurs because you pass the Response.Body property of the HttpResponseStream type to the CreateExcelFileAsync method. This results in an error because the HttpResponseStream doesn't support the Write method and requires the WriteAsync method.

        The Excel Export Library is not thread-safe; thus, I recommend that you modify the CreateExcelFileAsync method as follows:

        C#
        public static async Task CreateExcelFileAsync(XlDocumentFormat format, Stream stream) { await Task.Run(() => { var variables = GetValues(); IXlExporter exporter = XlExport.CreateExporter(format); using (IXlDocument document = exporter.CreateDocument(stream)) { using (var sheet = document.CreateSheet()) { sheet.Name = "Sheet 1"; using (var column = sheet.CreateColumn()) { } using (var row = sheet.CreateRow()) { foreach (var value in variables) { using (var cell = row.CreateCell()) { cell.Value = value; } } } } } }); } public static IEnumerable<int> GetValues() { for (int indexer = 0; indexer < 10; indexer++) { yield return indexer; } }

        Then, you can modify the GetExcel method in the following manner:

        C#
        //solution 1 [HttpGet("getExcelOne")] public async Task<IActionResult> GetExcelOne() { using MemoryStream ms = new MemoryStream(); await CreateExcelFileAsync(XlDocumentFormat.Xlsx, ms); return File(ms.ToArray(), "application/octet-stream", fileName); } //solution 2 [HttpGet("getExcelTwo")] public async Task GetExcelTwo() { using MemoryStream ms = new MemoryStream(); await CreateExcelFileAsync(XlDocumentFormat.Xlsx, ms); Response.Clear(); Response.Headers.Append("Content-Disposition", "attachment; filename=" + fileName); Response.Headers.Append("Content-Length", ms.Length.ToString()); Response.ContentType = "application/octet-stream"; await Response.Body.WriteAsync(ms.ToArray(), 0, (int)ms.Length); await Console.Out.WriteLineAsync("This won't be hit"); }

        Attached is the updated project. Please review it and let me know if this solution is suitable.

          Comments (2)

            Hi Sasha,

            That makes perfect sense, thank you. Yes, that does resolve the current issue.

            Could I please ask if there's any plans for the Excel Export Library to be made thread safe in the future? Just to close off the topic.

            Sasha (DevExpress Support) 6 months ago

              Hi,

              I'm happy to hear that this solution is suitable.

              Could I please ask if there's any plans for the Excel Export Library to be made thread safe in the future? Just to close off the topic.

              Sure. I discussed your usage scenario with the team, but we don't have immediate plans to make the Excel Export Library thread-safe. We recommend you use it in a single background thread to avoid synchronization issues.

              Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

              Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.