Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 20 additions & 30 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
17 changes: 7 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,37 @@ 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
run: |
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
Expand Down
78 changes: 78 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -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 (`<Nullable>enable</Nullable>`)
- Language version is C# 9.0
- Strong-name signing is used (`EntityFrameworkCore.Triggered.snk`)

## Architecture

### NuGet Packages (src/)

- **Abstractions** — Trigger interfaces only (`IBeforeSaveTrigger<T>`, `IAfterSaveTrigger<T>`, `IAfterSaveFailedTrigger<T>`, `ITriggerContext<T>`, 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<T>`, `IAfterCommitTrigger<T>`, etc.)

### Core Flow

1. **Registration**: Triggers are registered via `options.UseTriggers(t => t.AddTrigger<MyTrigger>())` 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<T>` 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.
6 changes: 3 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
<PropertyGroup>
<Description>Triggers for EF Core. Respond to changes in your ApplicationDbContext before and after they are committed to the database</Description>
<RepositoryType>Git</RepositoryType>
<RepositoryUrl>https://github.com/koenbeuk/EntityFrameworkCore.Triggered</RepositoryUrl>
<Authors>Koen Bekkenutte</Authors>
<RepositoryUrl>https://github.com/autoguru-au/EntityFrameworkCore.Triggered</RepositoryUrl>
<Authors>AutoGuru</Authors>
<PackageTags>EntityFramework;EFCore;Triggers;SQLServer;SqlLite;CosmosDb;.NET Core;aspnetcore</PackageTags>
<PackageProjectUrl>https://github.com/koenbeuk/EntityFrameworkCore.Triggered</PackageProjectUrl>
<PackageProjectUrl>https://github.com/autoguru-au/EntityFrameworkCore.Triggered</PackageProjectUrl>
</PropertyGroup>

<PropertyGroup>
Expand Down
1 change: 1 addition & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
</PropertyGroup>

<PropertyGroup>
<PackageId>AutoGuru.$(MSBuildProjectName)</PackageId>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
Expand Down