A comprehensive C# demonstration project showcasing Vertical CSV format advantages over traditional horizontal CSV, featuring async streaming, schema evolution, and robust parsing capabilities.
git clone <repository-url>
cd vertical-cs
dotnet runTraditional Horizontal CSV:
Name,Age,Email,Phone,Department,Skills[0],Skills[1],Address.Street,Address.City
John,30,john@email.com,555-1234,Engineering,C#,Python,123 Main St,New York
Jane,25,jane@email.com,555-5678,Marketing,Design,Analytics,456 Oak Ave,Los AngelesVertical CSV (Better UX):
Name,John,Jane
Age,30,25
Email,john@email.com,jane@email.com
Phone,555-1234,555-5678
Department,Engineering,Marketing
Skills[0],C#,Design
Skills[1],Python,Analytics
Address.Street,123 Main St,456 Oak Ave
Address.City,New York,Los Angeles- Field names clearly visible on the left column
- No horizontal scrolling required
- Easy to scan and understand data structure
- Perfect for configuration-like data
- New fields easily visible when added
- Missing fields obvious in vertical layout
- Schema changes more transparent
- Backward compatibility easier to manage
- Arrays:
Skills[0],Skills[1],Languages[0] - Nested Objects:
Address.Street,Address.City,Address.State - Variable Array Lengths: Each record can have different array sizes
- Flattened Structure: Complex relationships in simple CSV format
// Async streaming
await foreach (var record in parser.ParseVerticalCsvFromFileAsync(filePath, schema, cancellationToken))
{
// Process records one at a time - constant memory usage
ProcessRecord(record);
}- IAsyncEnumerable for lazy evaluation
- Streaming parser - no memory limits
- Memory-mapped files for files >100MB
- Constant memory usage regardless of file size
- No OutOfMemoryException risk
- Cancellation token support for responsive applications
- ConfigureAwait(false) for library usage
- Robust error handling with proper async patterns
- Thread pool efficiency for concurrent operations
RequiredFields: Name, Age, EmailRequiredFields: Name, Age, Email
OptionalFields: PhoneRequiredFields: Name, Age, Email
OptionalFields: Phone, Department, StartDateRequiredFields: Name, Age, Email
OptionalFields: Phone, Department, StartDate, Notes, Address.Street, Address.City...
OptionalFieldPatterns: Skills[\d+], Languages[\d+], Projects[\d+].Name...Handles complex CSV data including:
- β
Quoted fields with commas:
"Smith, John" - β
Embedded quotes:
"John ""The Expert"" Doe" - β Multi-line content: Notes with line breaks
- β
Mixed line endings:
\r\n,\n - β Special characters: Apostrophes, Unicode
- β Excel compatibility: Handles Excel CSV exports
VerticalCsv/
βββ Models/
β βββ PersonRecord.cs # Data model with V1-V4 schemas
β βββ Address.cs # Nested object model
β βββ Project.cs # Complex object model
βββ Services/
β βββ CsvParser.cs # Async CSV parser
βββ SampleData/ # Example CSV files
β βββ vertical_v1.csv # Basic vertical format
β βββ horizontal_v1.csv # Traditional format
β βββ vertical_v4_flattened.csv # Complex data
β βββ vertical_problematic.csv # Edge cases
βββ TestDataGenerator.cs # Large file generator
βββ Program.cs # Demo application
βββ README.md # This file
var parser = new CsvParser();
// Parse vertical CSV with V4 schema
await foreach (var record in parser.ParseVerticalCsvFromFileAsync("data.csv", SchemaVersion.V4))
{
Console.WriteLine($"{record.Name}: {record.Skills.Count} skills");
}// Handles files >100MB with memory-mapped files automatically
var largeFileRecords = parser.ParseVerticalCsvFromFileAsync("large-file.csv", SchemaVersion.V4);
await foreach (var record in largeFileRecords)
{
// Constant memory usage - can process unlimited file sizes
await ProcessRecordAsync(record);
}// V4 data parsed with V1 schema - ignores complex fields gracefully
var records = parser.ParseVerticalCsvFromFileAsync("v4-data.csv", SchemaVersion.V1);
await foreach (var record in records)
{
// Only basic fields (Name, Age, Email) will be populated
Console.WriteLine($"{record.Name} - {record.Email}");
}Run dotnet run to see demonstrations of:
- Horizontal vs Vertical Comparison - Side-by-side format comparison
- Schema Evolution - V1βV4 progression with same application
- Complex Data Structures - Arrays, nested objects, variable lengths
- Robust Parsing - Problematic CSV data handling
- Streaming - Memory-efficient large file processing
- Async Performance - Cancellation tokens and scalability
- No dependencies - No external dependencies
| Feature | Traditional Parser | Vertical CSV Parser |
|---|---|---|
| Memory Usage | O(file size) | O(1) constant |
| Large Files | OutOfMemoryException | Unlimited size |
| Processing | Synchronous blocking | Async non-blocking |
| Scalability | Poor under load | Excellent |
| Cancellation | Not supported | Full support |
| Schema Evolution | Difficult | Natural |
- Configuration data with many optional fields
- Schema evolution requirements
- Complex data structures (arrays, nested objects)
- Human-readable data files
- Variable field counts per record
- Data with wide schemas (many columns)
- Simple tabular data with fixed schema
- Existing systems expecting horizontal format
- High-frequency trading data (performance critical)
- Database exports in standard format
- No code injection vulnerabilities
- Proper input validation and sanitization
- Safe parsing of untrusted CSV files
- Memory safety with bounded allocations
- Exception safety with proper async patterns
// Custom buffer size for performance tuning
parser.ParseVerticalCsvAsync(stream, schema, bufferSize: 16384);// Files >100MB automatically use memory-mapped files
// Configurable in CsvParser constructor if neededThis project is a demonstration of C# patterns and CSV parsing techniques. Use as reference for your own implementations.
Feel free to use the code as reference for your own CSV parsing implementations.
π‘ Key Takeaway: Vertical CSV format provides significantly better UX for complex data structures, schema evolution, and human readability while maintaining performance and scalability.