Skip to content

Releases: hlib-zinchenko/Hlibz.EntityFrameworkCore.ModelRules

v1.0.0: First stable release

Choose a tag to compare

@hlib-zinchenko hlib-zinchenko released this 29 Sep 12:26

The first stable release of Hlibz.EntityFrameworkCore.ModelRules: declarative rules that check a finished EF Core model against your team's conventions. A model that breaks one fails to build, at startup or in a unit test, and the error lists every violation at once.

dotnet add package Hlibz.EntityFrameworkCore.ModelRules

Rules

ID Rule Checks
MR001 NoShadowProperties() No shadow properties or foreign keys invented by convention
MR002 NamesFollow(style, scope) Tables, columns, keys, FKs, indexes, check constraints, sequences and functions follow snake_case (or your style)
MR003 DecimalsHavePrecision() Every decimal column has a precision
MR004 StringsHaveMaxLength() Every string column has a max length
MR005 NullabilityMatchesClr() C# nullability matches column nullability
MR006 EnumsStoredAsStrings() Enums aren't stored as numbers, including in collections and JSON
MR007 SingleSchema(schema?) One schema per DbContext
MR008 NoCascadeDeleteAcrossAggregates(isRoot) Aggregate roots never cascade-delete each other
MR009 MaxIdentifierLength(max, scope) No identifier exceeds your database's limit
MR010 EntitiesHaveQueryFilter<TMarker>() Soft-deletable / tenant-owned entities have a query filter
MR011 NoClientSideDeleteBehaviors() No ClientSetNull / ClientCascade
MR012 NoNavigationsAcrossAggregates(isRoot) Aggregate roots refer to each other by key only
MR013 AggregateRootsHaveConcurrencyToken(isRoot) Every aggregate root has a concurrency token
MR014 NoRedundantIndexes() No index is a leading prefix of another index or key

Highlights

  • Two ways to run: UseModelRules(...) in ConfigureConventions checks the model as EF builds it; ModelRuleVerifier.Verify(context) / VerifyAll(...) checks one or several contexts from a test.
  • Exclusions by entity, property, name or predicate, per rule or globally.
  • Custom rules: derive from ModelRule and work against IReadOnlyModel.
  • ConfigureClientSetNullAs(DeleteBehavior.SetNull) fixes every unconfigured optional relationship at once.

Compatibility

  • EF Core 8, 9 and 10, with one build per major (net8.0, net9.0, net10.0).
  • Provider-neutral. The same violations are verified on PostgreSQL (Npgsql), SQL Server, SQLite and MySQL (Oracle and Pomelo). Integration tests run against real PostgreSQL, SQL Server and MySQL.

Upgrading from the previews

No code changes since 1.0.0-preview.4. If you're upgrading from 1.0.0-preview.1, the static entry point was renamed in preview.2: replace ModelRules.Verify(...) / ModelRules.Validate(...) with ModelRuleVerifier.Verify(...) / ModelRuleVerifier.Validate(...).

Versioning

Semantic versioning, and rule IDs are permanent. A minor or patch release may make a rule catch cases it missed before. Only a major release changes what a rule requires. New rules are always opt-in. See Versioning.

📦 NuGet · 📖 README

v1.0.0-preview.4

v1.0.0-preview.4 Pre-release
Pre-release

Choose a tag to compare

@hlib-zinchenko hlib-zinchenko released this 29 Sep 11:53

Breaking changes

  • NamesFollow(...) and MaxIdentifierLength(...) throw ArgumentOutOfRangeException when
    scope is NamingScope.None, which used to register a rule that checked nothing.
  • SingleSchema(" ") throws ArgumentException; a blank schema used to be accepted.

Rules report more

Per the versioning policy, a rule may now find violations it missed before:

  • MR002, MR007, MR009 and MR014 check every table an entity type is split into with
    SplitToTable: its name, schema, columns, keys, indexes and the foreign key linking it to the
    main table.
  • MR006 checks collections of enums (List<Status>, Status[]), which EF Core stores as
    numbers even with Properties<Enum>().HaveConversion<string>(). Convert their elements with
    PrimitiveCollection(x => x.Statuses).ElementType().HasConversion<string>().
  • MR006 checks enums inside JSON documents (owned and complex types mapped with ToJson()),
    which EF Core writes as numbers unless converted.
  • Calling UseModelRules more than once reports every rule set's violations at once, instead of
    stopping at the first rule set that fails.

Fixed

  • MR003 and MR004 no longer report a foreign key column whose principal key sets the precision,
    max length or column type: EF Core gives the foreign key column the same facets.
  • MR004 no longer reports columns whose value converter implies a length, such as a Guid or
    int converted to a string (36 and 64 characters).
  • MR006 no longer reports enums the provider maps to a database enum type, such as PostgreSQL
    enums mapped with Npgsql's MapEnum. The exception is a single enum inside a JSON owned type,
    which Npgsql writes as a number, so it is still reported.
  • MR008 no longer reports the cascading foreign keys EF Core adds between one entity's tables:
    from a TPT-derived table to its base table, and from a SplitToTable table to the main one.

New

  • Exclusions reach owned types. Entity<Order>() covers the types Order owns,
    Property<Order>(x => x.ShippingAddress.Street) matches a property of an owned type, and
    Property<Order>(x => x.Lines) matches an owned type's own violations, such as its table name.
  • MR007 explains a table with no schema: it suggests modelBuilder.HasDefaultSchema(...), which
    SingleSchema("dbo") needs on SQL Server.

Docs

  • Every rule's message is shown in the README, along with how the rules work with compiled
    models and how to verify contexts resolved from DI.
  • The Compatibility section explains the minimum EF Core patch each build requires, and what to
    do about an NU1605 restore error.

v1.0.0-preview.3

v1.0.0-preview.3 Pre-release
Pre-release

Choose a tag to compare

@hlib-zinchenko hlib-zinchenko released this 29 Sep 08:22

New

  • ModelRuleVerifier.VerifyAll(params Func<DbContext>[]) checks several contexts in one test.
    Each factory creates its context, so constructors that take services besides the options work.
    Every context is checked before it throws, and the single ModelRuleViolationException lists
    the violations grouped by context.
  • ModelRuleVerifier.VerifyAll(params DbContextOptions[]) does the same for contexts whose
    constructor takes only their options.

v1.0.0-preview.2

v1.0.0-preview.2 Pre-release
Pre-release

Choose a tag to compare

@hlib-zinchenko hlib-zinchenko released this 29 Sep 07:59

Breaking changes

  • ModelRules is renamed to ModelRuleVerifier. The old name clashed with any consumer
    namespace that has a ModelRules segment (e.g. Acme.ModelRules.Tests), which caused CS0234.
    Replace ModelRules.Verify(...) / ModelRules.Validate(...) with
    ModelRuleVerifier.Verify(...) / ModelRuleVerifier.Validate(...).

New

  • ModelRuleVerifier.Verify(DbContextOptions), Verify(DbContextOptions, rules) and
    Validate(DbContextOptions, rules) create the context from its options, so a context normally
    built by DI can be verified in a test without building it yourself. The README shows a theory
    over several contexts.
  • configurationBuilder.ConfigureClientSetNullAs(DeleteBehavior.SetNull) replaces EF Core's
    ClientSetNull default on every optional relationship you haven't configured, which fixes MR011
    in one line. Explicit OnDelete(...) configuration is left alone. It runs before the rules
    whatever order you call them in. Use Restrict on SQL Server where SetNull would create
    multiple cascade paths.

Docs

  • The Install section explains installing a prerelease, including with central package
    management.