SpreadCheetah Documentation

repository·main·Indexed 20 days ago

https://github.com/sveinungf/spreadcheetah

A high-performance, low-allocation .NET library for generating Microsoft Excel (XLSX) files using a forward-only streaming approach. It features a C# Source Generator to map objects to spreadsheet rows and includes analyzer rules to ensure correct usage of attributes and property mappings.

Tokens
1.9K
Snippets
4
Records
5
Agent score
22%

What's inside SpreadCheetah

  1. How SpreadCheetah works: Forward-only streaming

    main

    SpreadCheetah uses a forward-only design pattern to maintain a low memory footprint. This means you must construct your spreadsheet in a specific order:

    1. Worksheets: From left to right.
    2. Rows: From top to bottom.
    3. Cells: From left to right within a row.

    This streaming approach allows for high performance and low memory allocation, as the library does not need to keep the entire spreadsheet structure in memory.

  2. Use the Source Generator to create rows from objects

    main

    SpreadCheetah includes a C# Source Generator that simplifies mapping objects (classes, records, or structs) to spreadsheet rows. This avoids manual cell mapping and uses pooled arrays internally for high performance.

    1. Define your data model

    Create a plain C# class with public getters. The order of properties determines the order of cells in the row.

    public class MyObject
    {
        public string Question { get; set; }
        public int Answer { get; set; }
    }

    2. Define the Row Context

    Create a partial class that inherits from WorksheetRowContext and decorate it with the [WorksheetRow] attribute, specifying your data type.

    using SpreadCheetah.SourceGeneration;
    
    namespace MyNamespace;
    
    [WorksheetRow(typeof(MyObject))]
    public partial class MyObjectRowContext : WorksheetRowContext;

    3. Add the object as a row

    Use the AddAsRowAsync method, passing your object and the generated context.

    await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream);
    await spreadsheet.StartWorksheetAsync("Sheet 1");
    
    var myObj = new MyObject
    {
        Question = "How many Rings of Power were there?",
        Answer = 20
    };
    
    // Use the generated context to map the object to a row
    await spreadsheet.AddAsRowAsync(myObj, MyObjectRowContext.Default.MyObject);
    
    await spreadsheet.FinishAsync();

    Note: The Source Generator requires C# 12 or greater, but can be used with all supported .NET versions including .NET Framework.

    // Example of the generated code pattern (internal implementation detail)
    // private static async ValueTask AddAsRowInternalAsync(Spreadsheet spreadsheet, MyObject obj, CancellationToken token)
    // {
    //     var cells = ArrayPool<DataCell>.Shared.Rent(2);
    //     try
    //     {
    //         cells[0] = new DataCell(obj.Question);
    //         cells[1] = new DataCell(obj.Answer);
    //         await spreadsheet.AddRowAsync(cells.AsMemory(0, 2), token).ConfigureAwait(false);
    //     }
    //     finally
    //     {
    //         ArrayPool<DataCell>.Shared.Return(cells, true);
    //     }
    // }
  3. Basic usage of SpreadCheetah

    main

    SpreadCheetah is designed for forward-only, streaming spreadsheet generation. You create a new spreadsheet from a stream, start a worksheet, and then add rows one by one.

    Important: You must call FinishAsync() before disposing of the spreadsheet object to ensure the XLSX file is properly finalized.

    using (var spreadsheet = await Spreadsheet.CreateNewAsync(stream))
    {
        // A spreadsheet must contain at least one worksheet.
        await spreadsheet.StartWorksheetAsync("Sheet 1");
    
        // Cells are inserted row by row.
        var row = new List<Cell>
        {
            new Cell("Answer to the ultimate question:"),
            new Cell(42)
        };
    
        // Rows are inserted from top to bottom.
        await spreadsheet.AddRowAsync(row);
    
        // Remember to call Finish before disposing.
        // This is important to properly finalize the XLSX file.
        await spreadsheet.FinishAsync();
    }
  4. Reference SpreadCheetah.SourceGenerator analyzer rules

    main

    The SpreadCheetah.SourceGenerator includes several analyzer rules that trigger during compilation to ensure correct usage of attributes and property mappings. These rules are categorized by severity (Error or Warning) and help identify configuration issues in your data models.

    | Rule ID | Category | Severity | Notes |
    |---------|----------|----------|-------|
    | SPCH1001 | SpreadCheetah.SourceGenerator | Warning  | NoPropertiesFound |
    | SPCH1002 | SpreadCheetah.SourceGenerator | Warning  | UnsupportedTypeForCellValue |
    | SPCH1003 | SpreadCheetah.SourceGenerator | Error | DuplicateColumnOrder |
    | SPCH1004 | SpreadCheetah.SourceGenerator | Error | UnsupportedPropertyForColumnHeader |
    | SPCH1005 | SpreadCheetah.SourceGenerator | Error | UnsupportedTypeForAttribute |
    | SPCH1006 | SpreadCheetah.SourceGenerator | Error | InvalidAttributeArgument |
    | SPCH1007 | SpreadCheetah.SourceGenerator | Error | AttributeTypeArgumentMustInherit |
    | SPCH1008 | SpreadCheetah.SourceGenerator | Error | AttributeCombinationNotSupported |
    | SPCH1009 | SpreadCheetah.SourceGenerator | Error | AttributeTypeArgumentMustHaveDefaultConstructor |
    | SPCH1010 | SpreadCheetah.SourceGenerator | Warning | MissingPropertyForColumnHeader |
    | SPCH1011 | SpreadCheetah.SourceGenerator | Error   | PropertyForColumnHeaderMustBePublic |
    | SPCH1012 | SpreadCheetah.SourceGenerator | Error | AttributeConflictingWithBaseClass |
    | SPCH1013 | SpreadCheetah.SourceGenerator | Warning | UseNewerCsharpVersion |