多租户解析与Demo

在做Saas应用时,多租户解析往往是很重要的组成部分,也是用户访问网站最先处理的逻辑。

文前介绍:

多租户的数据库实现方式主要有三种:

  1. 单一数据库实现,每条数据标识租户Id进行识别数据属于哪个租户
  2. 一租户一个数据库,能够做到完全的数据隔离
  3. 混合模式,部分数据在一张表上,主要是一些基础数据;其他业务数据分库存储。

无论是哪种方式都要知道租户是谁才能查询数据库。

获取租户的方式也可以有多种:

  1. 根据域名或者子域名不同可以获知租户,上面的所有场景都适合使用;(必须要有域名和dns服务)
  2. 根据用户id获取租户信息,从而存入前端的cookie中或者header中,只适用于上面的1、3方式,因为需要不同租户的用户都存入一种表中,方便查询对应的租户信息;

下文中的例子只是简单的一个例子,没有进一步的业务场景,实现功能如下:

用户根据不同的域名进入系统;

后台拿刀http传入的域名并解析;

查询租户数据库,查找出租户名称返回显示。

新建解决方案,并其下新增三个项目:

try_MultiTenantApi:webapi项目,项目的启动项目,为了方方便,租户的Service和IService放入了这个项目中,实际应用时要放入业务层;

MultiTenantApi.Models:类库项目,存放租户对象;

MultiTenantApi.Data:数据层,使用efcore,存放上下文DbContext和迁移文件

多租户解析与Demo

MultiTenantApi.Models中新增实体Tenant

```
public class Tenant
{
    public int Id { get; set; }
    public string Identifier { get; set; } // 租户标识符,例如域名
    public string Name { get; set; }
    public string ConnectionString { get; set; } // 每个租户的独立数据库连接字符串
}
```

MultiTenantApi.Data:

引入nuget包:要注意跟你项目的.net版本相同

多租户解析与Demo

这些包的具体说明:

  1. Microsoft.EntityFrameworkCore

    • 功能: Entity Framework Core 是一个现代的对象-关系映射器(ORM),用于 .NET。它支持 LINQ 查询、变更跟踪、更新和模式迁移。EF Core 可以与多种数据库一起工作,包括 SQL Server、Azure SQL Database、SQLite 和 Azure Cosmos DB。
    • 作用: 提供了核心的 ORM 功能,用于在 .NET 应用程序中与数据库进行交互。
    • Microsoft.EntityFrameworkCore.Design

    • 功能: 提供了 Entity Framework Core 工具的共享设计时组件。这些组件包括用于创建和管理迁移的工具。

    • 作用: 用于在设计时(如运行迁移命令时)提供必要的工具支持,帮助开发者更高效地管理和生成数据库迁移。
    • Microsoft.EntityFrameworkCore.Proxies

    • 功能: 为 Entity Framework Core 提供延迟加载代理。这些代理用于在需要时加载相关对象,从而减少内存使用和提高性能。

    • 作用: 通过代理机制实现延迟加载,提高应用程序的性能和响应速度。
    • Microsoft.EntityFrameworkCore.SqlServer

    • 功能: 提供了针对 Microsoft SQL Server 的数据库提供程序。这使得 Entity Framework Core 能够与 SQL Server 数据库进行交互。

    • 作用: 为 SQL Server 数据库提供特定的数据库提供程序,确保 EF Core 能够正确地与 SQL Server 交互。
    • Microsoft.EntityFrameworkCore.Tools

    • 功能: 提供了 Entity Framework Core 工具,用于 NuGet Package Manager Console 中的 Visual Studio。

    • 作用: 通过 Visual Studio 的 NuGet Package Manager Console 提供 EF Core 工具,帮助开发者更方便地管理和使用 EF Core 的设计时工具。

新增数据上下文:

```
public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions options)
    : base(options)
    {
    }
    public DbSet Tenants { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity().HasData(
            new Tenant { Id = 1, Identifier = "tenant1", Name = "Tenant 1", ConnectionString = "Server=(localdb)\mssqllocaldb;Database=TenantDb1;Trusted_Connection=True;" },
            new Tenant { Id = 2, Identifier = "tenant2", Name = "Tenant 2", ConnectionString = "Server=(localdb)\mssqllocaldb;Database=TenantDb2;Trusted_Connection=True;" }
        );
    }
}
```

注意实际应用中上面的数据库连接字符串根据你的具体情况变动一下,因为本例子没有用到这个字段所以想要尝试可以不用改。

try_MultiTenantApi:

引入nuget包:

多租户解析与Demo

新建类TenantService:

```
public interface ITenantService
{
    Task GetTenantByIdentifierAsync(string identifier);
}

public class TenantService : ITenantService
{
    private readonly ApplicationDbContext _context;

    public TenantService(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task GetTenantByIdentifierAsync(string identifier)
    {
        return await _context.Tenants.FirstOrDefaultAsync(t => t.Identifier == identifier);
    }
}
```

新建一个中间件TenantMiddleware:

用于每次的请求解析出租户信息

```
namespace try_MultiTenantApi
{
    public class TenantMiddleware
    {
        private readonly RequestDelegate _next;

        public TenantMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context, ITenantService tenantService)
        {
            var subdomain = GetSubdomain(context.Request.Host.Value);
            if (!string.IsNullOrEmpty(subdomain))
            {
                var tenant = await tenantService.GetTenantByIdentifierAsync(subdomain);
                if (tenant != null)
                {
                    context.Items["Tenant"] = tenant;
                }
            }

            await _next(context);
        }

        private string GetSubdomain(string host)
        {
            var parts = host.Split('.');
            return parts.Length > 2 ? parts[0] : null;
        }
    }
}

public static class TenantMiddlewareExtensions
{
    public static IApplicationBuilder UseTenantMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware();
    }
}
```

