Home > OS >  C# Processing CSV file asynchronous with CSVHelper does not seem to work
C# Processing CSV file asynchronous with CSVHelper does not seem to work

Time:07-23

I am creating a simple project with Blazor in C# where users can upload a CSV file and the app processes this file asynchronously to an object so that it can create a database record of this object.

I am getting stuck at processing the CSV file. I tried to process the CSV file myself first and this worked but read on several posts here that it would be wiser to use a library like CSVHelper. I am trying to use this library but it does not seem like it is doing anything and I cannot figure out what is going wrong.

I am using console.Writeline to check if anything is in the records collection but it seems empty. Can anybody explain what I am doing wrong or what I am missing?

This is the page:

@using CsvHelper
@using System.Globalization
@using System.Text
@using CsvHelper.Configuration
@using CompanyName.Data
@using CompanyName.Services.CSV
@page "/uploadcsv"

<PageTitle>Upload CSV file</PageTitle>

<h1>Upload CSV file</h1>
<p>
    <label>
        Upload your CSV here:
        <InputFile OnChange="@LoadFile" multiple />
    </label>
</p>

@code {

    private async Task LoadFile(InputFileChangeEventArgs e)
    {
        foreach (var file in e.GetMultipleFiles())
        {
            var config = new CsvConfiguration(CultureInfo.InvariantCulture)
            {
                Delimiter = ";",
                Encoding = Encoding.UTF8
            };
            using (var reader = new StreamReader(file.OpenReadStream()))
            using (var csv = new CsvReader(reader, config))
            {
                csv.Context.RegisterClassMap<OrderMap>();
                var records = csv.GetRecordsAsync<Order>();
                await foreach (var order in records)
                {
                    Console.WriteLine(order.OrderNumber);
                }

            }
        }
    }
}

This is the class that I use:

namespace CompanyName.Data;

    public class Order
    {
        public string OrderNumber { get; set; }
        public string RecipientName { get; set; }
        public string RecipientStreet { get; set; }
        public string RecipientPostalCode { get; set; }
        public string RecipientCity { get; set; }
        public string CustomerNotes { get; set; }
        public string TypeOfCargo { get; set; }
        public int ShipmentQuantity { get; set; }
        public string? LocationId { get; set; }
    }

This is the classmap to map the CSV to the object:

using CsvHelper.Configuration;
using CompanyName.Data;

namespace CompanyName.Services.CSV;

public sealed class OrderMap : ClassMap<Order>
{
    public OrderMap()
    {
        Map(o => o.OrderNumber).Name("ActivityId");
        Map(o => o.RecipientName).Name("LocationName");
        Map(o => o.RecipientStreet).Name("LocationStreet");
        Map(o => o.RecipientPostalCode).Name("LocationPostalCode");
        Map(o => o.RecipientCity).Name("LocationCity");
        Map(o => o.CustomerNotes).Name("ActivityNotes");
        Map(o => o.TypeOfCargo).Name("ShipmentTypeOfCargo");
        Map(o => o.ShipmentQuantity).Name("ShipmentQuantity");
        Map(o => o.LocationId).Ignore();
    }
}

Update

I have tried altering it to a synchronous flow (private void LoadFile with csv.GetRecords<Order>() and without using await) and this results in the same exception that I found when debugging the async task only now it immediately shows this when uploading the CSV. This is the stacktrace:

System.NotSupportedException: Synchronous reads are not supported.
   at Microsoft.AspNetCore.Components.Forms.BrowserFileStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at System.IO.StreamReader.ReadBuffer(Span`1 userBuffer, Boolean& readToUserBuffer)
   at System.IO.StreamReader.ReadSpan(Span`1 buffer)
   at System.IO.StreamReader.Read(Char[] buffer, Int32 index, Int32 count)
   at CsvHelper.CsvParser.Read()
   at CsvHelper.CsvReader.Read()
   at CsvHelper.CsvReader.GetRecords[T]() MoveNext()
   at CompanyNameLogistics.Pages.UploadCSV.LoadFile(InputFileChangeEventArgs e) in C:\Users\User\Documents\CompanyNameLogistics\CompanyNameLogistics\Pages\UploadCSV.razor:line 33`

CodePudding user response:

I think you have encountered a bug in the CsvHelper library. "dbc" pointed to issue #1751 in the CsvHelper repo, and I believe you are experiencing the same issue, which is a deadlock caused by async code that doesn't call ConfigureAwait(false). You can read about the details of that [here]:(https://devblogs.microsoft.com/dotnet/configureawait-faq/)

This issue happens when the code runs under a SynchronizationContext, which it appears is the case for Blazor server, as well as in windows GUI applications.

I opened a pull request which should fix this issue, though you would also need to ConfigureAwait(false) all of your async invocations: https://github.com/JoshClose/CsvHelper/pull/2007

In the mean time, you can pull and build the "configureawait" branch at: https://github.com/MarkPflug/CsvHelper/

That should at least allow you to answer if this will fix your issue.

CodePudding user response:

You are calling an async method, but not awaiting the results. Change this:

var records = csv.GetRecordsAsync<Order>();

To this:

var records = await csv.GetRecordsAsync<Order>();
  • Related