EF Core 实现多租户 (2)

重载 SaveChangesAsync 等方法, 保存数据的时候自动赋值 TenantId.

public abstract class BaseEntity { public int Id { get; set; } public Guid TenantId { get; set; } } public class Blog : BaseEntity { public string Name { get; set; } public string Url { get; set; } public virtual IList<Post> Posts { get; set; } } public class BlogConfiguration : IEntityTypeConfiguration<Blog> { public void Configure(EntityTypeBuilder<Blog> builder) { builder.HasKey(t => t.Id); builder.Property(t => t.Name).HasMaxLength(100).IsRequired(); builder.Property(t => t.Url).HasMaxLength(100).IsRequired(); builder.HasData( new Blog { Id = 1, Name = "Blog1 by A", Url = "http://sample.com/1", TenantId= Guid.Parse("B992D195-56CE-49BF-BFDD-4145BA9A0C13") }, new Blog { Id = 2, Name = "Blog2 by A", Url = "http://sample.com/2", TenantId = Guid.Parse("B992D195-56CE-49BF-BFDD-4145BA9A0C13") }, new Blog { Id = 3, Name = "Blog1 by B", Url = "http://sample.com/3", TenantId = Guid.Parse("F55AE0C8-4573-4A0A-9EF9-32F66A828D0E") }); } } public class BloggingContext : DbContext { private Guid _tenantId; public BloggingContext(DbContextOptions<BloggingContext> options, ITenantProvider tenantProvider) : base(options) { _tenantId = tenantProvider.GetTenantId(); } public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfiguration(new BlogConfiguration()); modelBuilder.ApplyConfiguration(new PostConfiguration()); foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { if (entityType.ClrType.BaseType == typeof(BaseEntity)) { ConfigureGlobalFiltersMethodInfo .MakeGenericMethod(entityType.ClrType) .Invoke(this, new object[] { modelBuilder }); } } base.OnModelCreating(modelBuilder); } public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)) { ChangeTracker.DetectChanges(); var entities = ChangeTracker.Entries().Where(e => e.State == EntityState.Added && e.Entity.GetType().BaseType == typeof(BaseEntity)); foreach (var item in entities) { (item.Entity as BaseEntity).TenantId = _tenantId; } return await base.SaveChangesAsync(cancellationToken); } #region private static MethodInfo ConfigureGlobalFiltersMethodInfo = typeof(BloggingContext).GetMethod(nameof(ConfigureGlobalFilters), BindingFlags.Instance | BindingFlags.NonPublic); protected void ConfigureGlobalFilters<T>(ModelBuilder builder) where T : BaseEntity { builder.Entity<T>().HasQueryFilter(e => e.TenantId == _tenantId); } #endregion }

在 Startup 里面配置依赖注入

services.AddDbContext<TenantsContext>(option => option.UseSqlServer(connectionString)); services.AddDbContext<BloggingContext>(option => option.UseSqlServer(connectionString)); services.AddScoped<ITenantProvider, TenantProvider>(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

多数据库实现

多数据的实现也不复杂, 在 Tenant 实体类里面加入新的字段 DatabaseConnectionString 用于存放每个租户的数据库连接字符串, 在 BloggingContext 的 OnConfiguring 方法里面根据获取的 Tenant 配置连接字符串.

public class Tenant { public int Id { get; set; } public string Name { get; set; } public string Host { get; set; } public string DatabaseConnectionString { get; set; } } public class BloggingContext : DbContext { private readonly Tenant _tenant; public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } public BloggingContext(DbContextOptions<BloggingContext> options, ITenantProvider tenantProvider) : base(options) { _tenant = tenantProvider.GetTenant(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(_tenant.DatabaseConnectionString); base.OnConfiguring(optionsBuilder); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfiguration(new BlogConfiguration()); modelBuilder.ApplyConfiguration(new PostConfiguration()); base.OnModelCreating(modelBuilder); } }

这只是一个简单的实现, 多租户系统需要关注的点还有蛮多, 比如租户的注册, 功能订阅, 计费, 数据备份, 统一管理等...

源代码

github

参考

EntityFrameworkCore.samples.QueryFilters

Global query filters in Entity Framework Core 2.0

Abp源码分析多租户体系与权限验证

将您的 web 应用程序转化为多租户 SaaS 解决方案

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpssyx.html