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>();