EntityFrameworkCore.Triggered: before and after save hooks for EF Core
Triggers for EFCore. Respond to changes in your DbContext before and after they are committed to the database.
At a glance
- What is it?
- EntityFrameworkCore.Triggered gives Entity Framework Core a trigger model, so you can implement IBeforeSaveTrigger and IAfterSaveTrigger for an entity and react to changes in the DbContext before and after they are committed. It ships as five NuGet packages, including transaction and extension variants.
- Who is it for?
- EntityFrameworkCore.Triggered fits a team that wants domain logic tied to a save without scattering it across SaveChanges overrides or relying on database triggers that the ORM does not know about.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 20 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem it solves
EntityFrameworkCore.Triggered is described in one line as triggers for EF Core, responding to changes in your DbContext before and after they are committed to the database. That framing matters, because it draws a line around the mechanism. These are not database triggers and they are not SQL Server triggers. They run inside your application, in the DbContext, at the moment the change tracker is about to write or has just written. The usual way to do this in Entity Framework is to override SaveChanges and interrogate the change tracker, which produces one large method that mixes auditing, validation, side effects and event publication. A trigger model splits that into one class per concern, each typed to the entity it cares about, and each given a context that says what changed. The repository topics list dotnet, efcore, entity-framework, entity-framework-core, sql and trigger, and the primary language is C#.
Five packages, because the pieces are separable
The NuGet package list is worth reading as an architecture. There is EntityFrameworkCore.Triggered, the main package, and EntityFrameworkCore.Triggered.Abstractions, which is the split that lets the interfaces live somewhere your core project can reference without pulling in the implementation. Then there is EntityFrameworkCore.Triggered.Transactions and EntityFrameworkCore.Triggered.Transactions.Abstractions, for working with the ambient transaction the changes are committed in. And there is EntityFrameworkCore.Triggered.Extensions, which the documentation describes as allowing you to discover triggers that are part of an assembly. The separation into abstractions, implementation and extensions is a deliberate choice, and it is the difference between a package you can use from a domain assembly and a package you can only reference from your data access project. The repositories are named with the owner as EFNext, while the documentation links point at the earlier koenbeuk organisation.
The two interfaces and the context they get
You write a trigger by implementing `IBeforeSaveTrigger<TEntity>` and `IAfterSaveTrigger<TEntity>`, and the example in the README shows what that looks like. The before-save trigger receives an `ITriggerContext<Student>` and a cancellation token, and it can inspect `context.ChangeType`, which the example compares against `ChangeType.Added` before adding a related row to the DbContext. The after-save trigger receives the same kind of context and, in the example, checks `context.Entity.SentDate == null && context.ChangeType != ChangeType.Deleted` before sending an email and then setting the sent date and saving the context again. The change type is the piece of context that makes the pattern usable, since it tells a trigger whether the entity was added, modified or deleted, and a trigger that ignores it will fire on the wrong operations.
Registering triggers, and the older way
The current recommendation is to register triggers directly with the DbContext, using the `UseTriggers` extension and the `AddTrigger` calls inside the options lambda, and the documentation says this is the recommended approach starting from version 2.3 and 1.4 respectively. If you are on an older version, the alternative is to register the triggers with your application's DI container instead, like this:
services
.AddDbContext<ApplicationContext>(options => options.UseTriggers())
.AddTransient<IBeforeSaveTrigger<User>, MyBeforeSaveTrigger<User>>();Doing that makes sure your triggers can use other services registered in the DI container, which is the reason the older path exists at all. The extensions package adds a third option, assembly based discovery, where a single call does the finding:
services.AddDbContext<ApplicationContext>(options => options.UseTriggers(triggerOptions => triggerOptions.AddAssemblyTriggers()));So there are three registration styles, and the choice is mostly about how much control you want over which trigger classes are live in a given context.
What is in the repository
The top level shows a large solution with the parts you would expect from a library that takes itself seriously. There is a `src/` directory for the packages, a `test/` directory, a `samples/` directory, a `benchmarks/` directory, and an `EntityFrameworkCore.Triggered.Analyzers/` directory, which is a Roslyn analyzer project, so some of the checks are compile-time rather than runtime. The solution file is named `EntityFrameworkCore.Triggered.slnx`, and there is a `Directory.Build.props` and a `Directory.Packages.props` for central package version management, a `global.json` pinning the SDK and a `nuget.config`. The samples are numbered and named for what they demonstrate, with `samples/1 - HelloWorld/`, `samples/2 - PrimarySchool/`, `samples/3 - StudentManagerAspNetCore/` and `samples/4 - BlazorTests/`, so the progression from a minimal trigger to a full application to a Blazor test scenario is visible in the directory names. The links in the documentation point at a separate samples repository and a sample book store application as well.
Where this is the wrong choice
The limits are worth being concrete about. First, the trigger only fires when your DbContext saves. If a row is changed by raw SQL, by another application, by a migration, or by a bulk operation that bypasses the change tracker, no trigger runs, and the documentation does not describe any mechanism for covering those cases. Second, the after-save trigger runs after the commit, so anything it does is outside the transaction that carried the change, which is why there is a separate Transactions package, and a design that depends on the trigger's work being atomic with the save needs thought. Third, the example code shows a trigger that saves the context again from inside an after-save trigger, and while that is a legitimate pattern for sending an email exactly once, it also means a trigger can re-enter the save path, and how that interacts with the change tracker is something you need to understand for your own model. Fourth, the maintenance picture is the real concern. The release tags are v4.0.0-preview.2 from 2026-10-04, v4.0.0-preview.1 from 2023-03-31 and v3.2.2 from 2022-12-11, so the stable line is old, the preview line is a 4.0 that has not reached a stable release, and the gap between them is where the churn is. Fifth, the project also has an interview by the EF Core team and a related article linked, which is nice, but the trigger model here is a community package rather than a first party feature, and the cost of that is that the API surface can change between previews.
Editorial conclusion
EntityFrameworkCore.Triggered fits a team that wants domain logic tied to a save without scattering it across SaveChanges overrides or relying on database triggers that the ORM does not know about. It is the wrong choice if you need changes applied by something other than your own code, since the trigger only runs when the DbContext saves, and the wrong choice if you expect a stable, regularly released package, since the newest tags are 4.0.0 previews and the last stable release is from 2022. Before you adopt it, read the samples in the repository, especially the two HelloWorld and PrimarySchool ones, and check whether your DbContext saves in a way that would let an after-save trigger observe and then modify the same context. The last push was on 2026-10-04, the licence is MIT, and the package set is EntityFrameworkCore.Triggered plus its Abstractions, Transactions, Transactions.Abstractions and Extensions packages.
Frequently asked questions
How do I add triggers to an Entity Framework Core DbContext?
Implement `IBeforeSaveTrigger<TEntity>` and `IAfterSaveTrigger<TEntity>` for your entity, then register them with the DbContext through the `UseTriggers` options, adding each trigger with `AddTrigger`. On older versions you can instead register the trigger implementations with your DI container, which also lets them use other registered services.
What does a trigger get when it runs?
The trigger receives an `ITriggerContext<TEntity>` and a cancellation token. The context exposes the entity and the change type, so a trigger can tell whether the entity was added, modified or deleted, and the example uses `context.ChangeType` to decide what to do.
Which packages does EntityFrameworkCore.Triggered publish?
Five: EntityFrameworkCore.Triggered, EntityFrameworkCore.Triggered.Abstractions, EntityFrameworkCore.Triggered.Transactions, EntityFrameworkCore.Triggered.Transactions.Abstractions and EntityFrameworkCore.Triggered.Extensions, the last of which adds discovery of triggers that are part of an assembly.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/efnext-entityframeworkcore-triggered)Was this useful?