diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 545c966..caa266a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,52 +5,43 @@ on: release: types: - published - + env: # Stop wasting time caching packages DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true # Disable sending usage data to Microsoft DOTNET_CLI_TELEMETRY_OPTOUT: true # GitHub Packages Feed settings - GITHUB_FEED: https://nuget.pkg.github.com/koenbeuk/ - GITHUB_USER: koenbeuk + GITHUB_FEED: https://nuget.pkg.github.com/autoguru-au/ + GITHUB_USER: autoguru-au GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + jobs: build: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - version: [1, 2, 3] - include: - - version: 1 - configuration: ReleaseV1 - - version: 2 - configuration: ReleaseV2 - - version: 3 - configuration: Release - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.x - include-prerelease: True - name: Install dependencies - run: dotnet restore EntityFrameworkCore.Triggered.sln -p:Configuration=${{ matrix.configuration }} + run: dotnet restore EntityFrameworkCore.Triggered.sln -p:Configuration=Release - name: Build - run: dotnet build --configuration ${{ matrix.configuration }} --no-restore EntityFrameworkCore.Triggered.sln + run: dotnet build --configuration Release --no-restore EntityFrameworkCore.Triggered.sln - name: Test - run: dotnet test --configuration ${{ matrix.configuration }} --verbosity normal EntityFrameworkCore.Triggered.sln + run: dotnet test --configuration Release --verbosity normal EntityFrameworkCore.Triggered.sln - name: Pack if: matrix.os == 'ubuntu-latest' - run: | - dotnet pack -v normal --configuration ${{ matrix.configuration }} --include-symbols --include-source -p:PackageVersion=${{ matrix.version }}-pre-$GITHUB_RUN_ID -o nupkg EntityFrameworkCore.Triggered.Core.slnf + run: | + dotnet pack -v normal --configuration Release --include-symbols --include-source -p:PackageVersion=3-pre-$GITHUB_RUN_ID -o nupkg EntityFrameworkCore.Triggered.Core.slnf - name: Upload Artifact if: matrix.os == 'ubuntu-latest' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: nupkg path: ./nupkg/*.nupkg @@ -59,27 +50,26 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - runs-on: ${{ matrix.os }} - + runs-on: ${{ matrix.os }} + steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.x - include-prerelease: True - name: Install dependencies run: dotnet restore EntityFrameworkCore.Triggered.Samples.slnf - name: Build - run: dotnet build --configuration Release --no-restore EntityFrameworkCore.Triggered.Samples.slnf - + run: dotnet build --configuration Release --no-restore EntityFrameworkCore.Triggered.Samples.slnf + prerelease: needs: build if: github.ref == 'refs/heads/master' runs-on: ubuntu-latest steps: - name: Download Artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v4 with: name: nupkg - name: Push to GitHub Feed diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3bde8a6..969ebd3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,27 +3,27 @@ on: release: types: - published - + env: # Stop wasting time caching packages DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true # Disable sending usage data to Microsoft DOTNET_CLI_TELEMETRY_OPTOUT: true # GitHub Packages Feed settings - GITHUB_FEED: https://nuget.pkg.github.com/koenbeuk/ - GITHUB_USER: koenbeuk + GITHUB_FEED: https://nuget.pkg.github.com/autoguru-au/ + GITHUB_USER: autoguru-au GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Official NuGet Feed settings NUGET_FEED: https://api.nuget.org/v3/index.json NUGET_KEY: ${{ secrets.NUGET_KEY }} - + jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.x - name: Create Release NuGet package @@ -31,12 +31,9 @@ jobs: arrTag=(${GITHUB_REF//\// }) VERSION="${arrTag[2]}" echo Version: $VERSION - CONFIGURATION=$([ "${VERSION:0:2}" == "v1" ] && echo "ReleaseV1" || echo "Release") - CONFIGURATION=$([ "${VERSION:0:2}" == "v2" ] && echo "ReleaseV2" || echo $CONFIGURATION) VERSION="${VERSION:1}" echo Clean Version: $VERSION - echo Configuration: $CONFIGURATION - dotnet pack -v normal -c $CONFIGURATION --include-symbols --include-source -p:PackageVersion=$VERSION -o nupkg EntityFrameworkCore.Triggered.Core.slnf + dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=$VERSION -o nupkg EntityFrameworkCore.Triggered.Core.slnf - name: Push to GitHub Feed run: | for f in ./nupkg/*.nupkg diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..cf329de --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,78 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +EntityFrameworkCore.Triggered is a library that adds trigger support to EF Core. Triggers respond to entity changes before and after they are committed to the database, similar to SQL database triggers but implemented in C#. + +## Build Commands + +```bash +# Build (v3 is the default configuration on this branch) +dotnet build EntityFrameworkCore.Triggered.sln + +# Run all tests +dotnet test EntityFrameworkCore.Triggered.sln + +# Run a single test project +dotnet test test/EntityFrameworkCore.Triggered.Tests + +# Run a specific test by filter +dotnet test test/EntityFrameworkCore.Triggered.Tests --filter "FullyQualifiedName~TriggerSessionTests" + +# Build only core libraries (no samples) +dotnet build EntityFrameworkCore.Triggered.Core.slnf + +# Build samples only +dotnet build EntityFrameworkCore.Triggered.Samples.slnf +``` + +## Multi-Version Build System + +The repo supports 3 major versions via `$(EFCoreTriggeredVersion)` in `Directory.Build.props`: +- **V1**: EF Core 3.1, `netstandard2.0`, config `ReleaseV1`/`DebugV1` +- **V2**: EF Core 5.0, `netstandard2.1`, config `ReleaseV2`/`DebugV2` +- **V3** (current branch): EF Core 6.0+, `net6.0`, config `Release`/`Debug` + +Source files use `#if` directives with `EFCORETRIGGERED_V1`, `EFCORETRIGGERED_V2`, `EFCORETRIGGERED_V3` for version-specific code. + +## Build Settings + +- `TreatWarningsAsErrors` is enabled globally +- Nullable reference types are enabled globally (`enable`) +- Language version is C# 9.0 +- Strong-name signing is used (`EntityFrameworkCore.Triggered.snk`) + +## Architecture + +### NuGet Packages (src/) + +- **Abstractions** — Trigger interfaces only (`IBeforeSaveTrigger`, `IAfterSaveTrigger`, `IAfterSaveFailedTrigger`, `ITriggerContext`, lifecycle triggers) +- **EntityFrameworkCore.Triggered** — Core implementation: trigger session management, SaveChanges interception, cascading support +- **Extensions** — Assembly scanning for auto-discovery of triggers via `AddAssemblyTriggers()` +- **Transactions** / **Transactions.Abstractions** — Transaction-scoped triggers (`IBeforeCommitTrigger`, `IAfterCommitTrigger`, etc.) + +### Core Flow + +1. **Registration**: Triggers are registered via `options.UseTriggers(t => t.AddTrigger())` on `DbContextOptionsBuilder` +2. **Interception**: `TriggerSessionSaveChangesInterceptor` hooks into EF Core's `SaveChanges`/`SaveChangesAsync` +3. **Execution order**: BeforeSaveStarting → BeforeSave (with cascading) → CaptureChanges → BeforeSaveCompleted → [actual SaveChanges] → AfterSaveStarting → AfterSave → AfterSaveCompleted +4. **Cascading**: BeforeSave triggers can modify entities, causing additional trigger invocations. Controlled by `ICascadeStrategy` with a configurable max cycle count (default 100). + +### Key Internal Types + +- `TriggerSession` (`TriggerSession.cs`) — Orchestrates trigger execution for a single SaveChanges call +- `TriggerService` (`TriggerService.cs`) — Factory for trigger sessions, manages current session state +- `TriggerSessionSaveChangesInterceptor` (`Internal/`) — EF Core `ISaveChangesInterceptor` implementation +- `TriggersOptionExtension` (`Infrastructure/Internal/`) — EF Core options extension that registers all trigger services +- `TriggerContextTracker` — Wraps EF Core's ChangeTracker to produce `TriggerContext` instances + +### Test Projects (test/) + +- **Tests** — Unit tests for the core library (xUnit + ScenarioTests.XUnit) +- **Extensions.Tests** — Tests for assembly scanning +- **IntegrationTests** — End-to-end tests +- **Transactions.Tests** — Transaction trigger tests + +Tests use EF Core InMemory and SQLite providers. diff --git a/Directory.Build.props b/Directory.Build.props index 7df2156..200d4c1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,10 +11,10 @@ Triggers for EF Core. Respond to changes in your ApplicationDbContext before and after they are committed to the database Git - https://github.com/koenbeuk/EntityFrameworkCore.Triggered - Koen Bekkenutte + https://github.com/autoguru-au/EntityFrameworkCore.Triggered + AutoGuru EntityFramework;EFCore;Triggers;SQLServer;SqlLite;CosmosDb;.NET Core;aspnetcore - https://github.com/koenbeuk/EntityFrameworkCore.Triggered + https://github.com/autoguru-au/EntityFrameworkCore.Triggered diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 06d4f16..d059cad 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -17,6 +17,7 @@ + AutoGuru.$(MSBuildProjectName) true true snupkg