ASP.NET Core + SaasKit + Sql Server 的多租户应用程序架构示例

本文演示如何使用ASP.NETCore和SaasKit创建一个多租户应用程序,每个租户通过不同的域名连接。通过添加数据库模型、配置数据库上下文、安装必要的NuGet包以及实现租户解析器,实现了对不同租户数据的隔离。最后,展示了如何在视图中展示租户特定的内容。
摘要由CSDN通过智能技术生成

我们 demo 应用程序的每个租户都将通过不同的域名进行连接。
ASP.NET Core 将检查传入请求并在 tenants 表中查找域。
您还可以按子域(或您想要的任何其他 scheme)查找租户。

注意 tenant_id 是如何存储在 questions 表中的(通过外键关联)。

ASP.NET Core 项目

使用 dotnet new 从 MVC 模板创建一个新项目:

  1. dotnet new mvc -o QuestionExchange

  2. cd QuestionExchange

首先使用NuGet安装下面的包:

Microsoft.EntityFrameworkCore.SqlServe

Microsoft.EntityFrameworkCore.Tools

SaasKit.Multitenancy

在appsettings.json添加如下的配置

打开 Startup.cs 文件并将这些行添加到 ConfigureServices 方法中:
services.AddEntityFrameworkSqlServer()
                .AddDbContext<AppDbContext>(options => 
                options.UseSqlServer(Configuration.GetConnectionString("SaasKitContext"))); 

添加 Tenancy(租赁) 到 App

定义 Entity Framework Core 上下文和模型

在 Models 目录中创建一个 Tenant.cs 文件:

namespace QuestionExchange.Models
{
    public class Tenant
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [Required]
        public string Domain { get; set; }
        [Required]
        public string Name { get; set; }
        public string Description { get; set; }

        public DateTimeOffset CreatedAt { get; set; }

        public DateTimeOffset UpdatedAt { get; set; }
    }
}

紧接着创建一个 Question.cs 文件:

namespace QuestionExchange.Models
{
    public class Question
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        public Tenant Tenant { get; set; }

        public string Title { get; set; }

        public int Votes { get; set; }

        public DateTimeOffset CreatedAt { get; set; }
        public DateTimeOffset UpdatedAt { get; set; }
    }
}

接下来,您需要定义一个数据库上下文。在Models文件夹下创建一个名为 AppDbContext.cs 的文件,并添加以下代码:

namespace QuestionExchange.Models
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) 
        { 
        }
    
        public DbSet<Tenant> Tenants { get; set; }
        public DbSet<Question> Questions { get; set; }

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

然后打开程序包控制台,执行下面的命令:

Add-Migration Init

update-database     

  我们看到Sql Server中生成了如下的数据库和表: 

  

 我们分别在Tenants、Questions表中添加如下数据:

INSERT INTO tenants VALUES (
    'bufferoverflow.local',
    'Buffer Overflow',
    'Ask anything code-related!',
    getdate(),
    getdate());

INSERT INTO tenants VALUES (
    'dboverflow.local',
    'Database Questions',
    'Figure out why your connection string is broken.',
    getdate(),
    getdate());

    INSERT INTO questions VALUES (
        1,
        'How do you build apps in ASP.NET Core?',
        1,
        getdate(),
        getdate());
 
    INSERT INTO questions VALUES (
        2,
        'Using postgresql for multitenant data?',
        2,
        getdate(),
        getdate());

这样就完成了数据库结构和示例数据和ASP.NET Core设置 ,下一步是向 ASP.NET Core 管道添加多租户支持。

安装 SaasKit

SaasKit 是一款优秀的开源 ASP.NET Core 中间件。
该软件包使您的 Startup 请求管道 租户感知(tenant-aware) 变得容易,
并且足够灵活以处理许多不同的多租户用例。

使用NuGet安装下面的包:

SaasKit.Multitenancy

SaasKit 需要两件事才能工作:租户模型(tenant model)和租户解析器(tenant resolver)。

您已经有了前者(您之前创建的 Tenant 类),因此在项目根目录中创建一个名为 CachingTenantResolver.cs 的新文件:

namespace QuestionExchange
{
    public class CachingTenantResolver : MemoryCacheTenantResolver<Tenant>
    {
        private readonly AppDbContext _context;

