我有一个服务,它接收包含CSV数据的输入流,需要批量插入到数据库中,我的应用程序尽可能使用async/await。
处理过程是:使用CsvHelper的CsvParser解析流,将每行添加到DataTable中,使用SqlBulkCopy将DataTable复制到数据库中。
数据可能是任意大小,因此我希望避免一次性将所有数据读入内存 - 很明显,最终我会在DataTable中拥有所有数据,因此在内存中实际上会有2个副本。
我想尽可能异步地完成所有这些操作,但CsvHelper没有任何异步方法,因此我想到了以下解决方法:
这个解决方案有什么问题吗?它是否必要?我看到 CsvHelper 的开发人员明确没有添加异步功能(https://github.com/JoshClose/CsvHelper/issues/202),但我不太明白他们没有这样做的原因。
编辑:我刚意识到,这个解决方案在列包含换行符的情况下也不起作用 :( 我想我只能将整个输入流复制到 MemoryStream 或其他地方。
编辑2:更多信息。
这是一个异步方法,在库中我尝试实现全程异步。它可能会被 MVC 控制器使用(如果我只是想从 UI 线程卸载它,我会使用 Task.Run())。大部分时间,该方法将等待外部来源,如数据库 / DFS,并且我希望在等待期间释放线程。
CsvParser.Read()会阻塞,即使阻塞的是读取流(例如,如果我试图读取的数据位于世界另一端的服务器上),而如果CsvHelper实现了一个使用TextReader.ReadAsync()的异步方法,那么我就不会被阻塞等待来自迪拜的数据。据我所知,我并没有要求在同步方法周围添加异步包装器。
编辑3:来自未来的更新!实际上,异步功能已经在2017年添加到CsvHelper中。我希望我当时工作的公司有升级到更新版本!
处理过程是:使用CsvHelper的CsvParser解析流,将每行添加到DataTable中,使用SqlBulkCopy将DataTable复制到数据库中。
数据可能是任意大小,因此我希望避免一次性将所有数据读入内存 - 很明显,最终我会在DataTable中拥有所有数据,因此在内存中实际上会有2个副本。
我想尽可能异步地完成所有这些操作,但CsvHelper没有任何异步方法,因此我想到了以下解决方法:
using (var inputStreamReader = new StreamReader(inputStream))
{
while (!inputStreamReader.EndOfStream)
{
// Read line from the input stream
string line = await inputStreamReader.ReadLineAsync();
using (var memoryStream = new MemoryStream())
using (var streamWriter = new StreamWriter(memoryStream))
using (var memoryStreamReader = new StreamReader(memoryStream))
using (var csvParser = new CsvParser(memoryStreamReader))
{
await streamWriter.WriteLineAsync(line);
await streamWriter.FlushAsync();
memoryStream.Position = 0;
// Loop through all the rows (should only be one as we only read a single line...)
while (true)
{
var row = csvParser.Read();
// No more rows to process
if (row == null)
{
break;
}
// Add row to DataTable
}
}
}
}
这个解决方案有什么问题吗?它是否必要?我看到 CsvHelper 的开发人员明确没有添加异步功能(https://github.com/JoshClose/CsvHelper/issues/202),但我不太明白他们没有这样做的原因。
编辑:我刚意识到,这个解决方案在列包含换行符的情况下也不起作用 :( 我想我只能将整个输入流复制到 MemoryStream 或其他地方。
编辑2:更多信息。
这是一个异步方法,在库中我尝试实现全程异步。它可能会被 MVC 控制器使用(如果我只是想从 UI 线程卸载它,我会使用 Task.Run())。大部分时间,该方法将等待外部来源,如数据库 / DFS,并且我希望在等待期间释放线程。
CsvParser.Read()会阻塞,即使阻塞的是读取流(例如,如果我试图读取的数据位于世界另一端的服务器上),而如果CsvHelper实现了一个使用TextReader.ReadAsync()的异步方法,那么我就不会被阻塞等待来自迪拜的数据。据我所知,我并没有要求在同步方法周围添加异步包装器。
编辑3:来自未来的更新!实际上,异步功能已经在2017年添加到CsvHelper中。我希望我当时工作的公司有升级到更新版本!