fix: template rendering corrupts workbooks that contain formulas (calcChain + $= cell serialization)#990
Conversation
Rendered '\$=' formulas were written as an un-namespaced <f> inside a t=\"inlineStr\" cell, and the regenerated calcChain.xml carried wrong cell references (or none, which is schema-invalid) - either defect makes Excel refuse the output. Fixes the formula-cell serialization, derives calcChain refs from the cell's own address, drops the chain when empty instead of writing an invalid part, and clears collected refs between sheets.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/MiniExcel/SaveByTemplate/ExcelOpenXmlTemplate.cs (1)
135-152: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winRemove calcChain package metadata when omitting the part.
When
_calcChainContentis empty, this dropsxl/calcChain.xml, but the copiedxl/_rels/workbook.xml.relsstill targets it and[Content_Types].xmlstill declares it. That leaves an invalid package relationship. Remove the calcChain relationship and its content-type override whenever no calcChain part is emitted; extendTemplateWithStaticFormula_DoesNotWriteStaleOrEmptyCalcChainto assert both are absent.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/MiniExcel/SaveByTemplate/ExcelOpenXmlTemplate.cs` around lines 135 - 152, Update the calcChain handling around _calcChainContent and CalcChainHelper.GenerateCalcChainSheet so that when no calcChain content is emitted, the corresponding calcChain relationship in workbook.xml.rels and its [Content_Types].xml override are also removed. Preserve generation of the part and metadata when content exists, and extend TemplateWithStaticFormula_DoesNotWriteStaleOrEmptyCalcChain to assert both metadata entries are absent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/MiniExcel/SaveByTemplate/ExcelOpenXmlTemplate.Impl.cs`:
- Around line 1018-1023: Update the formula-processing logic around celRef to
use the complete cell reference from c.GetAttribute("r") directly, preserving
the existing fallback to ExcelOpenXmlUtils.ConvertXyToCell only when the r
attribute is absent. Ensure calcChain entries use the rendered address rather
than the advanced newRowIndex, and add a regression case covering $= inside an
expanded enumerable row.
---
Outside diff comments:
In `@src/MiniExcel/SaveByTemplate/ExcelOpenXmlTemplate.cs`:
- Around line 135-152: Update the calcChain handling around _calcChainContent
and CalcChainHelper.GenerateCalcChainSheet so that when no calcChain content is
emitted, the corresponding calcChain relationship in workbook.xml.rels and its
[Content_Types].xml override are also removed. Preserve generation of the part
and metadata when content exists, and extend
TemplateWithStaticFormula_DoesNotWriteStaleOrEmptyCalcChain to assert both
metadata entries are absent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 2a81869c-b9cc-4d08-9002-2a8802662a70
📒 Files selected for processing (3)
src/MiniExcel/SaveByTemplate/ExcelOpenXmlTemplate.Impl.cssrc/MiniExcel/SaveByTemplate/ExcelOpenXmlTemplate.cstests/MiniExcelTests/SaveByTemplate/MiniExcelTemplateCalcChainTests.cs
|
@martin-vitous Thank you for your contribution, I'm reviewing it now. If you could please prepare a PR for the main branch it would be appreciated. |
Problem
Rendering a template that contains any formula produces a file that Excel refuses to open ("the file is corrupt and cannot be opened" — repair also fails). Two independent defects in
SaveAsByTemplate, isolated against Excel (Microsoft 365):1. Broken
calcChain.xmlafter renderingA template containing formulas carries
xl/calcChain.xml, whose<c>entries point at formula-cell addresses. Rendering anIEnumerableinserts rows and shifts those cells, so the chain is regenerated (#491) — but the regeneration is broken in three ways:ProcessFormulasderives the calcChain ref from the cell's position in the row's child list (ConvertXyToCell(ci + 1, rowIndex)). In a sparse row (e.g. a label in column A and a$=formula in column F, no cells between), the formula is child Query Support Sheet Xml c without r #2 and gets recorded as column B. Excel rejects a chain pointing at non-formula cells.$=cells are collected. When a template's formulas are all static Excel formulas (a footerVLOOKUP, a=E27*B27row total, …), the regenerated chain has zero entries — and acalcChainwith no<c>children is schema-invalid (ECMA-376), so Excel rejects the whole package._calcChainCellRefsis never cleared between sheets, so a multi-sheet template duplicates sheet 1's refs into sheet 2's entries; and one code path copied the template's original calcChain into the output verbatim, whose refs are stale the moment rows shift.2.
$=formulas serialize as invalid cellsProcessFormulascreates thefelement without a namespace prefix; it then relies on a defaultxmlnsdeclaration thatCleanXmlstrips, leaving<f>in no namespace. The cell also keeps itst="inlineStr"attribute while no longer containing an<is>child:Either defect alone is enough for Excel to refuse the file, so in 1.45.0 the template-formula feature and any formula-bearing template are unusable. Likely the root cause behind reports like #632/#857; reproduced identically on
2.0.0-preview.4.Fix
ExcelOpenXmlTemplate.Impl.cs—ProcessFormulas:felement with the document's prefix so it survivesCleanXml(<x:f>…</x:f>);tattribute (the cell no longer holds an inline string);rattribute instead of its child-list position (falls back to the old computation whenris absent).ExcelOpenXmlTemplate.cs—SaveAsByTemplate:_calcChainCellRefsafter each sheet;Validation
MiniExcelTemplateCalcChainTests) fail on current1.45.0sources and pass with the fix. Templates are authored in-test with ClosedXML (already a test dependency).MiniExcelTestssuite: 360/360 pass.VLOOKUP,$=SUM, and a real-world quote template (embedded images, custom XML parts, conditional formatting, dozens of static formulas). All rendered files that previously failed now open, and the formulas compute ($=SUMranges resolve to the expanded rows, per-row static formulas recalculate).Notes
[Content_Types]override still reference the absent part. Excel tolerates this; strict consumers (System.IO.Packaging) do not. Cleaning those two entries requires touching the entry-copy path — happy to add it to this PR if you prefer.2.0.0-previewline; I can port this fix there if wanted.Summary by CodeRabbit