Skip to content

Add Weasel 8 → 9 migration guide#276

Merged
jeremydmiller merged 4 commits into
masterfrom
docs/9.0-migration-guide
May 13, 2026
Merged

Add Weasel 8 → 9 migration guide#276
jeremydmiller merged 4 commits into
masterfrom
docs/9.0-migration-guide

Conversation

@jeremydmiller
Copy link
Copy Markdown
Member

Summary

Adds docs/migration-guide.md to walk consumers through the Critter Stack 2026 upgrade path. Sits alongside Polecat 3→4 and the (in-flight) Marten 8→9 guides; cross-linked from all three.

The doc covers, in order:

  1. Foundation pin bumps — JasperFx 2.0 alpha line, dropping net8.0, current TFMs.
  2. Dedup-audit relocations with before/after snippets:
    • Weasel.Postgresql.CascadeAction / Weasel.SqlServer.CascadeActionWeasel.Core.CascadeAction (sibling overloads, no defaults to avoid call-site ambiguity)
    • Polecat.BulkInsertModeWeasel.Core.BulkInsertMode (gained OverwriteIfVersionMatches)
    • Weasel.*.MisconfiguredForeignKeyExceptionWeasel.Core.MisconfiguredForeignKeyException
  3. API changes:
    • Canonical AutoIncrement() fluent rename (Serial / BigSerial / SmallSerial / AutoNumber are [Obsolete] one cycle)
    • SchemaObjectDelta<T>.WriteRestorationOfPreviousState now virtual — with the no-op-on-null-Actual override pattern SQLite ViewDelta uses
    • ForeignKey.Equals widening to "same provider root" — restores the consumer marker-subclass pattern Marten and similar libraries rely on
  4. Behaviour fixes worth flagging:
  5. New surface (no migration impact):
    • TableBase / ForeignKeyBase / SequenceBase / FunctionBase / ViewBase / SchemaObjectBase — links out to the Provider Trait Matrix
    • IDdlSyntaxStrategy prototype scope (PG + SQLite for now, opt-in for custom Table subclasses)
    • IsAotCompatible=true + reflective-surface annotation summary
  6. Dependency lockstep with Marten 9 / Polecat 4 / JasperFx 2 / JasperFx.Events 2.

The migration guide is added to the VitePress sidebar under Getting Started:

{ text: 'Migrating from 8.x to 9.0', link: '/migration-guide' }

Drive-by fix

docs/core/provider-trait-matrix.md had a dead relative-path link to src/Weasel.Core/IDdlSyntaxStrategy.cs that broke npm run build — VitePress can't resolve cross-tree file links. Replaced with the GitHub URL on the 9.0 branch.

Test plan

  • cd docs && npm install && npm run build — passes
  • Sidebar entry visible on the rendered site
  • CI passes

Refs #270, #272, jasperfx#217 (Critter Stack 2026 umbrella), jasperfx#213 (AOT pillar).

🤖 Generated with Claude Code

