多租户解析与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/4895.html

(0)
LomuLomu
上一篇 2024 年 12 月 30 日 上午9:21
下一篇 2024 年 12 月 30 日 上午10:22

相关推荐

  • 【GreatSQL优化器-10】find_best_ref

    【GreatSQL优化器-10】find_best_ref 一、find_best_ref介绍 GreatSQL的优化器对于join的表需要根据行数和cost来确定最后哪张表先执行哪张表后执行,这里面就涉及到预估满足条件的表数据,在keyuse_array数组有值的情况下,会用find_best_ref函数来通过索引进行cost和rows的估计,并且会找出最…

    2025 年 1 月 16 日
    59500
  • 10个案例告诉你mysql不使用子查询的原因

    大家好,我是 V 哥,上周跟一个哥们吃饭,技术人在一起,你知道的,没聊上一会儿,就转到技术问题探讨上了,其中聊到数据库子查询的问题印象深刻,回来整理了以下10个案例说明不使用子查询的问题,分享给大家。 首先,来说一下在MySQL中,不推荐使用子查询和JOIN的原因,主要有以下几点: 性能问题 :子查询在执行时,MySQL需要创建临时表来存储内层查询的结果,查…

    未分类 2024 年 12 月 30 日
    44100
  • Redis Java 集成到 Spring Boot

    # Hi~!这里是奋斗的明志,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎~~ ## 📚 本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为展示我的学习过程及理解。文笔、排版拙劣,望见谅。 ![在这里插入图片描述](https://pic.it1024doc.com/csdn/202412/ca054e533844dc927c24e57c1713f…

    2024 年 12 月 27 日
    51000
  • 促销系统:促销活动、优惠券、优惠规则概念模型设计

    大家好,我是汤师爷~ 概念模型设计是促销系统开发的关键环节,我们需要基于之前的功能分析,将复杂的促销业务拆解成清晰的领域概念,这些概念之间的关系界定和边界划分,将直接决定系统的可维护性和扩展性。 促销系统核心概念模型 通过对促销业务的分析,我们可以抽象出促销系统的关键概念模型。 1、促销活动模型 促销活动模型对活动的各个要素和规则进行抽象,包含活动名称、描述…

    2025 年 1 月 11 日
    43500
  • 最新IDEA 2024激活破解教程,附IDEA激活码

    引言 在现代软件开发中,集成开发环境(IDE)是不可或缺的工具,特别是对于Java程序员来说,选择一个高效、智能的IDE至关重要。IntelliJ IDEA作为JetBrains公司推出的旗舰产品,以其强大的功能和卓越的用户体验,成为众多Java开发者的首选。 给大家整理了IDEA永久激活码,IDEA破解教程,需要的直接浏览器访问: docs.qq.com/…

    未分类 2024 年 7 月 31 日
    3.5K00

发表回复

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

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

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

关注微信