配置appsetting文件,配置租户数据库连接字符串:

```
"ConnectionStrings": {
  "DefaultConnection": "Data Source=localhost;Initial Catalog=try_MultiTenant;User ID=sa;Password=******;Encrypt=False;"
},
```

修改Program:

```
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}


app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthorization();

app.UseTenantMiddleware(); // 使用租户解析中间件
app.MapControllers();

app.Run();
```

新建一个controller:ValuesController

```
[ApiController]
[Route("[controller]")]
public class ValuesController : ControllerBase
{
    private readonly ITenantService _tenantService;

    public ValuesController(ITenantService tenantService)
    {
        _tenantService = tenantService;
    }

    [HttpGet(nameof(Get1))]
    public async Task Get1()
    {
        var tenant = HttpContext.Items["Tenant"] as Tenant;
        if (tenant == null)
        {
            return NotFound("Tenant not found");
        }

        return Ok(new { Message = $"Hello from {tenant.Name}" });
    }
}
```

OK,至此项目就编码完成,接下来就是进行数据迁移。因为前面上下文中已经设置了出事测试数据

add-migration init

update-database

到此,租户的数据库已经创建,并且也有了初始的测试数据:

多租户解析与Demo

为了测试方便咱们在launch启动文件中配置两个租户的访问地址:

```
"profiles": {
  "http": {
    "commandName": "Project",
    "dotnetRunMessages": true,
    "launchBrowser": true,
    "launchUrl": "swagger",
    "applicationUrl": "http://localhost:5252;http://tenant1.Paas.JBWL.com:5252",
    "environmentVariables": {
      "ASPNETCORE_ENVIRONMENT": "Development"
    }
  },
}
```

接下来设置try_MultiTenantApi项目为启动项目,启来后出swagger页面:

多租户解析与Demo

页面地址是默认第一个http://localhost:5252/

下面我们就可以修改一下本地的host文件映射两个域名,可以使用swichhost,也可以找到本地的host文件,增加地址映射

```
127.0.0.1 tenant1.Paas.JBWL.com
127.0.0.1 tenant2.Paas.JBWL.com
```

启用后访问

```
tenant1.Paas.JBWL.com:5252
```

出现前面一模一样的页面,访问一下测试接口看是否解析成功:

多租户解析与Demo

成功!

接下来试一下tenant2.paas.jbwl.com:5252

多租户解析与Demo

至此,这个测试demo也就完成了,只要解析出租户信息,接下来Saas的租户数据就能获取了。

文章整理自互联网,只做测试使用。发布者:Lomu,转转请注明出处:https://www.it1024doc.com/5162.html

(0)
LomuLomu
上一篇 2025 年 1 月 1 日 上午10:39
下一篇 2025 年 1 月 1 日 上午11:40

相关推荐

  • Python数据结构与算法分析 第3版PDF、EPUB免费下载

    适读人群 :1. 希望学习数据结构和算法的Python用户; 2. 计算机专业的学生和老师。 只有洞彻数据结构与算法,才能真正精通Python!热门计算机科学教材,华盛顿大学、北京大学等多家高校采用,让你在代码编写的战场上所向披靡! 电子版仅供预览,下载后24小时内务必删除,支持正版,喜欢的请购买正版书籍 点击原文去下载 书籍信息 作者: [美] 布拉德利·…

    2025 年 1 月 7 日
    10800
  • MySQL 面试题

    MySQL 中有哪几种锁? 全局锁、行级锁、自增锁、记录锁、外键锁、间隙锁、表级锁、元数据锁、意向锁、临键锁 MySQL 中有哪些不同的表格? 基础表、临时表、系统表、信息表、性能模式表、分区表、外键表、触发器使用的表、存储过程和函数使用的表 简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别? 事务支持 InnoDB:支持事务处理,具有提…

    未分类 2025 年 1 月 12 日
    16900
  • 数据库审计与监控

    — title: 数据库审计与监控 date: 2024/12/20 updated: 2024/12/20 author: cmdragon excerpt: 数据库审计与监控是维护数据库安全性和性能的基石。通过精心设计的审计策略,我们可以跟踪用户行为,监控数据的访问与变更,及时发现潜在的安全风险和性能问题。本文将探讨数据库审计的基础理论、审计策略和监…

    未分类 2024 年 12 月 27 日
    15700
  • 架构-初识BFF

    引言 晚上公司开了一个技术分享会,主要内容就是公司的项目架构,会中讲解了项目整体架构是BFF架构,就是在微服务之上多加了一层。 除此之外,还讲解了DDD设计思想,主要用于各个业务中台,如订单中台、用户中台等。 这是我的架构第一课,听得有些似懂非懂,于是浅浅地整理一下。 BFF 是什么 BFF是服务于前端的后端,全称Backend For Frontend。B…

    2024 年 12 月 29 日
    13500
  • Java刷题训练第一期

    个人主页:手握风云 专栏:Java刷题训练营 1. 字符转ASCII码 问题描述:BoBo教KiKi字符常量或字符变量表示的字符在内存中以ASCII码形式存储。BoBo出了一个问题给KiKi,输入一个字符,输出该字符相应的ASCII码。 输入描述:一行,一个字符。 输出描述:一行,输出输入字符对应的ASCII码。 算法分析:在Java当中,没有针对字符的输入…

    2024 年 12 月 31 日
    16600

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信