Tom C. Tom C. - 2 months ago 228
C# Question

EF-Core 2.0 Filter all queries (trying to achieve soft delete)

I am trying to get to work soft delete behaviour in EF-Core 2.0.

public interface ISoftDeleteModel
{
bool IsDeleted { get; set; }
}


Creating proper column and soft-deleting are working fine but filtering entities from DbContext isn't.

I would like to use query filtering in context but I am stuck.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
Type entityType;
// ^^^ it contains type of entity, eg. Blog, Post, etc. using
// modelBuilder.Model.GetEntityTypes().First().Name and converting to Type

var entity = modelBuilder.Entity(entityType);
if(entityType.GetInterface("ISoftDeleteModel") != null)
{
// ??? how to access IsDeleted property ???
entity.HasQueryFilter(x => !x.IsDeleted);
}
}


The question is simple - how to access IsDeleted property?

If I knew type of the entity, eg. Post, and Post implemented ISoftDeleteModel I would be able to do this:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>().HasQueryFilter(x => !x.IsDeleted);
}


But I do not know the type. I am trying to achieve simple thing - all models implementing this interface would be automatically filtered.

Am I missing something?

Thanks

Answer Source

Can't test the exact API, but the general approach would be to create a constrained generic method and call it via reflection:

public static class EFFilterExtensions
{
    public static void SetSoftDeleteFilter(this ModelBuilder modelBuilder, Type entityType)
    {
        SetSoftDeleteFilterMethod.MakeGenericMethod(entityType).Invoke(null, new object[] { modelBuilder });
    }

    static readonly MethodInfo SetSoftDeleteFilterMethod = typeof(EFFilterExtensions).GetMethods(BindingFlags.Public | BindingFlags.Static)
        .Single(t => t.IsGenericMethod && t.Name == "SetSoftDeleteFilter");

    public static void SetSoftDeleteFilter<TEntity>(this ModelBuilder modelBuilder) where TEntity : class, ISoftDeleteModel
    {
        modelBuilder.Entity<TEntity>().HasQueryFilter(x => !x.IsDeleted);
    }
}

Now you can use something like this inside your OnModelCreating:

foreach (var type in modelBuilder.Model.GetEntityTypes())
{
    if (typeof(ISoftDeleteModel).IsAssignableFrom(type.ClrType))
        modelBuilder.SetSoftDeleteFilter(type.ClrType);
}