Reputation: 1
I am trying to read an xlsx file using CloseXlm so I created the below ReadFile method. In order to test it, I've created a test method using MSTest and another method that creates an xlsx file and save it into a memory stream that is returned. That same memory stream is then used by the test to check if the functionality is working. However, I keep getting the error "Cannot access a disposed object" and I can't find the reason why.
ReadFile method:
public async Task<IEnumerable<string[]>> ReadFile(Stream fileReader)
{
using var workbook = new XLWorkbook(fileReader);
var worksheets = workbook.Worksheets;
return worksheets.SelectMany(worksheet =>
worksheet.RowsUsed()
.Skip(1)
.Select(row => ExtractRowData(row)));
}
private string[] ExtractRowData(IXLRow row)
{
return row.Cells()
.Where(cell => !string.IsNullOrWhiteSpace(cell.Value.ToString()))
.Select(cell => cell.Value.ToString())
.ToArray();
}
Test class
[TestClass]
public class ReadXlsxFile
{
private XlsxFileReaderStrategy _excelReader;
[TestInitialize]
public void SetUp()
{
_excelReader = new XlsxFileReaderStrategy();
}
[TestMethod]
public async Task ReadExcelFile_MultipleSheets_ReturnsCorrectData()
{
// Arrange
var rowDataSheet1 = new List<string[]>
{
new[] { "Header1", "Header2", "Header3" },
new[] { "Data1", "Data2", "Data3" },
new[] { "Data4", "Data5", "Data6" }
};
var rowDataSheet2 = new List<string[]>
{
new[] { "HeaderA", "HeaderB", "HeaderC" },
new[] { "Info1", "Info2", "Info3" },
new[] { "Info4", "Info5", "Info6" }
};
// Crear archivo Excel en MemoryStream
using var memoryStream = CreateExcelFileWithData(rowDataSheet1);
// Act
memoryStream.Seek(0, SeekOrigin.Begin);
var result = await _excelReader.ReadFile(memoryStream);
// Assert
Assert.AreEqual(3, result.Count()); // 3 filas en la primera hoja
var row1 = result.ElementAt(0);
Assert.AreEqual("Data1", row1[0]);
Assert.AreEqual("Data2", row1[1]);
Assert.AreEqual("Data3", row1[2]);
}
private MemoryStream CreateExcelFileWithData(List<string[]> data)
{
// Crear el archivo Excel en memoria
var workbook = new XLWorkbook();
// Crear una hoja llamada "Sheet1"
var worksheet = workbook.AddWorksheet("Sheet1");
// Escribir los datos en las celdas
for (int row = 0; row < data.Count; row++)
{
for (int col = 0; col < data[row].Length; col++)
{
worksheet.Cell(row + 1, col + 1).Value = data[row][col];
}
}
// Crear un MemoryStream para almacenar el archivo Excel en memoria
var memoryStream = new MemoryStream();
workbook.SaveAs(memoryStream); // Guardar en el MemoryStream
memoryStream.Seek(0, SeekOrigin.Begin); // Reposicionar el flujo al principio
// No se llama a Dispose() aquí, porque el flujo se gestionará en el test
return memoryStream;
}
}
Upvotes: 0
Views: 57
Reputation: 131
SelectMany
returns an IEnumerable
, which is not "calculated" until you enumerate it. The first time this happens is when you call result.Count()
in your assert statement. By then, the XLWorkbook
is already disposed because of the using
statement in ReadFile
, resulting in an ObjectDisposedException
. For a solution, you have two options. Either pass the XLWorkbook
into the ReadFile
method, thus avoiding it being disposed prematurely, or you materialize the enumeration before you leave ReadFile
:
public async Task<IEnumerable<string[]>> ReadFile(Stream fileReader)
{
using var workbook = new XLWorkbook(fileReader);
var worksheets = workbook.Worksheets;
return worksheets.SelectMany(worksheet =>
worksheet.RowsUsed()
.Skip(1)
.Select(row => ExtractRowData(row)))
.ToList(); // <---
}
The second solution seems to be the clearer solution in your case. You can also use other "materializing" methods like ToArray()
as well.
A side note - your ReadFile
method doesn't seem to have any asynchronous calls, so you should probably convert it to a regular method.
Upvotes: 0