Loading

ef core SoftDelete Multi-tenancy 软删除、多租户实现 Global Query Filters

ef core提供了Global Query Filters特性来实现多租户与软删除,收集了一些实现方法。
最简单的例子是微软官方的特性解释。

https://docs.microsoft.com/en-us/ef/core/querying/filters

 modelBuilder.Entity<Post>().HasQueryFilter(p => !p.IsDeleted);

比较全面的实现可参考:

https://gunnarpeipman.com/ef-core-global-query-filters/

需要注意Cache问题:

https://docs.microsoft.com/en-us/ef/core/modeling/dynamic-model

https://spin.atomicobject.com/2019/01/29/entity-framework-core-soft-delete/

https://www.meziantou.net/entity-framework-core-soft-delete-using-query-filters.htm

https://comment-it.co.uk/entity-framework-core-soft-delete-workaround/

其中如果需要扫描model自动注册所有实体,属于是高阶用法

构建Expression的代码难以理解,不便后人维护,不过学习一下对理解lambda表达式的底层还是有帮助的

// 1. Add the IsDeleted property
entityType.GetOrAddProperty("IsDeleted", typeof(bool));

// 2. Create the query filter

var parameter = Expression.Parameter(entityType.ClrType);

// EF.Property<bool>(post, "IsDeleted")
var propertyMethodInfo = typeof(EF).GetMethod("Property").MakeGenericMethod(typeof(bool));
var isDeletedProperty = Expression.Call(propertyMethodInfo, parameter, Expression.Constant("IsDeleted"));

// EF.Property<bool>(post, "IsDeleted") == false
BinaryExpression compareExpression = Expression.MakeBinary(ExpressionType.Equal, isDeletedProperty, Expression.Constant(false));

// post => EF.Property<bool>(post, "IsDeleted") == false
var lambda = Expression.Lambda(compareExpression, parameter);

builder.Entity(entityType.ClrType).HasQueryFilter(lambda);

lambda通过全局函数借助统一的父类实现,当然实际开发中,可以改为接口扫描来做。需要注意多个filter条件需要通过“与”操作一起设置,否则后面设置会覆盖前面的设置。

public void SetGlobalQuery<T>(ModelBuilder builder) where T : BaseEntity
{
    builder.Entity<T>().HasKey(e => e.Id);
    builder.Entity<T>().HasQueryFilter(e => e.TenantId == _tenant.Id);
}
private static IList<Type> GetEntityTypes()
{
    if (_entityTypeCache != null)
    {
        return _entityTypeCache.ToList();
    }

    _entityTypeCache = (from a in GetReferencingAssemblies()
                        from t in a.DefinedTypes
                        where t.BaseType == typeof(BaseEntity)
                        select t.AsType()).ToList();

    return _entityTypeCache;
}
protected override void OnModelCreating(ModelBuilder builder)
{
    var navigation = builder.Entity<ProductCategory>()
                            .Metadata
                            .FindNavigation(nameof(ProductCategory.Products));

    navigation.SetPropertyAccessMode(PropertyAccessMode.Field);

    foreach (var type in GetEntityTypes())
    {
        var method = SetGlobalQueryMethod.MakeGenericMethod(type);
        method.Invoke(this, new object[] { builder });
    }

    base.OnModelCreating(builder);
}
posted @ 2019-11-27 16:48  wswind  阅读(593)  评论(0编辑  收藏  举报