4

Using EF Core Global Query Filters To Ignore Soft Deleted Entities

 2 years ago
source link: https://dotnetcoretutorials.com/2022/03/17/using-ef-core-global-query-filters-to-ignore-soft-deleted-entities/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Using EF Core Global Query Filters To Ignore Soft Deleted Entities

In a previous post, we talked about how we could soft delete entities by setting up a DateDeleted column (Read that post here : https://dotnetcoretutorials.com/2022/03/16/auto-updating-created-updated-and-deleted-timestamps-in-entity-framework/) But if you’ve ever done this (Or used a simple “IsDeleted” flag), you’ll know that it becomes a bit of a burden to always have the first line of your query go something like this :

dbSet.Where(x => x.DateDeleted == null);

Essentially, you need to remember to always be filtering out rows which have a DateDeleted. Annoying!

Microsoft have a great way to solve this with what’s called “Global Query Filters”. And the documentation even provides an example for how to ignore soft deletes in your code : https://docs.microsoft.com/en-us/ef/core/querying/filters

The problem with this is that it only gives examples on how to do this for each entity, one at a time. If your database has 30 tables, all with a DateDeleted flag, you’re going to have to remember to add the configuration each and every time.

In previous versions of Entity Framework, we could get around this by using “Conventions”. Conventions were a way to apply configuration to a broad set of Entities based on.. well.. conventions. So for example, you could say “If you see an IsDeleted boolean field on an entity, we always want to add a filter for that”. Unfortunately, EF Core does not have conventions (But it may land in EF Core 7). So instead, we have to do things a bit of a rinky dink way.

To do so, we just need to override the OnModelCreating to handle a bit of extra code (Of course we can extract this out to helper methods, but for simplicity I’m showing where it goes in our DBContext).

public class MyContext: DbContext
{
	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		foreach (var entityType in modelBuilder.Model.GetEntityTypes())
		{ 
			//If the actual entity is an auditable type. 
			if(typeof(Auditable).IsAssignableFrom(entityType.ClrType))
			{
				//This adds (In a reflection type way), a Global Query Filter
				//https://docs.microsoft.com/en-us/ef/core/querying/filters
				//That always excludes deleted items. You can opt out by using dbSet.IgnoreQueryFilters()
				var parameter = Expression.Parameter(entityType.ClrType, "p");
				var deletedCheck = Expression.Lambda(Expression.Equal(Expression.Property(parameter, "DateDeleted"), Expression.Constant(null)), parameter);
				modelBuilder.Entity(entityType.ClrType).HasQueryFilter(deletedCheck);
			}
		}
		
		base.OnModelCreating(modelBuilder);
	}
}

What does this do?

Of course, we can use this same loop to add other “Conventions” too. Things like adding an Index to the DateDeleted field is possible via the OnModelCreating override.

Now, whenever we query the database, Entity Framework will automatically filter our soft deleted entities for us!

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Comment

Name *

Email *

Website


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK