Files
sbroenne__mcp-server-excel/specs/TABLE-API-SPECIFICATION.md
T
Stefan Broenner 6b3a557870 refactor: Migrate entire codebase to exception-based pattern (void mutations, no OperationResult suppression) (#233)
* Refactor MCP tools to share error handling

* Refactor Core Commands to remove exception suppression

Per CRITICAL-RULES.md Rule 1b:
- Removed catch blocks that suppress exceptions by returning OperationResult { Success = false }
- Let exceptions propagate naturally through batch.Execute()
- Kept finally blocks for COM resource cleanup
- Removed orphaned try blocks from methods with no COM cleanup needs

Modified files:
- RangeCommands.AutoFit.cs (2 methods)
- RangeCommands.Advanced.cs (5 methods)
- RangeCommands.Validation.cs (3 methods)
- RangeCommands.Formatting.cs (3 methods)
- PivotTableCommands.GrandTotals.cs (1 method)
- OlapPivotTableFieldStrategy.cs (2 methods)
- RegularPivotTableFieldStrategy.cs (1 method)

Architecture: batch.Execute() ALREADY captures exceptions via TaskCompletionSource.
Inner try-catch suppresses exceptions and loses stack context.
Exception propagation now happens at correct layer (batch, not method).

* Update copilot-instructions: Add surgical testing guidance and exception propagation pattern

CRITICAL additions:
- Integration tests take 45+ MINUTES for full suite
- ALWAYS use surgical testing with Feature filters (2-5 min per feature)
- Core Commands NEVER wrap batch.Execute() in catch blocks
- Let exceptions propagate naturally - batch.Execute() handles via TaskCompletionSource
- ONLY use finally blocks for COM cleanup, NO catch blocks returning OperationResult

This prevents 45+ minute test runs and documents correct exception handling pattern per CRITICAL-RULES.md Rule 1b.

* Remove additional violating catch blocks from PivotTable commands

Found and removed 7 more catch blocks that violated Rule 1b:
- PivotTableCommands.Subtotals.SetSubtotals
- PivotTableCommands.Grouping.GroupByDate
- PivotTableCommands.Grouping.GroupByNumeric
- PivotTableCommands.CalculatedFields.CreateCalculatedField
- PivotTableCommands.Analysis.GetData
- PivotTableCommands.Analysis.SetFieldFilter
- PivotTableCommands.Analysis.SortField

All were inside batch.Execute() and returning OperationResult { Success = false }.
Removed catch blocks, kept finally blocks for COM cleanup.
Exceptions now propagate naturally through batch.Execute().

* refactor(core): remove violating catch block from TableCommands.DataModel.cs

Removed catch block that violated Rule 1b (CRITICAL-RULES.md).
AddToDataModel now lets exceptions propagate through batch.Execute().

Pattern removed:
catch (Exception ex) {
    return new OperationResult { Success = false, ErrorMessage = ... };
}

Rationale:
- batch.Execute() captures exceptions via TaskCompletionSource
- Inner catch suppresses exceptions and loses stack context
- Exceptions should originate from batch layer, not method layer

Part of comprehensive audit removing all violating catch blocks from Core Commands.

* test(core): fix Range style tests to expect exceptions after architectural changes

Updated 2 tests to expect exceptions instead of OperationResult { Success = false }:
- SetStyle_InvalidStyleName_ThrowsException (was ReturnsError)
- GetStyle_InvalidRange_ThrowsException (was ReturnsError)

Why Changed:
After removing violating catch blocks (Rule 1b), SetStyle() and GetStyle()
no longer catch Excel COM exceptions. batch.Execute() captures exceptions via
TaskCompletionSource and re-throws them.

Pattern:
Methods WITHOUT pre-validation → Excel COM throws → batch.Execute() captures →
Exception propagates to caller → Test must expect exception

Verified Clean:
- Tests with pre-validation (Sheet.Move, NamedRange.CreateBulk) still expect
  Success=false correctly
- Tests with business rules (OLAP calculated fields) correctly expect Success=false
- Only methods letting Excel COM throw directly needed test updates

All 2 tests now pass with architectural fix in place.

* refactor: remove async wrappers and bulk operations

Remove ViewAsync, CreateBulk methods and update documentation

* refactor(chart): Convert Chart commands to exception-based pattern

- IChartCommands: Convert 14 method signatures (OperationResult/ChartSeriesResult/ChartListResult → void/SeriesInfo/List<ChartInfo>)
- IChartStrategy: Convert 3 data operation signatures (SetSourceRange→void, AddSeries→SeriesInfo, RemoveSeries→void)
- RegularChartStrategy: Remove OperationResult wrappers, return data types directly
- PivotChartStrategy: Convert error returns to throw NotSupportedException with helpful messages
- ChartCommands implementations: Update DataSource/Lifecycle/Appearance to match new signatures
- Remove unnecessary Models using directives

CRUD ops return void (throw on error), Query ops return data types, Exceptions replace Success/ErrorMessage wrappers.
Pattern validated: Core commands build successfully with 0 errors/warnings.

* fix(chart): Fix COM cleanup in PivotChartStrategy to use finally blocks

- SetSourceRange: Move COM Release to finally block ensuring cleanup even if exception occurs
- AddSeries: Move COM Release to finally block ensuring cleanup even if exception occurs
- RemoveSeries: Move COM Release to finally block ensuring cleanup even if exception occurs
- Declare COM objects as nullable dynamic? variables before try-catch-finally

Prevents COM object leaks when exceptions occur during PivotTable name retrieval.
Finally blocks guarantee cleanup happens regardless of try/catch outcome.
Follows proper COM interop resource management pattern from excel-com-interop.instructions.md.

* refactor(chart): Remove empty catch blocks from PivotChartStrategy

- GetInfo: Remove empty catch blocks, keep only try-finally for COM cleanup
- GetDetailedInfo: Remove empty catch block, keep only try-finally for COM cleanup

Empty catch blocks are unnecessary when finally blocks handle cleanup.
Finally blocks execute regardless of exception occurrence.
Cleaner code pattern: try { operations } finally { cleanup }

* refactor(chart): Fix COM cleanup in RegularChartStrategy

- GetInfo: Move seriesCollection to try-finally without empty catch
- GetDetailedInfo: Move seriesCollection to try-finally, keep only justified catch blocks for optional properties
- Remove COM Release from try block, move to finally block
- Keep catch blocks only where fallback values make sense (HasLegend=false, etc.)

Ensures COM objects always released even if exceptions occur.
Empty catch blocks removed where no fallback action needed.

* refactor(chart): Simplify PivotChartStrategy exception messages

- SetSourceRange: Remove COM object lookup, just throw NotSupportedException
- AddSeries: Remove COM object lookup, just throw NotSupportedException
- RemoveSeries: Remove COM object lookup, just throw NotSupportedException

LLM already knows context - no need for complex COM operations to get PivotTable name.
Simpler, cleaner, no COM cleanup needed for error-only code paths.

* refactor(chart): Convert CLI ChartCommand to exception-based pattern

- Removed Result wrapper pattern from all Execute methods
- Added try-catch exception handling with proper exit codes (0=success, 1=error)
- Query operations: return data via WriteJson
- CRUD operations: void with WriteInfo success messages
- Removed WriteResult helper method (no longer needed)
- Removed unused Models using statement
- All 14 Execute methods converted:
  * ExecuteList (List<ChartInfo>)
  * ExecuteRead (ChartInfoResult)
  * ExecuteCreateFromRange (ChartCreateResult)
  * ExecuteCreateFromPivotTable (ChartCreateResult)
  * ExecuteDelete (void)
  * ExecuteMove (void)
  * ExecuteSetSourceRange (void)
  * ExecuteAddSeries (SeriesInfo)
  * ExecuteRemoveSeries (void)
  * ExecuteSetChartType (void)
  * ExecuteSetTitle (void)
  * ExecuteSetAxisTitle (void)
  * ExecuteShowLegend (void)
  * ExecuteSetStyle (void)

Part of Phase 2.2: Chart CLI Updates

* refactor(chart): Convert MCP ExcelChartTool to exception-based pattern

- Removed Result wrapper anonymous object serialization
- Query operations: serialize data types directly (List<ChartInfo>, ChartInfoResult, ChartCreateResult, SeriesInfo)
- CRUD operations (void): return success message JSON after completion
- Exception handling: ExecuteToolAction wrapper catches and serializes exceptions
- All 14 actions converted:
  * List → serialize List<ChartInfo>
  * Read → serialize ChartInfoResult
  * CreateFromRange → serialize ChartCreateResult
  * CreateFromPivotTable → serialize ChartCreateResult
  * Delete → success message JSON
  * Move → success message JSON
  * SetSourceRange → success message JSON
  * AddSeries → serialize SeriesInfo
  * RemoveSeries → success message JSON
  * SetChartType → success message JSON
  * SetTitle → success message JSON
  * SetAxisTitle → success message JSON
  * ShowLegend → success message JSON
  * SetStyle → success message JSON

Part of Phase 2.3: Chart MCP Tool Updates

* refactor(chart): Convert MCP ExcelChartTool to exception-based pattern

- Removed Result wrapper anonymous object serialization
- Query operations: serialize data types directly (List<ChartInfo>, ChartInfoResult, ChartCreateResult, SeriesInfo)
- CRUD operations (void): return success message JSON after completion
- Exception handling: ExecuteToolAction wrapper catches and serializes exceptions
- All 14 actions converted:
  * List → serialize List<ChartInfo>
  * Read → serialize ChartInfoResult
  * CreateFromRange → serialize ChartCreateResult
  * CreateFromPivotTable → serialize ChartCreateResult
  * Delete → success message JSON
  * Move → success message JSON
  * SetSourceRange → success message JSON
  * AddSeries → serialize SeriesInfo
  * RemoveSeries → success message JSON
  * SetChartType → success message JSON
  * SetTitle → success message JSON
  * SetAxisTitle → success message JSON
  * ShowLegend → success message JSON
  * SetStyle → success message JSON

Part of Phase 2.3: Chart MCP Tool Updates

* refactor(namedrange): Convert NamedRange commands to exception pattern (all layers)

Core Changes:
- INamedRangeCommands: List returns List<NamedRangeInfo>, Read returns NamedRangeValue, CRUD operations void
- Added NamedRangeValue class (POCO for Read operation)
- All methods throw exceptions on error (InvalidOperationException, ArgumentException)
- Parameter validation enforced (255 char limit, no duplicates)

CLI Changes:
- All Execute methods use try-catch with exit codes (0=success, 1=exception, -1=validation)
- List/Get serialize data directly via WriteJson
- Create/Update/Delete/Write use WriteInfo for success messages
- Removed WriteResult helper method
- Removed unused using directive

MCP Server Changes:
- List serializes List<NamedRangeInfo> directly
- Read serializes NamedRangeValue directly
- Write/Create/Update/Delete wrap void operations in lambda with dummy return
- All CRUD ops return success message JSON with operation-specific messages
- Delete includes workflowHint about #NAME? errors

Test Changes (12 tests converted, all passing):
- Lifecycle.cs: 4 tests - List accesses data directly, Create/Delete void with verification
- Validation.cs: 6 tests - Parameter validation with Assert.Throws for errors
- Values.cs: 3 tests - Write/Read void/data operations with verification

All layers build successfully with 0 errors, 0 warnings
All 12 NamedRange tests passing (100%)

Phase 3 (NamedRange) complete - 6 Core methods + 12 tests converted

* refactor(conditionalformat): Convert ConditionalFormatting commands to exception pattern

Core Changes:
- IConditionalFormattingCommands: AddRule and ClearRules return void
- All methods throw exceptions on error (InvalidOperationException, ArgumentException)
- Removed OperationResult wrappers, use dummy return 0 for batch.Execute
- Removed unused using directives

CLI Changes:
- ExecuteAddRule/ExecuteClearRules use try-catch with exit codes (0=success, 1=exception, -1=validation)
- WriteInfo for success messages
- Removed WriteResult helper method
- Removed unused using directive

MCP Server Changes:
- AddRule/ClearRules wrap void operations in lambda with dummy return
- Return success message JSON with operation-specific messages
- Keep workflowHints for formatting verification guidance

All layers build successfully with 0 errors, 0 warnings
No tests exist for ConditionalFormatting commands

Phase 4.1 (ConditionalFormatting) complete - 2 Core methods converted

* Refactor: Convert Connection, File, VBA subsystems from OperationResult to exception-throwing pattern

- Connection: IConnectionCommands methods now return void, throw on error
- File: CreateEmpty returns void, throws on error
- VBA: Trust, Import, Delete, Run operations throw exceptions
- CLI layer: Updated error handling for void operations
- MCP layer: Responses serialized as {success: true} for void operations
- Test infrastructure: Updated 14 test helper files to check exceptions

Mutation operations now follow .NET convention of throwing exceptions instead of returning error-wrapped results. Read operations continue returning typed results.

All three layers (Core, CLI, MCP) coordinated for consistent behavior.

* Merge branch 'feature/shared-tool-error-handling' of https://github.com/sbroenne/mcp-server-excel into feature/shared-tool-error-handling

* Refactor: Convert Sheet subsystem from OperationResult to exception-throwing pattern

Convert 12 Sheet mutation methods to void signatures across all layers:
- ISheetCommands: Create, Rename, Copy, Delete, Move, CopyToWorkbook, MoveToWorkbook, SetTabColor, ClearTabColor, SetVisibility, Show, Hide, VeryHide
- SheetCommands (Core): All implementations use batch.Execute() with return 0, exceptions propagate
- SheetCommand (CLI): All void operations wrapped in try-catch with success/error messages
- ExcelWorksheetTool (MCP): All 10 void methods use WithSession() + return 0 + JSON serialization

Production code compiles cleanly. MCP smoke test: PASSED. Tests require separate refactoring for void method assertions.

* Refactor: Convert Table subsystem from OperationResult to exception-throwing pattern (17 void methods, Core+CLI+MCP layers)

- ITableCommands: 17 mutation methods converted to void signatures with exception documentation
- Core layer: 17 implementations across 7 partial files use batch.Execute() return 0 pattern
- CLI layer: 10 void handlers use try-catch-serialize pattern with success/error responses
- MCP layer: 10 void tool methods use try-catch-JSON pattern (fixed 8 broken WithSession<T> calls)
- All production layers compile cleanly: 0 errors, 0 warnings
- MCP smoke test PASSED: all 11 tools working correctly
- Test layer errors (44) deferred: void assertion issues to be fixed separately
- Pattern matches Sheet/Connection/File/VBA subsystem conversions

* feat: Convert PowerQuery subsystem mutation methods to void pattern (Delete, Create, Update, LoadTo, RefreshAll)

* Convert DataModel subsystem to exception-throwing pattern

- Interface: 7 void mutation methods (DeleteMeasure, DeleteRelationship, Refresh (2 overloads), CreateMeasure, UpdateMeasure, CreateRelationship, UpdateRelationship)
- Core: Converted Write.cs (6 mutations) and Refresh.cs (2 methods) to void pattern with batch.Execute((ctx, ct) => { ...code...; return 0; })
- CLI: 7 handlers with try-catch-console JSON output
- MCP: 7 tool methods with try-catch-JSON error handling
- Removed unused Core.Models imports from Write.cs and Refresh.cs
- Build: 0 errors, 0 warnings across Core, CLI, MCP layers

Note: Test failures expected (44 in Core, 50+ in other test files due to void assertion issues) - these are deferred per testing strategy (Rule 14)

* Merge branch 'feature/shared-tool-error-handling' of https://github.com/sbroenne/mcp-server-excel into feature/shared-tool-error-handling

* fix: Complete void test migration - 84/84 errors fixed

Complete migration of 84 test compilation errors to exception-based pattern.
This completes the integration of void Execute infrastructure with test layer updates.

✅ BUILD VERIFIED: 0 errors, 0 warnings
✅ PRE-COMMIT PASSED: All quality gates passing

Test Migrations (84 total):
- Sheet tests: 19 errors (Move, TabColor, Visibility, Lifecycle)
- Helper fixtures: 4 errors (PowerQuery, Table, DataModel, PivotTable)
- PowerQueryCommandsTests: 19 errors (Create, Update, Delete, LoadTo calls)
- DataModelCommandsTests: 12 errors (CreateMeasure, UpdateMeasure, CreateRelationship, DeleteRelationship)
- DataModelTestsFixture: 1 error (var assignment from void CreateMeasure)
- TableCommandsTests: 2 errors (Create calls in tests and fixture)
- PivotTableCommandsTests: 3 errors (Create calls from Table/DataModel)
- PivotTableCommandsTests.OlapFields: 2 errors (CreateMeasure calls)

Pattern Applied (Two types):
1. CS0815 'Cannot assign void': Remove 'var result =' and 'Assert.True(result.Success)' lines
2. Remove unnecessary success checks - void methods throw on error instead

Infrastructure:
- void Execute(Action<ExcelContext, CancellationToken>) in IExcelBatch/ExcelBatch
- Delegates to Execute<T>(Func<...>) returning dummy 0
- Non-breaking enhancement for clean void method support

Verification:
✅ Build: 0 errors, 0 warnings
✅ Pre-commit checks: All passed
  - COM leak detection: 0 leaks
  - Success flag validation: All consistent
  - MCP implementation coverage: 100%
  - MCP server smoke test: Passed
✅ Feature branch active (feature/shared-tool-error-handling)

* docs: Add void test migration completion summary

* refactor: Convert 11 Range void mutations to fire-and-forget pattern across all layers

Complete refactoring of Range command mutations to void pattern:

CORE LAYER (11 void mutations):
- RangeCommands.Search.cs: Replace, Sort
- RangeCommands.Formatting.cs: SetStyle, FormatRange
- RangeCommands.Validation.cs: ValidateRange, RemoveValidation
- RangeCommands.AutoFit.cs: AutoFitColumns, AutoFitRows
- RangeCommands.Advanced.cs: MergeCells, UnmergeCells, SetCellLock

INTERFACE:
- IRangeCommands.cs: All 11 signatures changed from OperationResult to void

MCP TOOL LAYER:
- ExcelRangeTool.cs: All 11 calls updated to use WithSession<object?>() pattern
  * Explicit type parameter resolves CS0411 type inference on void returns
  * Returns { Success = true } JSON directly for all void mutations
  * Lambda formatting fixed (IDE0055 compliance)

CLI LAYER:
- RangeCommand.cs: All 11 command handlers adapted
  * Removed var result = ... assignments
  * Call void methods directly
  * Return { Success = true } JSON on completion

TEST LAYER (14+ files):
- RangeCommandsTests: All test files updated
  * Removed var result = ... assignments
  * Removed Assert.True(result.Success, ...) assertions
  * Void methods throw on failure; silence = success

BUILD STATUS: ✅ Clean - 0 errors, 0 warnings

LEGITIMATE OperationResult METHODS (NOT CONVERTED):
- SetValues, SetFormulas, Copy, SetNumberFormat, AddHyperlink
- These data mutations need validation and result reporting

PATTERN:
- Void mutations: Fire-and-forget with exception-based error handling
- Exceptions propagate naturally through batch.Execute() layer
- No OperationResult wrapping needed for simple write operations

* chore: Remove unused imports and temporary documentation files

- Clean up unused using directives across Table and PivotTable commands
- Remove temporary documentation files
- Maintain code style consistency

* fix: Update MartinCostello.Logging.XUnit package version to 0.7.0

* fix(chart): Suppress CodeQL false positives for dynamic COM interop in Strategy pattern

- Add pragma warning disable CS8604 for all Strategy method calls
- CodeQL cannot verify dynamic Excel COM objects have required methods at compile-time
- All methods verified to exist in both RegularChartStrategy and PivotChartStrategy
- False positives: SetSourceRange, AddSeries, RemoveSeries, GetInfo, GetDetailedInfo
- Addresses code scanning alerts #2631-2635
2025-11-23 17:32:04 +00:00

15 KiB

Excel Table (ListObject) API Specification

Comprehensive specification for Excel Table operations - reviewing current implementation and future refactoring needs

Executive Summary

This specification reviews the current TableCommands implementation to determine:

  1. What functionality already exists
  2. What overlaps with RangeCommands
  3. What should be refactored or removed
  4. What's missing that should be added

Key Questions to Answer

  1. Does TableCommands duplicate RangeCommands? - Data operations on tables
  2. What's the proper division of responsibilities? - Table structure vs data operations
  3. Should ReadDataAsync/AppendRowsAsync move to RangeCommands? - Data operations
  4. What table-specific features are missing? - Structured references, filters, slicers?

Current TableCommands Implementation

Interface Review (ITableCommands.cs)

Lifecycle Operations:

  • ✅ List - List all tables in workbook
  • ✅ Create - Create table from range with headers/style
  • ✅ Rename - Rename table
  • ✅ Delete - Delete table (convert back to range)
  • ✅ GetInfo - Get detailed table information

Structure Operations:

  • ✅ Resize - Resize table to new range
  • ✅ ToggleTotals - Show/hide totals row
  • ✅ SetColumnTotal - Set totals function for column
  • ✅ SetStyle - Change table style

Data Operations: ⚠️ POTENTIAL OVERLAP WITH RANGECOMMANDS

  • ✅ ReadData - Read table data
  • ✅ AppendRows - Append rows to table

Data Model Integration:

  • ✅ AddToDataModelAsync - Add table to Power Pivot

Excel Table (ListObject) Capabilities

What is an Excel Table?

Excel Tables (ListObject COM objects) are structured ranges with metadata:

  • Named references (e.g., "SalesTable")
  • Column headers with names
  • Automatic expansion when data added
  • Built-in filtering and sorting UI
  • Table styles and formatting
  • Totals row with aggregate functions
  • Structured references in formulas ([@ColumnName])
  • Can be added to Data Model for relationships

Excel COM API - ListObject Operations

1. Table Lifecycle

// Create table
dynamic listObjects = sheet.ListObjects;
dynamic table = listObjects.Add(
    SourceType: xlSrcRange,
    Source: sheet.Range["A1:D100"],
    XlListObjectHasHeaders: xlYes
);
table.Name = "SalesTable";

// Delete table (convert to range, preserve data)
table.Unlist();

// Delete table (remove everything)
table.Delete();

2. Table Properties

// Basic properties
string name = table.Name;
string range = table.Range.Address;
bool hasHeaders = table.ShowHeaders;
bool hasTotals = table.ShowTotals;
string style = table.TableStyle.Name;

// Row counts
int totalRows = table.Range.Rows.Count;  // Including header/totals
int dataRows = table.DataBodyRange?.Rows.Count ?? 0;  // Data only

// Column operations
int columnCount = table.ListColumns.Count;
dynamic column = table.ListColumns.Item(1);  // or by name
string columnName = column.Name;

3. Table Resize

// Resize table to new range
table.Resize(sheet.Range["A1:E200"]);

4. Totals Row

// Show/hide totals row
table.ShowTotals = true;

// Set totals function for column
dynamic column = table.ListColumns.Item("Amount");
column.TotalsCalculation = xlTotalsCalculationSum;  // Sum
column.TotalsCalculation = xlTotalsCalculationAverage;  // Average
column.TotalsCalculation = xlTotalsCalculationCount;  // Count
column.TotalsCalculation = xlTotalsCalculationMax;  // Max
column.TotalsCalculation = xlTotalsCalculationMin;  // Min
column.TotalsCalculation = xlTotalsCalculationStdDev;  // Std Dev
column.TotalsCalculation = xlTotalsCalculationVar;  // Variance
column.TotalsCalculation = xlTotalsCalculationCustom;  // Custom formula

5. Table Styles

// Built-in styles
table.TableStyle = workbook.TableStyles.Item("TableStyleMedium2");

// Or by name
table.TableStyle = "TableStyleLight9";

6. AutoFilter (Filtering)

// Tables automatically have AutoFilter
dynamic autoFilter = table.AutoFilter;

// Apply filter to column
autoFilter.Range.AutoFilter(
    Field: 2,  // Column index (1-based)
    Criteria1: "USA",
    Operator: xlFilterValues
);

// Clear filters
autoFilter.ShowAllData();

// Check if filtered
bool isFiltered = table.ShowAutoFilter;

7. Data Operations

// Read data (values only, no headers)
dynamic dataBodyRange = table.DataBodyRange;
object[,] values = dataBodyRange?.Value2;

// Read entire table (including headers)
object[,] allData = table.Range.Value2;

// Append row (table auto-expands)
dynamic newRow = table.ListRows.Add();
newRow.Range.Value2 = new object[,] { { val1, val2, val3 } };

// Insert row at position
dynamic insertedRow = table.ListRows.Add(Position: 5);

8. Data Model Integration

// Add to Data Model (Power Pivot)
table.TableObject = table;  // Make it a "proper" table
// Then use Power Pivot to add to model

Overlap Analysis: TableCommands vs RangeCommands

Current Overlap

Operation TableCommands RangeCommands Verdict
Read data ReadDataAsync GetValuesAsync ⚠️ OVERLAP - RangeCommands can read table ranges
Write data AppendRowsAsync SetValuesAsync ⚠️ OVERLAP - RangeCommands can write to table ranges
Clear data ❌ Not implemented ClearContentsAsync ✅ RangeCommands handles this
Format cells ❌ Not implemented SetNumberFormatAsync, SetFontAsync, etc. ✅ RangeCommands handles this

Key Insight: Tables ARE Ranges with Metadata

Excel Tables are fundamentally ranges with additional structure:

  • Underlying cells = regular range
  • Table structure = metadata layer (headers, totals, style, filters)

Proposed Division:

  • TableCommands = Table structure and metadata (lifecycle, totals, filters, styles)
  • RangeCommands = Data operations on any range (including table data ranges)

Proposed Refactoring Strategy

Option 1: Remove Data Operations from TableCommands

Remove from TableCommands:

  • ❌ ReadDataAsync → Use RangeCommands.GetValuesAsync(batch, sheetName, "TableName[#Data]")
  • ❌ AppendRowsAsync → Use RangeCommands.SetValuesAsync + ResizeAsync

Keep in TableCommands:

  • ✅ All lifecycle operations (List, Create, Rename, Delete, GetInfo)
  • ✅ All structure operations (Resize, ToggleTotals, SetColumnTotal, SetStyle)
  • ✅ Data Model integration (AddToDataModel)
  • ✅ Filter operations (if added)

Benefits:

  • Clear separation: Table structure vs data operations
  • Users learn ONE API for data (RangeCommands)
  • TableCommands focuses on table-specific features

Challenges:

  • Users need to know table structured references (TableName[#Data])
  • Auto-expansion on append requires manual resize

Option 2: Keep Data Operations but Delegate to RangeCommands Internally

Keep current interface:

  • ✅ ReadDataAsync - Internally calls RangeCommands
  • ✅ AppendRowsAsync - Internally calls RangeCommands + auto-resize

Benefits:

  • User-friendly API (no need to know structured references)
  • Auto-expansion handled automatically
  • Backwards compatible

Challenges:

  • Duplication of functionality
  • Two ways to do the same thing

TableCommands focuses on table-specific operations:

  • ✅ Lifecycle: List, Create, Rename, Delete, GetInfo
  • ✅ Structure: Resize, ToggleTotals, SetColumnTotal, SetStyle
  • ✅ Table-specific data: AppendRows (auto-expansion feature)
  • ✅ Filters: Apply, clear, get filter state
  • ✅ Data Model: AddToDataModel
  • ❌ Remove: ReadData - Use RangeCommands instead

Rationale:

  • AppendRows has table-specific behavior (auto-expansion) - KEEP
  • ReadData is just range read with no table-specific logic - REMOVE
  • Filters are table-specific (AutoFilter object) - ADD
  • Data operations (format, copy, etc.) - Use RangeCommands

Missing Table Features

1. Filter Operations ⭐ HIGH PRIORITY

// Apply filter to column
Task<OperationResult> ApplyFilterAsync(IExcelBatch batch, string tableName, string columnName, string criteria);

// Apply multiple criteria filter
Task<OperationResult> ApplyFilterAsync(IExcelBatch batch, string tableName, string columnName, List<string> values);

// Clear filters
Task<OperationResult> ClearFiltersAsync(IExcelBatch batch, string tableName);

// Get filter state
Task<TableFilterResult> GetFiltersAsync(IExcelBatch batch, string tableName);

Excel COM:

dynamic autoFilter = table.AutoFilter;
autoFilter.Range.AutoFilter(Field: 2, Criteria1: "USA");
autoFilter.ShowAllData();  // Clear all filters

2. Structured Reference Support ⭐ MEDIUM PRIORITY

// Get structured reference for table regions
Task<OperationResult> GetStructuredReferenceAsync(IExcelBatch batch, string tableName, TableRegion region);

public enum TableRegion
{
    All,        // TableName[#All]
    Data,       // TableName[#Data]
    Headers,    // TableName[#Headers]
    Totals,     // TableName[#Totals]
    ThisRow     // TableName[@]
}

3. Column Operations ⭐ MEDIUM PRIORITY

// Add column to table
Task<OperationResult> AddColumnAsync(IExcelBatch batch, string tableName, string columnName, int? position = null);

// Remove column from table
Task<OperationResult> RemoveColumnAsync(IExcelBatch batch, string tableName, string columnName);

// Rename column
Task<OperationResult> RenameColumnAsync(IExcelBatch batch, string tableName, string oldName, string newName);

Excel COM:

dynamic newColumn = table.ListColumns.Add(Position: 3);
newColumn.Name = "NewColumn";
table.ListColumns.Item("OldColumn").Delete();

4. Sort Operations ⭐ LOW PRIORITY (RangeCommands has Sort)

Tables can use standard Range.Sort(), so RangeCommands.SortAsync works on table ranges.

5. Data Validation on Columns ⭐ LOW PRIORITY (RangeCommands has Validation)

RangeCommands validation operations work on table column ranges.

6. Slicers ⭐ FUTURE ENHANCEMENT

// Add slicer for table column
Task<OperationResult> AddSlicerAsync(IExcelBatch batch, string tableName, string columnName);

Slicers are complex UI objects - defer to future phase.


Phase 1: Remove Duplication (THIS PHASE)

Remove from TableCommands:

  1. ❌ ReadDataAsync - Users should use RangeCommands.GetValuesAsync(batch, sheetName, "TableName[#Data]")
    • Document migration: "Use RangeCommands to read table data"
    • Provide examples in docs

Keep in TableCommands: 2. ✅ AppendRowsAsync - Table-specific auto-expansion behavior

  • This is unique to tables (auto-resize when data added)
  • Cannot be easily replicated with RangeCommands alone

Update Documentation: 3. Document that RangeCommands works with table ranges 4. Provide examples of table structured references

Phase 2: Add Missing Features (FUTURE)

Filter Operations:

  1. ApplyFilterAsync - Apply filter to column
  2. ClearFiltersAsync - Clear all filters
  3. GetFiltersAsync - Get current filter state

Column Operations: 4. AddColumnAsync - Add column to table 5. RemoveColumnAsync - Remove column 6. RenameColumnAsync - Rename column

Structured References: 7. GetStructuredReferenceAsync - Get range address for table regions


Implementation Details

Current TableCommands Methods Review

✅ KEEP - Table Lifecycle

  • ListAsync - List all tables
  • CreateAsync - Create table from range
  • RenameAsync - Rename table
  • DeleteAsync - Delete table
  • GetInfoAsync - Get table details

✅ KEEP - Table Structure

  • ResizeAsync - Resize table
  • ToggleTotalsAsync - Show/hide totals row
  • SetColumnTotalAsync - Set totals function
  • SetStyleAsync - Change table style

✅ KEEP - Table-Specific Data

  • AppendRowsAsync - Append with auto-expansion

✅ KEEP - Data Model

  • AddToDataModelAsync - Add to Power Pivot

❌ REMOVE - Data Operations (Use RangeCommands)

  • ReadDataAsync - Duplicate of RangeCommands.GetValuesAsync

Migration Guide for Users

Before (TableCommands.ReadDataAsync)

var result = await tableCommands.ReadDataAsync(batch, "SalesTable");
List<List<object?>> data = result.Data;

After (RangeCommands.GetValuesAsync)

// Option 1: Read data only (no headers)
var result = await rangeCommands.GetValuesAsync(batch, "Sales", "SalesTable[#Data]");
List<List<object?>> data = result.Values;

// Option 2: Read everything (headers + data)
var result = await rangeCommands.GetValuesAsync(batch, "Sales", "SalesTable[#All]");

// Option 3: If you don't know the sheet name
var tableInfo = await tableCommands.GetInfoAsync(batch, "SalesTable");
var result = await rangeCommands.GetValuesAsync(batch, tableInfo.SheetName, "SalesTable[#Data]");

Table Structured References

Excel Tables support structured references:

  • TableName[#All] - Entire table including headers and totals
  • TableName[#Data] - Data rows only (no headers or totals)
  • TableName[#Headers] - Header row only
  • TableName[#Totals] - Totals row only
  • TableName[[ColumnName]] - Specific column
  • TableName[@] - Current row (in formulas)

Summary

Current State

  • TableCommands has 12 operations
  • 1 operation (ReadDataAsync) duplicates RangeCommands
  • 1 operation (AppendRowsAsync) has table-specific behavior worth keeping
  • Missing important table features: filters, column management

Proposed Changes

Phase 1 - Remove Duplication:

  1. ❌ Delete ReadDataAsync - Use RangeCommands instead
  2. ✅ Keep AppendRowsAsync - Table-specific auto-expansion
  3. 📝 Update documentation with migration guide and structured reference examples

Phase 2 - Add Missing Features (Future): 4. Add filter operations (Apply, Clear, Get) 5. Add column operations (Add, Remove, Rename) 6. Add structured reference helper

Architecture Principle

TableCommands = Table structure and metadata

  • Lifecycle (create, rename, delete, list)
  • Structure (resize, totals, styles, columns)
  • Table-specific behaviors (auto-expansion on append, filters)
  • Data Model integration

RangeCommands = Data operations on any range

  • Read/write values and formulas
  • Formatting and styling
  • Copy, clear, insert, delete
  • Works on table ranges via structured references

This maintains clear separation of concerns and prevents duplication!


Open Questions for Review

  1. Should we remove ReadDataAsync in Phase 1?

    • Pro: Eliminates duplication, encourages unified API
    • Con: Breaking change, users need to learn structured references
  2. Should AppendRowsAsync accept 2D arrays instead of CSV?

    • Pro: Consistent with RangeCommands (no CSV in Core/MCP)
    • Con: Breaking change
  3. Should we add filter operations in Phase 1 or Phase 2?

    • Filters are common table operations
    • But adds scope to refactoring
  4. Should we support table slicers?

    • Complex UI objects
    • Defer to future phase?

Next Step: Review this specification and decide on Phase 1 scope before implementation!