Skip to content

v0.13.0 — the columns those entities persist

Choose a tag to compare

@peopleworks peopleworks released this 14 Aug 16:22
· 72 commits to main since this release
bef0b40

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.Unknown is a new member of a public enum, returned whenever no evidence names an
    ORM. Anyone consuming XafLogicExplainer.Core directly and switching exhaustively over
    OrmType has a new case to handle; anyone using the CLI or the MCP server has nothing to do.
    The same note IControllerAnalyzer.AnalyzeControllerFile got in 0.12.0, for the same reason —
    on 0.x this is what a minor is for.

  • ExtractionOptions.BaseTypeNames is the single source of the list. The CLI, the MCP server
    and the test harness each passed their own copy, so the default in Core was 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

  • Unknown now reaches every place the ORM is reported. The agent files learned it; the HTML
    explainer and the MCP xaf_overview kept 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 one Orm helper — 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 Unknown when nothing says. Detection scanned raw file
    text for DevExpress.Persistent.BaseImpl.EF and 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 that DbContext, 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 — a DbSet<T> registered on a context first,
    then using directives 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 as SelectControllers does, and resolves a base name through the
    deriving file's own scope rather than by simple name, so a Contracts.Order beside a
    BusinessObjects.Order still resolves to the base it actually named. On the demos shipped with
    26.1: FeatureCenter.NET.XPO 43 → 140 entities, MainDemo.NET.XPO 14 → 17, and
    OutlookInspiredDemo.NET.EFCore 23 → 24 — the last of which is an EF Core application, where an
    entity that is not registered as a DbSet<T> had no fallback either.

  • PersistentBase and XPBaseObject are recognised as persistent bases. The XPO hierarchy is
    PersistentBaseXPBaseObjectXPCustomObjectXPObject, with XPLiteObject also 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 recommends PersistentBase. 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.XPO gains OidGenerator, NoKeyPropertyNamedBaseObject and
    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: PriorityOrder listed
    Rank and omitted the Name and Number it 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 an AuditedObject lost 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.XPO folds 151 properties over 146
    entities, MainDemo.NET.XPO 26 over 17 — where Employee reaches Photo through Person and
    is correctly told it comes from Party.

    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 -- --help

Full changelog: v0.12.1...v0.13.0