How should I remove all elements in a DbSet?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Removing all rows from a DbSet sounds simple, but the best approach depends on your Entity Framework version and what "remove" really means for your application. Sometimes you want tracked entity deletion with cascade behavior and domain hooks. Other times you just want the fastest possible SQL DELETE.
The important decision is whether Entity Framework should load entities into the change tracker first. If not, use a set-based delete. If yes, use RemoveRange.
Best Option in Modern EF Core: ExecuteDelete
If you are using EF Core 7 or later, the cleanest solution is usually ExecuteDeleteAsync():
This translates to a single SQL DELETE statement and does not load every row into memory. It is usually the right answer when:
- you do not need each entity tracked
- you do not need per-entity business logic in application code
- you want database-side execution and better performance
Because the delete happens directly in the database, it is dramatically more efficient than fetching thousands of rows and removing them one by one.
Use RemoveRange When Tracking Matters
If you need EF to track the deletions, or you rely on client-side logic around entity removal, use RemoveRange:
This works in older EF Core versions and in scenarios where entity instances must pass through the normal change tracker pipeline.
The downside is obvious: every row is first materialized into memory. That can be expensive for large tables.
When Raw SQL or TRUNCATE Makes Sense
Sometimes you are not really doing "entity deletion" at all. You are clearing a table.
In that case, raw SQL may be appropriate:
Or, if your database allows it and the table relationships permit it:
TRUNCATE is usually faster than DELETE, but it comes with important differences:
- it may reset identity values
- it can be blocked by foreign key constraints
- it bypasses normal EF change tracking entirely
So use it only when the database semantics match what you need.
Choosing the Right Approach
A practical rule of thumb looks like this:
- use
ExecuteDeleteAsync()for large set-based deletes in EF Core 7+ - use
RemoveRange()when the entities need to be loaded and tracked - use raw SQL or
TRUNCATEwhen you are performing a database maintenance style operation
Here is a small comparison:
The fastest option is not always the safest one if your code depends on the entity lifecycle.
Consider Relationships and Side Effects
Before clearing a DbSet, think about foreign keys, cascade delete behavior, and other tables that depend on the rows.
For example, deleting all Orders may fail or behave differently depending on:
- required child rows
- cascade rules in the database
- soft-delete conventions
- triggers or auditing tables
This is why the correct answer is never just "call delete on everything." You need to understand what the database is supposed to do when those rows disappear.
Common Pitfalls
- Loading an entire large table into memory when a set-based delete would do the job.
- Using raw SQL or
TRUNCATEand then forgetting the EF change tracker still has stale entities in memory. - Assuming
TRUNCATEis just a fasterDELETE. It has different semantics. - Clearing a
DbSetwithout considering foreign keys or cascade behavior. - Choosing
RemoveRangein a hot path where performance matters and there is no need for tracking.
Summary
- The best modern solution is often
ExecuteDeleteAsync()in EF Core 7+. - Use
RemoveRange()when you genuinely need tracked entity deletion. - Use raw SQL or
TRUNCATEonly when database-level behavior is acceptable. - Always consider relationships, cascade rules, and the EF change tracker.
- "Remove all rows" is easy to say, but the right implementation depends on whether you want entity semantics or pure database semantics.

