Skip to content

fix(data): five defects in model convert that silently lose or corrupt output - #190

Draft
david-hudec-networg wants to merge 5 commits into
TALXIS:masterfrom
david-hudec-networg:fix/data-model-convert-loses-model-content
Draft

fix(data): five defects in model convert that silently lose or corrupt output#190
david-hudec-networg wants to merge 5 commits into
TALXIS:masterfrom
david-hudec-networg:fix/data-model-convert-loses-model-content

Conversation

@david-hudec-networg

@david-hudec-networg david-hudec-networg commented Aug 31, 2026

Copy link
Copy Markdown

Five defects in DataModelConverter, all silent: the output stays well-formed while losing or corrupting content, so nothing surfaces until you diff against the source declarations. Found across a product base and three customer solutions.

# Defect Fix
1 N:1 relationships deduped on (table, table), ignoring the column — one table with four lookups to account emitted one edge, with all four columns still listed include LeftSideRow in the key
2 Picklist/bit column deleted when its option set won't resolve: <options />, declared in another module, or platform-owned clear OptionSetName, leave RowType so sql/edmx keep their own handling
3 Output not reproducible — random colours, and unordered Directory.GetFiles at three sites colour from the module name (FNV-1a); ordinal ordering
4 Self-referencing N:N emitted the intersect column twice, the Ref: twice, and the EDMX navigation property twice suffix the second leg's column and relationship name positionally
5 --target plainsql advertised in AllowedValues, absent from SupportedFormats, rejected on every call one word

Measured

folder refs columns
customer Model layer 9 → 14 214 → 215
Service/Project/Model 87 → 99 914 → 918
Environment/Bootstrap/Model 87 → 100 1344 → 1349

Three solutions × five targets: exit 0, idempotent by hash. plainsql now emits DDL (351/528/684 lines).

Tests

Seven unit tests over in-memory XElement fixtures, no new dependencies. Verified as genuine regressions by reverting the converter to master and re-running: exactly the defect tests fail.

Known limits, deliberately not fixed here

  • EDMX, entity side of a self-referencing N:N still emits one duplicate NavigationProperty (measured: talxis_product has 9, one repeated). It is named after the PK row, not the relationship. The real per-side names live in metadata and are author-chosen — Microsoft's own example pairs connectionroleid with associatedconnectionroleid — so they are not derivable from solution XML. The intersect side is clean.
  • ribbon emits an empty document on all three solutions. Faithful: every RibbonDiffXml in them is self-closed.
  • Activity primary keys. The synthesised <entity>id is wrong for activity entities, and is not fixable here — the only layer that triggers key synthesis is also the layer that omits IsActivity.

🤖 Generated with Claude Code

@david-hudec-networg david-hudec-networg changed the title fix(data): model convert silently drops relationships and picklist columns fix(data): five defects in model convert that silently lose or corrupt output Sep 1, 2026
@david-hudec-networg
david-hudec-networg force-pushed the fix/data-model-convert-loses-model-content branch from 716dc16 to 7ac0ac2 Compare September 1, 2026 12:36
david-hudec-networg and others added 5 commits September 1, 2026 15:15
A table with several lookups to one target rendered a single edge, while all
its lookup columns still appeared - understating the model without looking
broken.

The duplicate guard keyed on (LeftSideTable, RighSideTable), ignoring which
column the relationship ran through.

The key now includes LeftSideRow. Genuine duplicates still collapse, which is
what the guard is for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Required, form-visible columns disappeared from the output with no warning, and
a module whose only contribution was such an attribute read as contributing
nothing.

Rows of an optionset kind were deleted outright when their OptionSetName did not
resolve. Three causes seen in real solutions: the global option set declares
<options />, it is declared in a different module, or it is platform-owned.

The row is kept and only OptionSetName is cleared. That is what
ToDbDiagramNotation prefers over RowType, so leaving it set would reference an
Enum that was never emitted; RowType is left alone so sql and edmx keep their
own handling for the kind.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The same unchanged solution converted to a different file on every run, at
identical length - so a generated diagram could not be committed, diffed, or
compared across a model change.

Two causes. Module seeded Colorhex from new Random(). And three file
enumerations used Directory.GetFiles, which guarantees no ordering, so table,
relationship and enum order followed the filesystem.

Colour now derives from the module name with FNV-1a - not string.GetHashCode,
which is randomised per process on .NET Core - and all three enumerations are
ordered ordinally. Everyone's colours change; nothing could have depended on
the old values.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Where an entity has a many-to-many with itself, the intersect table carried the
same column twice, the same Ref twice, and the same EDMX navigation property
twice. A DBML parser rejects the first two outright.

Both sides resolved to <entity>id, and both legs carried the relationship name.

The second column and the second leg's name are suffixed positionally. The real
per-side names live in metadata (Entity1/Entity2IntersectAttribute) and are
author-chosen - the platform's own example pairs connectionroleid with
associatedconnectionroleid - so they cannot be derived from solution XML and are
not guessed at here.

Known limit: on the entity side EDMX still names the navigation property after
the primary key row, so one duplicate remains there. The intersect side is clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rejected

Every plainsql conversion failed, and the error listed the formats it did
support - contradicting the option's own help.

The format is declared in the option's AllowedValues and fully implemented in
the conversion switch, but was missing from the service's SupportedFormats
guard three lines earlier.

Added. A test now asserts every value the option advertises actually converts,
so the two lists cannot drift apart again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant