当前位置 博文首页 > 云怀大师兄:ABP vNext 实现租户Id自动赋值插入

    云怀大师兄:ABP vNext 实现租户Id自动赋值插入

    作者:云怀大师兄 时间:2021-01-23 12:41

    背景

    在使用ABP vNext过程中,因为我们的用户体系庞大,所以一直与其他业务同时开发,在开发其他业务模块时,我们一直存在着误区:认为ABP vNext 自动处理了数据新增时的租户Id(TenantId)的自动赋值插入。直到我们开始接入用户权限模块后,发现并不如此。

    思路

    为了实现字段的自动赋值,且无感知的,我们的思路是做类似拦截器,在上层应用新增数据相关代码流程进入DbContext的时候,在DbContext中进行处理。

    其他

    问题

    为了实现上层业务开发人员的【无感知】,哪怕在代码编写过程中,我们也不希望开发人员有所明显感知自己在使用经过处理的DbContext,于是想到了与Volo.Abp.EntityFrameworkCore.AbpDbContext使用同一个名字AbpDbContext。

    解决方案

    我们首先知道,在C#中,如果有两个命名空间下,具有同名类,那么两个类的优先级为何。

    假设,我们写的类在:TripleH.AbpDbContext。我们在使用这个类的地方的命名空间为:TripleH.*.AClass。

    那么在AClass中使用AbpDbContext时,我们就算引用了Volo.Abp.EntityFrameworkCore命名空间,编译时也会使用TripleH.AbpDbContext。

    这是因为,C#在此处的优先级决定的,它优先找Triple.*命名空间下的AbpDbContext这个类,如果没有,就会逐级往上,找Triple命名空间下的AbpDbContext,如果找到了,就会直接使用它,使用时连命名空间都不需要手动引用。当然,如果没找过,才会去其他命名空间如Volo.Abp.EntityFrameworkCore中寻找。

    实现

    namespace TripleH
    {
        public abstract class AbpDbContext<TDbContext> : Volo.Abp.EntityFrameworkCore.AbpDbContext<TDbContext>
            where TDbContext : AbpDbContext<TDbContext>
        {
            public AbpDbContext(DbContextOptions<TDbContext> options)
                : base(options)
            {
    
            }
    
            protected override void ApplyAbpConceptsForAddedEntity(EntityEntry entry, EntityChangeReport changeReport)
            {
                SetTenantIdIfNull(entry);
                base.ApplyAbpConceptsForAddedEntity(entry, changeReport);
            }
    
            protected virtual void SetTenantIdIfNull(EntityEntry entry)
            {
                if (entry.Entity is IMultiTenant entityWithTenantId
                    && entityWithTenantId.TenantId == null
                    && IsMultiTenantFilterEnabled)
                {
                    ObjectHelper.TrySetProperty(entityWithTenantId, e => e.TenantId, () => CurrentTenant.Id);
                }
            }
        }
    }
    

    使用

    //无需额外引用TripleH命名空间,做到真正的无感知,当然鼠标放到AbpDbContext上,VS 会告诉你是哪个命名空间
    namespace TripleH.Test.EntityFrameworkCore
    {
        //此处继承的AbpDbContext,便是来自TripleH命名空间下,而非Abp
        [ConnectionStringName(BasicDbProperties.ConnectionStringName)]
        public class BasicDbContext : AbpDbContext<BasicDbContext>, IBasicDbContext
        {
            public BasicDbContext(DbContextOptions<BasicDbContext> options)
                : base(options)
            {
    
            }
    
            protected override void OnModelCreating(ModelBuilder builder)
            {
                base.OnModelCreating(builder);
    
                builder.ConfigureBasic();
            }
        }
    }