jeremydmiller and others added 3 commits May 12, 2026 12:50
…Weasel 9.0) (#271)

* Bump JasperFx 2.0.0-alpha.1 → 2.0.0-alpha.8, JasperFx.Events → 2.0.0-alpha.3

Pulls in the latest pre-release alphas of JasperFx 2.0 ahead of #270
implementation work. Verified clean build across the solution and all
non-Docker test suites pass:

- Weasel.Core.Tests: 6/6
- Weasel.Sqlite.Tests: 361/361
- Weasel.CommandLine.Tests: 23/23 (with Postgres container)

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* #270 step 1: SchemaObjectBase + SequenceBase, refactor all 4 Sequence providers

Adds two new base classes in Weasel.Core that consolidate the boilerplate every
schema object reimplements today:

- SchemaObjectBase: implements ISchemaObject with default AllNames() (yield
  Identifier), default "single COUNT(*) row" CreateDeltaAsync, and the standard
  FindDeltaAsync(DbConnection) pattern that builds a query command, executes it,
  resolves the delta, and closes the reader. ReadExistsCountAsync is a virtual
  hook that converts the count to long via Convert.ToInt64 — handles the int /
  long / decimal variation across SQL Server / MySQL / Oracle so no provider
  needs to override the delta logic anymore.

- SequenceBase: extends SchemaObjectBase with the cross-provider sequence
  properties: nullable StartWith, plus Owner / OwnerColumn for providers that
  support OWNED BY.

Refactors the 4 provider Sequence subclasses to inherit from SequenceBase:

- Postgresql.Sequence:    84 → 55 LOC
- SqlServer.Sequence:     86 → 55 LOC
- Oracle.Sequence:       104 → 76 LOC (Oracle keeps its provider-specific
                                       OracleConnection FindDeltaAsync overload
                                       and its PL/SQL DROP wrapper)
- MySql.Sequence:         65 → 58 LOC (gains the standard IInheritance, keeps
                                       its table-based emulation override)

Each provider's concrete subclass now only contains the 3 things that genuinely
vary per database — WriteCreateStatement, WriteDropStatement,
ConfigureQueryCommand — plus a strongly-typed FindDeltaAsync overload (e.g.
NpgsqlConnection) where the original surface had one.

No behavioural change. All 15 Postgresql Sequence tests pass; all 6 SqlServer
Sequence tests pass.

Sets the foundation for FunctionBase, ViewBase, and other ISchemaObject
implementations in subsequent commits.

Refs #270.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* #270 step 2: FunctionBase, refactor PostgreSQL + SQL Server Function

Lifts the ISchemaObject boilerplate that both Function implementations were
duplicating:

- Constructor pattern (DbObjectName, body, optional drop statements)
- IsRemoved flag with protected setter
- Body(Migrator?) — routes through the provider's WriteCreateStatement
- DropStatements() — returns pre-supplied drops, empty for IsRemoved, or
  ComputeDefaultDropStatements() (abstract per provider)
- WriteDropStatement — iterates DropStatements() and writes each line
- CreateDeltaAsync — reads existing function via ReadExistingFromReaderAsync
  (abstract per provider) and routes through CreateFunctionDelta (abstract)
- AllNames, Identifier — inherited from SchemaObjectBase

Each provider's concrete Function now only contains:

- WriteCreateStatement (PG writes RawBody directly; SS wraps in EXEC sp_executesql)
- ConfigureQueryCommand (pg_proc + pg_namespace vs. sys.sql_modules)
- ReadExistingFromReaderAsync (PG reads 2 result sets; SS reads 1)
- ComputeDefaultDropStatements (PG parses function signature; SS uses identifier)
- GetDefaultMigrator (provider's migrator type)
- ParseIdentifier / ParseSignature (PG has the signature parser; SS uses identifier-only)
- FetchExistingAsync / FindDeltaAsync (strongly-typed connection overloads)
- ForSql / ForRemoval static factories
- PG-only: BuildTemplate / WriteTemplate (template-based function generation)

Result:

- Postgresql.Functions.Function:   230 → 185 LOC (-45)
- SqlServer.Functions.Function:    155 → 109 LOC (-46)
- Weasel.Core.FunctionBase:          0 → 133 LOC (new shared base)

Net: 91 LOC removed from per-provider Function files, 133 LOC of new
reusable infrastructure.

All 32 Postgresql Function tests pass; all 20 SqlServer Function tests pass.

Refs #270.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* #270 step 3: ViewBase, refactor PostgreSQL + SQLite View, SQLite ViewDelta

Add Weasel.Core/ViewBase (extends SchemaObjectBase) owning the SELECT body
(ViewSql), the MoveToSchema/WithSchema template-method pair, and the
ToBasicCreateViewSql helper. Both PostgreSQL.View and SQLite.View now
inherit from it and only carry the provider-specific DDL.

PostgreSQL View keeps its protected virtual ViewType/ViewKind/GetCreationOptions
hooks so MaterializedView continues to override "VIEW"→"MATERIALIZED VIEW",
'v'→'m', and USING <access_method>.

SQLite ViewDelta now inherits from SchemaObjectDelta<View> instead of
hand-rolling ISchemaObjectDelta. To preserve its "no previous state to
restore" no-op semantics when Actual is null, SchemaObjectDelta<T>.
WriteRestorationOfPreviousState is now virtual (it was concrete and would
NRE on a null Actual). No existing override conflicts — grep across src
shows the new SQLite ViewDelta override is the only one.

Tests pass with no behaviour change:
- SQLite view tests: 38/38 (net10.0)
- PostgreSQL view tests: 11/11 (net10.0)
- PostgreSQL sequence + function + materialized-view tests: 47/47
- SQL Server sequence + function tests: 26/26

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* #270 step 4: pull ForeignKey shared methods into ForeignKeyBase

The five provider ForeignKey classes (PG, SQL Server, Oracle, MySQL, SQLite)
were carrying ~120 LOC of near-identical boilerplate each. This commit
consolidates the cross-provider parts into ForeignKeyBase:

- Parse(string definition[, string? defaultSchema]) — fully shared; subclasses
  only supply ParseLinkedTable(string) to wrap the parsed table name in the
  provider-specific DbObjectName subclass. ParseCascadeClause helper reads
  ON DELETE / ON UPDATE clauses (PG keeps a `new` overload defaulting schema
  to "public" for catalog rows that hand back unqualified names).
- LinkColumns(columnName, referencedName) — append a (dependent, principal)
  pair to ColumnNames/LinkedNames. MySQL's setter-driven List<string> still
  works correctly through the base because the setters re-marshal.
- ReadReferentialActions(onDelete, onUpdate) — translates catalog cascade
  text via a virtual ReadAction default that handles every spelling used
  across the five providers (CASCADE / RESTRICT / NO_ACTION / NO ACTION /
  SET_NULL / SET NULL / SET_DEFAULT / SET DEFAULT). The static
  *Provider.ReadAction methods stay in place for external callers.
- Equals / GetHashCode — structural across same-provider FKs (name +
  column lists + LinkedTable + both cascades). Parametrised on virtual
  NameComparer/ColumnComparer so case-folding providers (Oracle, SQLite,
  MySQL) just override the comparers. Hash drops the buggy array-reference
  hashing the per-provider versions had — array identity hashing gave
  different hashes to structurally-equal FKs, breaking HashSet/Dictionary;
  omitting the arrays gives more collisions but keeps semantics correct.

Equality is widened from the previous strict same-type to "same provider
root" — Equals(object?) walks up the inheritance chain to find the
immediate subclass of ForeignKeyBase (the provider's concrete ForeignKey
class) and uses IsAssignableFrom, so consumer-supplied marker subclasses
(Marten ships one as `ForeignKeyTest` in PG tests) still compare equal to
plain provider FKs as they did pre-9.0, while cross-provider comparison
still returns false. This restores a regression a strict GetType()==GetType()
check would have introduced.

MisconfiguredForeignKeyException was defined identically and unused in
four providers; the canonical type now lives in Weasel.Core and the
provider duplicates are removed. (The PG-specific InvalidForeignKeyException
and TryToCorrectForLink stay where they are.) MySQL's IsEquivalentTo is
kept as a forwarder to the new EqualsCore for backward compatibility.

Not consolidated (deferred): WriteAddStatement / WriteDropStatement / ToDDL
all take a provider-specific Table parameter, blocked on TableBase (step 9).
The obsolete OnDelete/OnUpdate shim properties stay in the subclasses since
their per-provider local CascadeAction enums are already marked [Obsolete]
and scheduled for removal.

Net diff: -189 LOC across the FK files. All tests pass:
- PostgreSQL: 29/29 FK + 181/182 detecting_table_deltas (1 unrelated skip)
- SQL Server: 22/22 FK + 31/31 detecting_table_deltas
- SQLite: 24/24 FK + 361/361 full suite
- Weasel.Core: 6/6

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* #270 step 5: MySQL TableDelta now inherits SchemaObjectDelta<Table>

MySQL.TableDelta was the only provider TableDelta still implementing
ISchemaObjectDelta by hand (PG, SQL Server, Oracle, SQLite all inherit
from SchemaObjectDelta<Table>). This commit brings MySQL onto the same
pattern so the five deltas now compose uniformly:

- Constructor forwards (expected, actual) to base
- Differencing happens in the protected compare(expected, actual) override
  (the base ctor calls it and stores the SchemaPatchDifference on
  Difference); side-effect-populates Columns/Indexes/ForeignKeys/
  PrimaryKeyDifference as before
- WriteUpdate / WriteRollback overrides preserve the MySQL-specific DDL
  (MODIFY COLUMN, backtick quoting, DROP FOREIGN KEY syntax,
  PartitionStrategy = SchemaPatchDifference.Invalid path)
- WriteRestorationOfPreviousState overridden because MySQL has historically
  no-op'd when Actual is null (the base default would NRE)
- Expected/Actual properties drop from this file — inherited from
  SchemaObjectDelta<T>

The public surface stays compatible: Columns / Indexes / ForeignKeys /
PrimaryKeyDifference / HasChanges are still public (HasChanges is no
longer required by ISchemaObjectDelta but is kept for callers that ask
"anything to do?" without unpacking the Difference enum).

All MySQL tests pass: 188/188 (33 integration tests against MySQL 8.0
container, 155 unit tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* #270 step 7: extract DatabaseProvider memo-lookup helpers

The five provider ResolveDatabaseType + ResolveXxxDbType methods were
all carrying the same memo-lookup prologue: try DatabaseTypeMemo /
ParameterTypeMemo; if absent and the type is Nullable<T>, look up T
instead, copy the result to the nullable's slot for next-time, and
return. Only the fallback after that miss differs per provider
(PG → Npgsql plugin type map; SS/Oracle/MySQL → NotSupportedException;
SQLite → null/Text).

Move the lookup-with-nullable-promote into DatabaseProvider as two
shared protected helpers — ResolveDatabaseTypeFromMemo and
ResolveParameterTypeFromMemo — and rewrite each provider's two Resolve*
methods as one-or-two-liners that combine the helper with the provider-
specific fallback.

The audit also suggested templating determineParameterType but I held
back: in SS/Oracle/MySQL/SQLite the post-Resolve branches (IsEnum,
IsArray, DBNull, IsConstructedGenericType) are largely unreachable
because their Resolve always returns a sentinel fallback rather than
null. Removing that dead code would technically preserve behaviour but
risks edge cases that aren't covered by the current tests, so I left
those bodies alone.

Net diff: -97 LOC across the five provider files. No behaviour change.
Tests pass:
- PostgreSQL: 69/69 (Provider + TypeMapping + ToParameterType filter)
- SQL Server: 8/8 (+8 pre-existing skips)
- SQLite: 45/45
- MySQL: 37/37

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* #270 step 8: prototype IDdlSyntaxStrategy on PostgreSQL + SQLite

Introduces IDdlSyntaxStrategy in Weasel.Core — the pluggable strategy
object the #270 plan calls out for owning provider-specific DDL syntax
decisions. Step 8 deliberately covers only the parts of CREATE / DROP
that genuinely differ between PostgreSQL and SQLite (the two providers
at opposite ends of the feature spectrum, per the audit's "prototype
against the extremes" guidance). If the shape holds for those two,
step 9 will extend it across the remaining three providers when the
full CREATE algorithm moves into TableBase.

Strategy surface (intentionally minimal for the prototype):

  QuoteIdentifier(name)                       — provider quoting rules
  WriteDropTable(writer, identifier)          — PG appends CASCADE, SQLite doesn't
  WriteCreateTableHeader(writer, id, style)   — CREATE TABLE [IF NOT EXISTS] x (
  InlineForeignKeyConstraints                 — true for SQLite (no ALTER ADD FK)
  AutoIncrementToken                          — SERIAL / AUTOINCREMENT / ...
  StatementTerminator                         — ";", or "/" for Oracle

PostgresqlDdlSyntax (singleton) implements the PG side; SqliteDdlSyntax
the SQLite side. Both Table classes expose Syntax => instance and route
WriteDropStatement plus the CREATE-header part of WriteCreateStatement
through it. The remaining CREATE body (columns / PK / inline FKs /
indexes) is unchanged for now — it'll move into TableBase in step 9
once we've validated the strategy shape is workable.

This is a behaviour-preserving change: the emitted SQL is byte-for-byte
identical to before. Tests pass:
- PostgreSQL: 185/188 (3 pre-existing concurrent-index skips)
- SQLite: 361/361 (full suite)

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* #270 step 9: TableBase, refactor all five provider Tables

Introduces Weasel.Core.TableBase<TColumn, TIndex, TForeignKey> — the
cross-provider base for the five concrete Table classes that the audit
predicted would absorb ~600-700 LOC of duplication. This commit lifts
the high-confidence shared boilerplate; the full CREATE algorithm body
is still per-provider for now (step 8's IDdlSyntaxStrategy will absorb
that incrementally once the strategy shape has settled).

TableBase owns:
- _columns field + Columns getter (generic over TColumn : ITableColumn)
- ForeignKeys + Indexes + IgnoredIndexes
- MaxIdentifierLength + TruncatedNameIdentifier
- ColumnFor / HasColumn / IndexFor / HasIndex / HasIgnoredIndex / IgnoreIndex
- RemoveColumn (always case-insensitive — matches every provider's
  pre-existing impl, distinct from the per-provider NameComparison)
- PrimaryKeyName with DefaultPrimaryKeyName() hook (PG/SS use
  pkey_{name}_{cols}, Oracle/MySQL use pk_{name}_{cols}, SQLite uses
  pk_{name})
- NameComparison virtual (Ordinal default — PG only; SS/Oracle/MySQL/
  SQLite override to OrdinalIgnoreCase to match their pre-existing
  ColumnFor/HasColumn behaviour)
- ToString() returning $"Table: {Identifier}" (PG/SQLite had this
  override; SS/Oracle/MySQL inherit it now)
- ToBasicCreateTableSql() template via GetDefaultMigratorForBasicSql() hook
- All ITable explicit interface implementations (AddColumn(string,string),
  AddColumn(string,Type), AddPrimaryKeyColumn variants, ForeignKeys,
  AddForeignKey) — providers supply just three abstract hooks:
  CreateForeignKey(name), AddColumnAndReturn(name,type),
  AddPrimaryKeyColumnAndReturn(name,type), and GetDatabaseTypeFor(Type).

PrimaryKeyColumns stays abstract per provider — PG / SQLite store an
explicit _primaryKeyColumns field, SS / Oracle / MySQL derive from
Columns.Where(IsPrimaryKey). Both shapes preserved.

Provider-specific items retained:
- Constructor (provider wraps Identifier in its DbObjectName subclass)
- WriteCreateStatement / WriteDropStatement (step 8 strategy partially
  consumes; rest stays per-provider until follow-up)
- AllNames() override (provider-specific DbObjectName wrapping for
  indexes and FKs)
- ColumnExpression nested class
- AddColumn fluent overloads returning ColumnExpression
- PrimaryKeyDeclaration() (provider-specific quoting / formatting)
- Partition properties / Engine / Charset / WithoutRowId / StrictTypes
  / etc.

SQLite's TableDelta touched _columns directly via internal access;
swapped those to call the public AddColumn(TableColumn) factory which
properly sets Parent and adds via the base list.

IgnoredIndexes was previously a PG- and SQLite-only feature; now uniform
across all providers via TableBase. Empty set on SS / Oracle / MySQL is
a no-op until those providers wire it up.

Diff: -398 LOC net across the five Table.cs files (-565 deletions,
+167 insertions excluding the new TableBase.cs).

All tests pass:
- PostgreSQL: 653/656 (3 pre-existing skips)
- SQL Server: 244/252 (8 pre-existing skips)
- SQLite: 361/361
- MySQL: 188/188
- Weasel.Core: 6/6
- Oracle: build-only (no container available locally)

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* #270 step 10: canonical AutoIncrement() across providers, doc trait matrix

Standardises the identity / auto-increment fluent spelling. Before 9.0 the
same conceptual operation was spelled four different ways across providers:

  PostgreSQL:  .Serial() / .BigSerial() / .SmallSerial()
  SQL Server:  .AutoNumber()
  Oracle:      .AutoNumber()
  MySQL:       .AutoNumber()
  SQLite:      .AutoIncrement()

In 9.0 every provider's ColumnExpression exposes a canonical .AutoIncrement()
(plus .BigAutoIncrement() / .SmallAutoIncrement() on PostgreSQL for the 64-bit
and 16-bit variants). The historical names are kept as [Obsolete] aliases for
one major-version cycle, with deprecation messages pointing at the canonical
name.

Polymorphic schema-building code now works without provider-specific
casts:

  table.AddColumn<int>("id").AsPrimaryKey().AutoIncrement();   // works on all 5

SQL emitted is unchanged per provider — only the C# spelling on the
caller side is normalised.

Also adds docs/core/provider-trait-matrix.md (linked in the sidebar) that
explicitly documents the per-provider differences the audit catalogued:
quoting, case-folding, ALTER TABLE limitations, partitioning, JSON storage,
identity spelling, primary-key name defaults, the new TableBase /
ForeignKeyBase / SequenceBase / FunctionBase / ViewBase / IDdlSyntaxStrategy
class hierarchy, and the constructor pattern. End users can now answer
"what works on every provider vs what's provider-specific" without reading
source.

DocSamples updated to use the canonical AutoIncrement() spelling. Test
code still uses the old names (Serial() / AutoNumber()) for now to validate
the [Obsolete] forwarders work — those warnings will surface in CI as a
gentle nudge to migrate but they're not failures.

This completes the #270 work plan. Sequence of commits on this branch:

  83cf822  JasperFx 2.0.0-alpha.8 upgrade
  5ea60f3  step 1: SchemaObjectBase + SequenceBase
  639628f  step 2: FunctionBase
  2967556  step 3: ViewBase + SQLite ViewDelta
  e7afae8  step 4: ForeignKeyBase                  (-174 LOC net)
  02a6440  step 5: MySQL TableDelta                ( +5 LOC net)
  9c49c1e  step 7: DatabaseProvider memo helpers   ( -37 LOC net)
  1319cf4  step 8: IDdlSyntaxStrategy prototype
  99485c1  step 9: TableBase                       (-154 LOC net)
  <this>   step 10: AutoIncrement + trait matrix

Step 6 (TableDeltaBase shared helpers) was absorbed into step 9 since
the helpers needed TableBase to exist before they could decompose
cleanly.

All tests pass:
- PostgreSQL: 653/656 (3 pre-existing skips)
- SQL Server: 244/252 (8 pre-existing skips)
- SQLite: 361/361
- MySQL: 188/188
- Weasel.Core: 6/6
- Oracle: build-clean (no container locally)

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
…ions (#272)

#261 — Postgres/SqlServer CascadeAction obsolete

PG and SqlServer ColumnExpression.ForeignKeyTo overloads took the local
[Obsolete] CascadeAction enum, with no Weasel.Core.CascadeAction overload.
Users wanting to opt out of the obsolete enum had no fluent path. (Oracle
and MySQL already used Core.CascadeAction directly via the using directive —
verified by grep; no local CascadeAction enum exists in those projects.)

Adds Core.CascadeAction sibling overloads on PG and SS ColumnExpression for
ForeignKeyTo(string,...), ForeignKeyTo(Table,...), ForeignKeyTo(DbObjectName,...)
and (PG only) ForeignKeyTo(PostgresqlObjectName,...). The new overloads have
no defaults to avoid call-site ambiguity with the existing overloads, so
.ForeignKeyTo("x", "y") binds unchanged. Callers opt into Core by passing
the enum values explicitly:

  // before — obsolete enum
  .ForeignKeyTo("tbl", "id", onDelete: Weasel.Postgresql.CascadeAction.Cascade)

  // after — canonical
  .ForeignKeyTo("tbl", "id", fkName: null,
                onDelete: Weasel.Core.CascadeAction.Cascade,
                onUpdate: Weasel.Core.CascadeAction.NoAction)

Defaults can be restored when the local CascadeAction enums are removed in
a future major (currently scheduled per #263).

#265 — AOT IL3050 on AnsiConsole.WriteException in AssertCommand

Annotates AssertCommand.Execute with [RequiresDynamicCode] so the IL3050
diagnostic propagates to AOT-publishing consumers as a precise warning
instead of getting swallowed inside Weasel.Core. The xmldoc explains why:
db-assert is a dev-time CLI tool, so the minimum-blast-radius fix is to
let consumers see the warning at the entry point rather than chase
Spectre.Console's ExceptionFormatter.

#266 — AOT IL2075 on CommandBuilderBase.AddParameters reflection

Annotates the parameters argument of AddParameters(object) with
[DynamicallyAccessedMembers(PublicProperties)] so callers carry the
runtime contract through the type system, plus an
[UnconditionalSuppressMessage("Trimming", "IL2075", ...)] that
documents why the warning at the GetType().GetProperties() call site is
expected — the trimmer's flow analysis doesn't currently propagate the
DAM annotation through object.GetType(), but the runtime contract holds.
xmldoc points at the longer-term source-generator path as path 2 from
the issue.

All existing tests pass: PG 653/656 (3 pre-existing skips), SS 244/252
(8 pre-existing), SQLite 361/361, MySQL 188/188, Core 6/6.

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
New docs/migration-guide.md walks consumers through the Critter Stack 2026
upgrade path for Weasel:

- Foundation pin bumps (JasperFx 2.0 alphas) + dropping net8.0
- Dedup-audit relocations (CascadeAction, BulkInsertMode,
  MisconfiguredForeignKeyException → Weasel.Core) with before/after snippets
- Canonical AutoIncrement() fluent rename + the [Obsolete] alias table
  (Serial / BigSerial / SmallSerial / AutoNumber)
- SchemaObjectDelta<T>.WriteRestorationOfPreviousState now virtual,
  including the no-op-on-null-Actual override pattern that SQLite ViewDelta
  uses
- ForeignKey.Equals widening — restores the Marten/consumer marker-subclass
  pattern that strict GetType() comparison broke
- Behaviour fixes worth flagging (SQL Server column casing, SQLite composite
  PK)
- New surface (no migration impact): TableBase / ForeignKeyBase /
  SequenceBase / FunctionBase / ViewBase / IDdlSyntaxStrategy, with links
  out to the trait matrix
- AOT posture summary + link to the cross-stack publishing guide
- Lockstep dependency table for the 2026 wave

Sidebar gets a "Migrating from 8.x to 9.0" entry under Getting Started.

Drive-by fix: docs/core/provider-trait-matrix.md had a dead link to
src/Weasel.Core/IDdlSyntaxStrategy.cs via a relative path VitePress can't
resolve. Replaced with the GitHub URL on the 9.0 branch so the docs build
passes again.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@jeremydmiller jeremydmiller changed the base branch from 9.0 to master May 13, 2026 19:27
@jeremydmiller jeremydmiller merged commit dd1c81a into master May 13, 2026
21 checks passed
@jeremydmiller jeremydmiller deleted the docs/9.0-migration-guide branch May 13, 2026 19:33
@jeremydmiller jeremydmiller mentioned this pull request May 18, 2026
14 tasks
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