.net core 2.0 global filter for IsDeleted column

asp.net-core c# entity-framework entity-framework-core global-filter

Question

I want to add global filter for deleted row by entity framework. And i achieve this by this code in DbContext class.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Role>().HasQueryFilter(p => !p.IsDeleted);
    modelBuilder.Entity<User>().HasQueryFilter(p => !p.IsDeleted);
    modelBuilder.Entity<Foo>().HasQueryFilter(p => !p.IsDeleted);
    modelBuilder.Entity<Bar>().HasQueryFilter(p => !p.IsDeleted);
    .
    .
    .
}

But for every table, i am adding new row to OnModelCreating method. How can i do this with one row for every table like this?

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<T>().HasQueryFilter(p => !p.IsDeleted);
}
1
2
8/29/2018 12:04:07 PM

Accepted Answer

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);
}
22
12/30/2017 7:15:08 PM

Popular Answer

In case you have base class or interface defining the IsActive property, you could use the approach from Filter all queries (trying to achieve soft delete).

Otherwise you could iterate entity types, and for each type having bool IsActive property build dynamically filter expression using Expression class methods:

foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
    var isActiveProperty = entityType.FindProperty("IsActive");
    if (isActiveProperty != null && isActiveProperty.ClrType == typeof(bool))
    {
        var parameter = Expression.Parameter(entityType.ClrType, "p");
        var filter = Expression.Lambda(Expression.Property(parameter, isActiveProperty.PropertyInfo), parameter);
        entityType.QueryFilter = filter;
    }
}

Update (EF Core 3.0): Due to public metadata API breaking change (replacing many properties with Get / Set extension methods), the last line becomes

entityType.SetQueryFilter(filter);


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow