1

I am developing an api, which has to return csv file on some endpoint. Here's my controller responsible for csv generation:

    [ApiController]
    [Route("api/[controller]")]
    [Authorize]
    public sealed class ReportController : BaseController
    {
        public ReportController(ICommandBus commandBus,
            IQueryBus queryBus)
                : base(commandBus, queryBus)
        {
        }

        [HttpGet]
        public async Task<IActionResult> GetReportAsync([FromQuery] GenerateReportRequest request)
        {
            try
            {
                var report = await QueryBus
                    .SendAsync<GenerateReportQuery, Report>(new GenerateReportQuery
                    {
                        Filters = request.Filters,
                        ResponseFileFormat = request.ResponseFileFormat,
                        WithPodOnly = request.WithPodOnly
                    });

                return File(report.Content,
                    report.Type,
                    report.Name);
            }
            catch (Exception e)
            {
                // ToDo: Handle exception in proper way
                return StatusCode(StatusCodes.Status500InternalServerError,
                    e.Message);
            }
        }
    }

When the request comes to my api, certain handler is invoked, and the csv generation starts in CsvGenerationStrategy class, which is attached below:

    public class CsvGenerationStrategy : IReportGenerationStrategy
    {
        public async Task<Report> GenerateReportAsync(ICollection<ShipmentEntity> shipmentEntities)
        {
            var shipment = shipmentEntities
                .Select(s => (Shipment) s)
                .ToList();

            await using var memoryStream = new MemoryStream();
            await using var streamWriter = new StreamWriter(memoryStream);
            await using var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture);
            
            csvWriter.Configuration.Delimiter = ";";
            
            await csvWriter.WriteRecordsAsync(shipment);
            var content = memoryStream.ToArray();

            var report = new Report
            {
                Content = content,
                Type = ReportConstants.CsvFileType,
                Name = ReportConstants.CsvReportFileName
            };

            return report;
        }

        private class Shipment
        {
            [Name(ReportConstants.IssueColumnName)]
            public string Issue { get; set; }
            [Name(ReportConstants.MaterialReleaseReceiptColumnName)]
            public string MaterialReleaseReceipt { get; set; }
            [Name(ReportConstants.FreightBillIssueColumnName)]
            public string FreightBillIssue { get; set; }
            [Name(ReportConstants.InvoiceNumberColumnName)]
            public string InvoiceNumber { get; set; }
            [Name(ReportConstants.TaxCodeColumnName)]
            public string TaxCode { get; set; }
            [Name(ReportConstants.ContractorIdColumnName)]
            public string ContractorId { get; set; }
            [Name(ReportConstants.AddressIdColumnName)]
            public string AddressId { get; set; }
            [Name(ReportConstants.ContractorNameColumnName)]
            public string ContractorName { get; set; }
            [Name(ReportConstants.ShipmentCountryColumnName)]
            public string ShipmentCountry { get; set; }

            public static explicit operator Shipment(ShipmentEntity entity) =>
                entity != null
                    ? new Shipment
                        {
                            Issue = entity.Issue,
                            MaterialReleaseReceipt = entity.MaterialReleaseReceipt,
                            FreightBillIssue = entity.FreightBillIssue,
                            InvoiceNumber = entity.InvoiceNumber,
                            TaxCode = entity.TaxCode,
                            ContractorId = entity.ContractorId,
                            AddressId = entity.AddressId,
                            ContractorName = entity.ContractorName,
                            ShipmentCountry = entity.ShipmentCountry
                        }
                    : null;
        }
    }

The code looks properly, but the behavior of the class is quite strange. In most cases, the generation runs properly, but few times i have noticed a situation, when the MemoryStream object contains no data, even if shipment collection is correct. I believe, such a behavior does not depend on data passed as a parameter. Probably i've made something wrong with the streams. How to use them properly? How to generate csv file correctly using CsvHelper library?

1 Answer 1

1

I've found a solution. StreamWriter has to be flushed, after writing records, so now the function looks like:

        public async Task<Report> GenerateReportAsync(ICollection<ShipmentEntity> shipmentEntities)
        {
            var shipment = shipmentEntities
                .Select(s => (Shipment) s)
                .ToList();

            await using var memoryStream = new MemoryStream();
            await using var streamWriter = new StreamWriter(memoryStream);
            await using var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture);
            
            csvWriter.Configuration.Delimiter = ";";
            
            await csvWriter.WriteRecordsAsync(shipment);
            await streamWriter.FlushAsync();

            var report = new Report
            {
                Content = memoryStream.ToArray(),
                Type = ReportConstants.CsvFileType,
                Name = ReportConstants.CsvReportFileName
            };

            return report;
        }

And it works properly :)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.