Skip to content

docs: fix cross reference and duplicate assembly issues, refresh docfx build - #64

Merged
MaceWindu merged 5 commits into
masterfrom
docs/republish-uid-prefixes
Aug 9, 2026
Merged

docs: fix cross reference and duplicate assembly issues, refresh docfx build#64
MaceWindu merged 5 commits into
masterfrom
docs/republish-uid-prefixes

Conversation

@MaceWindu

Copy link
Copy Markdown
Contributor

Replaces the globalPrefix hack in the vendored docfx build with the uidPrefix/uidPrefixes options proposed upstream (MaceWindu/docfx#2, for dotnet/docfx#8966), and fixes the issues that surfaced once docfx started reporting them honestly.

Ready to republish: 0 errors, and the generated page count is unchanged (2389 API files, 2407 HTML pages).

Warning count: 1302 → 67

before after
InvalidBookmark 1197 1 dead in-page anchors
Ignore duplicated member 28 0 F# assembly loaded once per target framework
InvalidAssemblyReference 5 0 FSharp.Core not resolvable
UidNotFound 0 0 two stale article xrefs, fixed here
InvalidFileLink 5 5 from submodule XML comments
MSBuild duplicate source file 60 60 from submodule analyzer projects

The old vendored build prefixed UIDs by prepending the assembly name to the whole documentation comment id. That produced three bugs:

  1. Malformed commentIdlinq2db.T:LinqToDB.AnalyticFunctions.IAggregateFunction\1, with the prefix landing *before* the T:` kind marker instead of after it.
  2. Unprefixed hrefs — references pointed at LinqToDB.html while the page that exists is linq2db.LinqToDB.html.
  3. ~1200 dead member anchors — generated as #LinqToDB_Internal_SchemaProvider_DataTypeInfo_CreateFormat while the target pages contain the prefixed bookmark.

Changes

source/docfx.json — prefix options

globalPrefixuidPrefix on the four EntityFrameworkCore entries. They all build an assembly named linq2db.EntityFrameworkCore (LinqToDB.EntityFrameworkCore.props), so they cannot be told apart by assembly name — the per entry uidPrefix is the only thing that separates them. A combined uidPrefixes map is declared once on the first entry, which is what makes references between entries resolve.

source/docfx.json — F# entry

The glob was linq2db/.build/bin/LinqToDB.FSharp/Release/**/linq2db.FSharp.dll, which matched all five target framework folders, so the same assembly was loaded five times into one metadata entry and every member was reported duplicated. Pinned to netstandard2.0, with references pointing at the net462 output — the only one that carries FSharp.Core next to the assembly, so the F# types in public signatures now resolve. Same 5 generated pages as before.

Articles — two silently broken links

-[Class with name constants](xref:LinqToDB.ProviderName).
+[Class with name constants](xref:linq2db.LinqToDB.ProviderName).

-See [BulkCopyOptions](xref:LinqToDB.Data.BulkCopyOptions) properties …
+See [BulkCopyOptions](xref:linq2db.LinqToDB.Data.BulkCopyOptions) properties …

These were already broken on the published site. The old build did not warn, but it rendered <a class="xref" href="LinqToDB.ProviderName.html"> — a file that does not exist in _site. The new build reports them, and with the UIDs corrected both now link to the real pages.

docfx/ — refreshed vendored build

Rebuilt from custom/linq2db-uidprefix (d89a8f030), which is the branch proposed upstream plus one commit bumping Roslyn to 5.6.0. That bump is required, not cosmetic: with Roslyn 5.0.0 and the .NET 10 SDK, opening the linq2db projects fails with System.TimeoutException from MSBuildProjectLoader's BuildHost.

New docfx/README.md documents why the vendored build exists, how to reproduce it, and when it can be dropped (once these options ship in a released docfx, source/docfx.json needs no further change). docfx.exe --version prints the source commit, so the binaries can always be traced back.

The 11 removed assemblies (Microsoft.Extensions.*, System.Collections.Immutable, …) are transitive dependencies the newer Roslyn no longer ships alongside.

Remaining warnings

All originate in the linq2db submodule and cannot be fixed from this repo:

  • 60 MSBuild Duplicate source file warnings from the analyzer projects' AnalyzerReleases.*.md
  • 5 InvalidFileLink from ~/relational-databases/... links in XML documentation comments
  • 1 InvalidBookmark from a #remarks anchor in an XML documentation comment
  • 1 No .NET API detected for linq2db.Compat

Verification

dotnet build -c Release -p:RunAnalyzersDuringBuild=false -p:EnforceCodeStyleInBuild=false `
  submodules/linq2db/Source/LinqToDB.FSharp/LinqToDB.FSharp.fsproj
./docfx/docfx source/docfx.json

Checked after the build: all four EF Core versions generate their own 21 pages under distinct prefixes, both fixed article links resolve to existing pages, and source/api contains the same 2389 files as before.

🤖 Generated with Claude Code

MaceWindu and others added 2 commits August 8, 2026 15:27
…x build

Replaces the `globalPrefix` hack in the vendored docfx build with the `uidPrefix`/`uidPrefixes` options
proposed upstream (MaceWindu/docfx#2, for dotnet/docfx#8966), and fixes the
issues that surfaced once docfx started reporting them honestly.

Site build goes from 1302 warnings to 67, with no change in generated page count (2389 API files):

  InvalidBookmark          1197 -> 1   dead in-page anchors
  Ignore duplicated member   28 -> 0   F# assembly loaded once per target framework
  InvalidAssemblyReference    5 -> 0   FSharp.Core not resolvable
  UidNotFound                 0 -> 0   two stale article xrefs, fixed here

The old build emitted a malformed `commentId` (`linq2db.T:LinqToDB...`, prefix before the kind marker),
unprefixed namespace and type hrefs pointing at pages that do not exist, and ~1200 member anchors that
did not match the bookmarks on their own pages.

- source/docfx.json: `globalPrefix` -> `uidPrefix` on the four EntityFrameworkCore entries, which all
  build an assembly named `linq2db.EntityFrameworkCore` and so cannot be told apart by assembly name,
  plus a combined `uidPrefixes` map so references between entries resolve.
- source/docfx.json: the F# entry globbed `Release/**/linq2db.FSharp.dll`, matching all five target
  framework folders, so the same assembly was loaded five times and every member reported duplicated.
  Pinned to netstandard2.0, with `references` pointing at the net462 output, the only one that carries
  FSharp.Core next to the assembly.
- articles: `xref:LinqToDB.ProviderName` and `xref:LinqToDB.Data.BulkCopyOptions` now use the prefixed
  UIDs. These were silently broken before: the old build rendered a link to `LinqToDB.ProviderName.html`,
  which is not a page that exists.
- docfx/: refreshed from custom/linq2db-uidprefix (d89a8f030) and documented in docfx/README.md. The 11
  removed assemblies are transitive dependencies the newer Roslyn no longer ships.

Remaining warnings all originate in the linq2db submodule and cannot be fixed here: 60 MSBuild
duplicate-source-file warnings from the analyzer projects, 5 `InvalidFileLink` and 1 `InvalidBookmark`
from XML documentation comments, and `No .NET API detected for linq2db.Compat`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follows the option rename in the upstream PR: `uidPrefix`/`uidPrefixes` read as singular and plural of
one concept while being scoped differently, so the map is now `assemblyUidPrefixes`, which says what its
key is.

- source/docfx.json: `uidPrefixes` -> `assemblyUidPrefixes`, and the comment now leads with the map
  since that is the primary option, with `uidPrefix` as the escape hatch for the four
  EntityFrameworkCore entries that share an assembly name.
- docfx/: rebuilt from custom/linq2db-uidprefix (28070313a), which also picks up a fix for crefs being
  left unprefixed when only `uidPrefix` was set.

Build output is unchanged: 67 warnings, 0 errors, 2389 API files, 2407 HTML pages, no duplicate member,
`UidNotFound` or `InvalidAssemblyReference` warnings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@MaceWindu

Copy link
Copy Markdown
Contributor Author

Updated for the option rename in MaceWindu/docfx#2.

uidPrefixes is now assemblyUidPrefixes — the old pair read as singular and plural of one concept while being scoped differently, which made the map hard to understand. The new name says what its key is.

source/docfx.json is otherwise unchanged in behaviour:

  • assemblyUidPrefixes (declared once, on the first entry) maps assembly name → prefix. It is combined across all metadata entries, which is what makes references between entries resolve.
  • uidPrefix stays on the four EntityFrameworkCore entries. They all build linq2db.EntityFrameworkCore, so they cannot be keyed by assembly name and this is the only thing separating them.

The vendored docfx/ is rebuilt from custom/linq2db-uidprefix (28070313a), which also picks up a bug fix found during the rename: crefs were left unprefixed when only uidPrefix was set, so <see cref="..."/> inside the EF Core docs would have produced dangling xrefs.

Build output is unchanged from the previous commit: 67 warnings, 0 errors, 2389 API files, 2407 HTML pages, and no duplicate-member, UidNotFound or InvalidAssemblyReference warnings.

Follows the upstream PR collapsing `assemblyUidPrefixes` and `uidPrefix` into a single `uidPrefix` that
accepts either form, so the value shape says which scope applies:

  "uidPrefix": { "linq2db": "linq2db", ... }        // by assembly name, shared by all entries
  "uidPrefix": "linq2db.EntityFrameworkCore.EF8"    // this entry's own assemblies

Both forms are still used here for the same reasons as before. Confirmed by measurement that the object
form is not redundant: building with a string on every entry instead leaves 6690 cross-entry references
into the core assembly, across 163 pages, pointing at UIDs no page has. They render as plain text with
no link and no warning.

docfx/ rebuilt from custom/linq2db-uidprefix (1d0b2de99).

Build output unchanged: 67 warnings, 0 errors, 2389 API files, 2407 HTML pages.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@MaceWindu

Copy link
Copy Markdown
Contributor Author

Updated again for MaceWindu/docfx#2, which collapsed the two options into a single uidPrefix accepting either form:

// object: by assembly name, combined across all metadata entries
"uidPrefix": { "linq2db": "linq2db", "linq2db.Tools": "linq2db.Tools", ... }

// string: this entry's own assemblies
"uidPrefix": "linq2db.EntityFrameworkCore.EF8"

source/docfx.json uses both forms for the same reasons as before, and docfx/ is rebuilt from custom/linq2db-uidprefix (1d0b2de99).

Both forms are load-bearing here — measured, not assumed. Building with the string form on all 16 entries and no object form still reports 67 warnings and 0 errors, but the output is quietly worse: a linq2db.Tools page then references LinqToDB.Data.DataConnection while the page that exists is linq2db.LinqToDB.Data.DataConnection, so it renders as plain text with no link. That affects 6690 references across 163 pages, with no warning of any kind. The object form is what keeps those links alive; the string form exists only because the four EntityFrameworkCore projects share an assembly name and cannot be keyed by name.

Build output unchanged from the previous commit: 67 warnings, 0 errors, 2389 API files, 2407 HTML pages, no duplicate-member, UidNotFound or InvalidAssemblyReference warnings.

Follows the upstream PR splitting the merged option back into two names, since one name accepting a
string or an object hid that the two forms have different scope.

  "assemblyUidPrefixes": { "linq2db": "linq2db", ... }   // project wide, keyed by assembly name
  "uidPrefixOverride": "linq2db.EntityFrameworkCore.EF8" // this entry's own assemblies

Same behaviour as before, and the config comment now says the map is project wide rather than merely
"declared once here", which is what makes references between entries resolve.

docfx/ rebuilt from custom/linq2db-uidprefix (5534e75af).

Build output unchanged: 67 warnings, 0 errors, 2389 API files, 2407 HTML pages.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@MaceWindu

Copy link
Copy Markdown
Contributor Author

Updated for the final option shape in MaceWindu/docfx#2. Two names again, because one name accepting either a string or an object hid that the two forms have different scope:

// project wide, keyed by assembly name — the maps of all metadata entries are combined
"assemblyUidPrefixes": { "linq2db": "linq2db", "linq2db.Tools": "linq2db.Tools", ... }

// this entry's own assemblies only
"uidPrefixOverride": "linq2db.EntityFrameworkCore.EF8"

source/docfx.json uses both for the same reasons as before, and the config comment now says the map is project wide rather than merely "declared once here" — that is what makes references between entries resolve, not a formatting preference.

docfx/ rebuilt from custom/linq2db-uidprefix (5534e75af).

Build output unchanged: 67 warnings, 0 errors, 2389 API files, 2407 HTML pages, no duplicate-member, UidNotFound or InvalidAssemblyReference warnings, and all four EF Core versions still generating their own 21 pages.

Follows the upstream PR moving the map out of the metadata entries and up to the top level of
docfx.json, next to `metadata`, where a project level setting belongs:

  {
    "assemblyUidPrefixes": { "linq2db": "linq2db", ... },
    "metadata": [ ... ]
  }

The comment now explains the selection rule rather than just the mechanics: every assembly appearing in
another assembly's public signatures is listed, not only the ones whose namespaces collide, because a
reference that comes out unprefixed loses its link with no warning. It also records why
LinqToDB.Identity is absent, and why `uidPrefixOverride` is safe for the EF Core entries: nothing else
in this project references those packages, so no other entry needs to name one of the four versions.

docfx/ rebuilt from custom/linq2db-uidprefix (91955d98a).

Build output unchanged: 67 warnings, 0 errors, 2389 API files, 2407 HTML pages.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@MaceWindu

Copy link
Copy Markdown
Contributor Author

Updated for the final shape in MaceWindu/docfx#2: the map moves out of the metadata entries and up to the top level of docfx.json, next to metadata, where a project level setting belongs.

{
  "assemblyUidPrefixes": { "linq2db": "linq2db", "linq2db.Tools": "linq2db.Tools", ... },
  "metadata": [
    { "src": [ ... ], "output": "api/linq2db" },
    { "src": [ ... ], "output": "api/linq2db.efcore.8", "uidPrefixOverride": "linq2db.EntityFrameworkCore.EF8" },
    ...
  ]
}

The config comment now records the selection rule rather than the mechanics, since that is the part that is easy to get wrong: every assembly appearing in another assembly's public signatures is listed, not only the ones whose namespaces collide, because a reference that comes out unprefixed loses its link with no warning. It also records two things a future reader would otherwise have to rediscover:

  • why LinqToDB.Identity is absent — its namespace does not collide and nothing else here references it
  • why uidPrefixOverride is safe for the four EF Core entries — nothing else in this project references those packages, so no other entry ever needs to name one of the four versions. If something did, the override could not express it, since all four share the assembly name linq2db.EntityFrameworkCore.

docfx/ rebuilt from custom/linq2db-uidprefix (91955d98a).

Build output unchanged: 67 warnings, 0 errors, 2389 API files, 2407 HTML pages, cross-entry references still prefixed, and all four EF Core versions still generating their own 21 pages.

@MaceWindu
MaceWindu merged commit b12a91f into master Aug 9, 2026
2 checks passed
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