        public CachingTenantResolver(AppDbContext context, IMemoryCache cache, ILoggerFactory loggerFactory)
            :base(cache, loggerFactory) 
        {
            _context = context;
        }

        protected override async Task<TenantContext<Tenant>> ResolveAsync(HttpContext context)
        {
            var subdomain = context.Request.Host.Host.ToLower();

            var tenant = await _context.Tenants
                .FirstOrDefaultAsync(t => t.Domain == subdomain);

            if (tenant == null) return null;

            return new TenantContext<Tenant>(tenant);
        }

       protected override MemoryCacheEntryOptions CreateCacheEntryOptions()
            => new MemoryCacheEntryOptions().SetSize(1024).SetAbsoluteExpiration(TimeSpan.FromHours(2));

        protected override string GetContextIdentifier(HttpContext context)
            => context.Request.Host.Host.ToLower();

        protected override IEnumerable<string> GetTenantIdentifiers(TenantContext<Tenant> context)
            => new string[] { context.Tenant.Domain };
    }
}

准备好租户模型(tenant model)和租户解析器(tenant resolver)后,在 ConfigureServices 方法中添加下面的代码:

services.AddMultitenancy<Tenant, CachingTenantResolver>();

接下来,将此行添加到 Configure 方法中,在 UseStaticFiles 下方但在 UseRouting上方

Configure 方法代表您的实际请求管道,因此顺序很重要!

更新视图

现在所有部分都已就绪,您可以开始在代码和视图中引用当前租户。

在 Models 目录中创建 QuestionListViewModel.cs 文件:

namespace QuestionExchange.Models
{
    public class QuestionListViewModel
    {
        public IEnumerable<Question> Questions { get; set; }
    }
}


打开 Views/Home/Index.cshtml 视图并用这个标记替换整个文件:

@inject Tenant Tenant
@model QuestionListViewModel
 
@{
    ViewData["Title"] = "Home Page";
}
 
<div class="row">
    <div class="col-md-12">
        <h1>Welcome to <strong>@Tenant.Name</strong></h1>
        <h3>@Tenant.Description</h3>
    </div>
</div>
 
<div class="row">
    <div class="col-md-12">
        <h4>Popular questions</h4>
        <ul>
            @foreach (var question in Model.Questions)
            {
                <li>@question.Title</li>
            }
        </ul>
    </div>
</div>

@inject 指令从 SaasKit 获取当前租户,并且@model 指令告诉 ASP.NET Core,此视图将由模型类QuestionListViewModel支持。

查询数据库

HomeController 负责渲染您刚刚编辑的索引视图。打开它并用下面的代码替换构造函数和Index() 方法:

   public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private readonly AppDbContext _context;
        private readonly Tenant _currentTenant;


        public HomeController(ILogger<HomeController> logger, AppDbContext context, Tenant currentTenant)
        {
            _logger = logger;
            _context = context;
            _currentTenant = currentTenant;
        }

        public async Task<IActionResult> Index()
        {
            var topQuestions = await _context.Questions.Where(q => q.Tenant.Id == _currentTenant.Id)
                .OrderByDescending(q => q.UpdatedAt)
                .Take(5).ToArrayAsync();

            var viewModel = new QuestionListViewModel
            {
                Questions = topQuestions
            };

            return View(viewModel);
        }

测试应用程序

您添加到数据库的测试租户与(fake)域 bufferoverflow.local 和 dboverflow.local 相关联。

首先需要修改本地Hosts文件,添加:

  127.0.0.1 bufferoverflow.local

  127.0.0.1 dboverflow.local

运行cmd(命令行),输入以下命令,刷新DNS:

  ipconfig /flushdns

使用 dotnet run 或单击 Visual Studio 中的 Start 启动当前项目,
应用程序将开始侦听 localhost:5000 之类的 URL。

如果您直接访问该 URL,您将看到一个错误,因为您尚未设置任何 默认租户行为

访问 http://bufferoverflow.local:5000,  http://dboverflow.local:5000 可以看到之前插入的测试数据在不同租户下显示不同。


添加更多租户现在只需在 tenants 表中添加更多行即可。

                          

                      

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值