Fix "That Wasn't Supposed to Happen" on grid Excel export (Group Attendance, Content Channel Item List) - #9
Merged
Conversation
…aObject-backed grids Rock 16 made AttendanceListOccurrence inherit LavaDataObject (upstream ecef40f, issue SparkDevNetwork#5917), whose public this[string key] indexer reflects as a property named Item. The grid's DataSource-mode Excel export reads every property with prop.GetValue( item, null ) and throws TargetParameterCountException on the indexer, failing the export on every group (ExceptionLog 1573206-1579801, 2026-08-18). FilterDynamicObjectPropertiesCollection already strips LavaDataObject base properties, but only on the Fluid branch; our RockLiquid (DotLiquid) engine setting takes the branch that never did. The v16 line ended (1.16.13.1) with the gap still present; v17 removed RockLiquid entirely, which is why this is unreported upstream. - Grid.cs: skip indexer properties in the export property filter - Grid.cs: strip LavaDataObject base properties on the RockLiquid branch, mirroring the Fluid branch (also repairs Merge Template and Launch Workflow via GetEntitySetFromGridSourceList) - LavaField.cs: exclude indexer properties when building the custom Lava column property dictionary, which otherwise throws at render for any custom column on a LavaDataObject-backed grid
There was a problem hiding this comment.
Pull request overview
Fixes reflection failures when grids process LavaDataObject indexers during Excel export, merge actions, workflows, and custom Lava rendering.
Changes:
- Excludes indexed properties from Excel export and Lava field reflection.
- Aligns RockLiquid property filtering with Fluid behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
Rock/Web/UI/Controls/Grid/Grid.cs |
Filters indexers and inherited LavaDataObject properties. |
Rock/Web/UI/Controls/Grid/LavaField.cs |
Prevents indexer reads in custom Lava columns. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
stphnlee
self-requested a review
August 21, 2026 19:34
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the
That Wasn't Supposed to Happenerror page when exporting Group Attendance to Excel.Problem
Exporting the Group Attendance List grid to Excel fails 100% of the time — every group, every user, every date range, all browsers. Six identical entries in the v16
ExceptionLog(Ids 1573206–1579801, 2026-08-18, page 363 "Group Attendance"):Grid.cs:2705isobject propValue = prop.GetValue( item, null );.Root cause
Upstream commit
ecef40fd9b("(Group) Fixed the Group Attendance List block to correctly display custom columns", #5917) changed the grid's row type:LavaDataObjectexposes a public indexer,public object this[string key].Type.GetProperties()returns it as a property namedItemwhose getter is non-virtual, so it survives the export's existing property filters. Reading it withGetValue( item, null )— no index argument — throws.Grid.FilterDynamicObjectPropertiesCollectionexists precisely to strip base-class plumbing properties like this, but only the Fluid branch handlesLavaDataObject:Our
core_LavaEngine_LiquidFrameworkglobal attribute isDotLiquid, soRockLiquidIsEnabled == trueand the cleanup never runs. Sites on Fluid never hit this, which is why it is unreported upstream.No upstream fix exists to cherry-pick. Verified against
1.16.13.1(final v16 release) — the gap is still present. Upstreamdevelopresolved it incidentally in v17 by deleting RockLiquid support, which removed the branch entirely.Changes
Two files, +20 −1.
1.
Grid.cs— skip indexer properties in the export property filter (~2447)Added as a third guard alongside the existing virtual-property and
Data_Lava_guards.GetIndexParameters()returns an empty array for ordinary properties, so this is a no-op for every other grid. Covers all three reflection reads that consumeprops.2.
Grid.cs— stripLavaDataObjectbase properties on the RockLiquid path (~2760)Copied in shape from the
RockDynamicbranch immediately above. Brings the RockLiquid branch to parity with the Fluid branch, and matches the end state upstream reached in v17.This is the load-bearing hunk.
FilterDynamicObjectPropertiesCollectionhas two callers — the Excel export andGetEntitySetFromGridSourceList, which backs Merge Template and Launch Workflow. Hunk 1 only sits in the export loop, so hunk 2 is the only one that repairs the other grid actions.3.
LavaField.cs— exclude indexers from the custom Lava column property dictionary (:246)PopulateDataItemPropertiesDictionaryfiltered only virtual getters, so it admitted theItemindexer and read it withGetValue( dataItem, null )at :226 — meaning any custom Lava column on aLavaDataObject-backed grid threw at page render, on both Lava engines. Same one-line guard. (This is the ironic sibling of SparkDevNetwork#5917: custom columns were the feature the inheritance change was meant to fix.)What this fixes
Groups/GroupAttendanceListEntityTypeId, button not renderedCms/ContentChannelItemListconfirmed = reproduced in the production log. traced = same code path to the same
GetValuecall, not yet clicked.Only these two v16 WebForms blocks bind a
LavaDataObject-derived list to aRock:Grid. No SECC plugin type derives fromLavaDataObjectorRockDynamic. Communicate is not rendered on either block (noPersonIdField).Behavior change
Hunk 2 removes an
AvailableKeysmerge field that previously appeared on the EntitySet/merge path for these grids. It is[LavaHidden], carries no meaningful data, and never existed on Fluid — this aligns the two engines. Otherwise the exported column set is identical, minus theItemcolumn that could never be read.Risk and rollback
LavaDataObject-derived data sources.LavaDataObject-derived row types in the fork — none declaresItemorAvailableKeys.Rock.dll.Forward compatibility (v17)
RockLiquidIsEnabledblock it lives in was deleted upstream in v17; on merge the deletion wins. A botched conflict resolution leaves an orphanedelse if, which is a compile error — loud, not silent.developstill lacks both indexer guards. Harmless; record in the fork customization inventory and revisit at the v17 upgrade.Testing
Not yet built or run — needs a Windows build of
Rock.dll. Static analysis against this branch plus the production exception log.QA checklist once built:
Groups/GroupAttendanceList→ Export to Excel downloads a file; spot-check row valuesGroups/GroupAttendanceList→ Merge Template proceeds to the merge pageCms/ContentChannelItemList→ Export to Excel, Merge Template, Launch WorkflowGroupAttendanceList; page renders and the column exportsGroups/GroupMemberListor any report grid) exports unchangedItemcolumn in exported outputDeliberately not in this PR
ExportSource="ColumnOutput"workaround inGroupAttendanceList.ascx— viable same-day file-drop unblock, but it has a different deploy/rollback path than a DLL and would muddy a revert. Ship separately if interim relief is wanted.NullReferenceExceptionatGrid.GetDataSourceObjectType(), null grid DataSource on export postback inorg.secc.SportsAndFitness/.../GuestCheckin.ascx.cs). Plugin-side, tracked separately.🤖 Generated with Claude Code