v0.13.0 — the columns those entities persist
The entities an application actually has, and all of the columns they actually persist.
Every fix here came from outside. @MBrekhof read the code before
filing, separated the reports by cause rather than by symptom, and kept finding the next one in
the review of the last — three issues and five pull requests, each of which turned out to be a
different way of asking the same question: what is this tool entitled to call an entity, and what
is it entitled to leave out.
The number that measures it, against the demos DevExpress ships with 26.1 rather than against our
own fixtures: FeatureCenter.NET.XPO 43 → 140 entities, MainDemo.NET.XPO 14 → 17,
OutlookInspiredDemo.NET.EFCore 23 → 24. Anyone evaluating this tool by pointing it at
FeatureCenter was seeing under a third of it — under an AGENTS.md telling their agent the
inventory was complete. That shape is the one thing this project exists to prevent, and it was
happening in the place a newcomer was most likely to look.
A minor rather than a patch: OrmType.Unknown is a new member on a public enum and
ExtractionOptions.BaseTypeNames changed its default. Neither affects the CLI or the MCP server;
both are breaking for code calling XafLogicExplainer.Core directly.
Changed
-
OrmType.Unknownis a new member of a public enum, returned whenever no evidence names an
ORM. Anyone consumingXafLogicExplainer.Coredirectly and switching exhaustively over
OrmTypehas a new case to handle; anyone using the CLI or the MCP server has nothing to do.
The same noteIControllerAnalyzer.AnalyzeControllerFilegot in 0.12.0, for the same reason —
on 0.x this is what a minor is for. -
ExtractionOptions.BaseTypeNamesis the single source of the list. The CLI, the MCP server
and the test harness each passed their own copy, so the default inCorewas four names while
every caller passed five — four copies with three chances to disagree about what an entity is.
The callers now use the default.
Fixed
-
Unknownnow reaches every place the ORM is reported. The agent files learned it; the HTML
explainer and the MCPxaf_overviewkept deciding in a binary with no third answer, so a project
whose ORM could not be determined was reported as XPO by both. The MCP one was the worse of
the two: it prints the ORM two lines above "These lists are complete, not sampled", from a tool
whose description tells the agent that anything absent does not exist in the application. All
three now go through oneOrmhelper — the defect was never the wrong answer, it was that three
places were each entitled to one. -
The ORM is read as syntax, and is
Unknownwhen nothing says. Detection scanned raw file
text forDevExpress.Persistent.BaseImpl.EFand fell through to XPO, so an EF Core application
whose entities do not use the DevExpress EF base implementation — a legacy schema, its security
tables in another project — was reported as XPO. That is not a hole in the document: ground rule
1 then tells the agent thatDbContext,DbSet<T>and EF migrations "do not exist in this
application and must never be suggested", which forbids the only correct answer. Signals are now
ranked by what it costs to be wrong about them — aDbSet<T>registered on a context first,
thenusingdirectives and base classes — and where neither ORM leaves a trace, the rule is
omitted rather than guessed. Reading text also counted a mention: a comment naming the
namespace was enough, which is how the fixture for this fix first passed against the old code. -
Entities are found through a base class the project wrote itself. Classification matched a
class's own base list against the root names and stopped there, so an application with a shared
base — auditing, a key convention, a display-name property — lost every business object below it.
The inversion is what makes it severe: the abstract base is matched, so the inventory reported
the one class that is not a table and omitted the ones that are. Selection now repeats until a
round changes nothing, exactly asSelectControllersdoes, and resolves a base name through the
deriving file's own scope rather than by simple name, so aContracts.Orderbeside a
BusinessObjects.Orderstill resolves to the base it actually named. On the demos shipped with
26.1:FeatureCenter.NET.XPO43 → 140 entities,MainDemo.NET.XPO14 → 17, and
OutlookInspiredDemo.NET.EFCore23 → 24 — the last of which is an EF Core application, where an
entity that is not registered as aDbSet<T>had no fallback either. -
PersistentBaseandXPBaseObjectare recognised as persistent bases. The XPO hierarchy is
PersistentBase→XPBaseObject→XPCustomObject→XPObject, withXPLiteObjectalso under
XPBaseObject. The list held the three leaves and neither of the classes above them, so a hole
sat in the middle of a documented API — DevExpress names all five as bases a persistent class may
derive from, and recommendsPersistentBase. Deriving from the higher bases is what you do when
the table brings its own key, which is the same population as the legacy schemas the DbSet roster
was added for.FeatureCenter.NET.XPOgainsOidGenerator,NoKeyPropertyNamedBaseObjectand
LayoutDemoObject. -
An entity carries the properties it inherits. A class found through a base declared in the
same project was reported with only the columns it declares itself:PriorityOrderlisted
Rankand omitted theNameandNumberit persists. Finding those classes at all is what
0.12.1 was about, and it converted a silent omission into a stated one — the entity now appeared
under a heading presenting the application's tables, with two thirds of its columns absent, in a
document that tells an agent its inventories are complete. At scale it is the shared base that
hurts: an application on anAuditedObjectlost whatever that base holds from every entity,
which is normally the audit fields an agent most needs to know it must not set by hand. Each
entity now folds in its ancestors' properties in declaration order from the root down, each
marked with the class that declared it; a property the class redeclares stays its own, and the
abstract base is marked as abstract.FeatureCenter.NET.XPOfolds 151 properties over 146
entities,MainDemo.NET.XPO26 over 17 — whereEmployeereachesPhotothroughPersonand
is correctly told it comes fromParty.Summaries of fixed width name an entity's own columns first. The full listings read root
down, the way the class does, but a five-slot table sharing its width with a six-column audit
base spends every slot on the base — and then every row of the entity table names the same
columns and none of the ones that tell one entity from another. Rules and associations an entity
inherits are still listed only under the class that declares them (#14).
Install
dotnet tool install -g XafLogicExplainer.Cli
dnx XafLogicExplainer.Mcp --yes -- --helpFull changelog: v0.12.1...v0.13.0