在ASP.NET Core上實施每個租戶策略的數據庫
不定時更新翻譯系列,此系列更新毫無時間規律,文筆菜翻譯菜求各位看官老爺們輕噴,如覺得我翻譯有問題請挪步原博客地址
本博文翻譯自:
http://gunnarpeipman.com/2017/08/database-per-tenant/
讓我們繼續使用ASP.NET Core web應用程序中的多租戶,並關注每個租戶都有自己的數據庫的解決方案。它不僅僅是關於數據庫的——可以有更多的服務,每個租戶都有自己的實例。它使這里提供的解決方案可以輕松地擴展到除SQL Server或其他任何關系數據庫之外的其他服務。
以往的工作
在討論解決方案之前,我建議先瀏覽一下我以前的文章,內容包括ASP.NET Core web應用程序中的多租戶的一些方面:
- [Entity Framework Core 2.0 全局查詢過濾器](http://www.cnblogs.com/chen-jie/p/ef-core-global-query-filters.html"Global query filters in Entity Framework Core 2.0")
- 在 ASP.NET Core 中執行租戶服務
注意! 本文中的代碼建立在上述文章的代碼之上。
將數據庫連接字符串移動到租戶配置
這里的問題是如何動態決定使用哪個連接字符串,以及如何使用web應用程序配置未更改的方式使連接字符串可用。后者實際上是在我之前的多租戶文章中解決的在 ASP.NET Core 中執行租戶服務我建議使用我此篇文章中提到的BlobStorageTenantProvider
與前一篇文章不同的是,當租戶使用不同的數據庫時,在這些數據庫中不需要租戶id。不支持軟刪除的應用程序可以在使用本文建議的解決方案時使用經典的簡單數據上下文。
新的租戶類有一個額外的屬性- DatabaseConnectionString
,如這里所示。
public class Tenant
{
public int Id { get; set; }
public string Name { get; set; }
public string Host { get; set; }
public string DatabaseConnectionString { get; set; }
}
在Azure blob存儲中保存的租戶配置文件將是這樣的。
[
{
"Id": 2,
"Name": "Local host",
"Host": "localhost:30172",
"DatabaseConnectionString": "<connection string 1>"
},
{
"Id": 3,
"Name": "Customer X",
"Host": "localhost:3331",
"DatabaseConnectionString": "<connection string 2>"
},
{
"Id": 4,
"Name": "Customer Y",
"Host": "localhost:33111",
"DatabaseConnectionString": "<connection string 3>"
}
]
返回租戶而不是租戶ID
由於租戶可以定義更多的設置,所以開始處理租戶類而不是租戶ID。它將改變我的ITenantProvider接口和BlobStorageTenantProvider類。
public class BlobStorageTenantProvider : ITenantProvider
{
private static IList<Tenant> _tenants;
private Tenant _tenant;
public BlobStorageTenantProvider(IHttpContextAccessor accessor, IConfiguration conf)
{
if(_tenants == null)
{
LoadTenants(conf["StorageConnectionString"], conf["TenantsContainerName"], conf["TenantsBlobName"]);
}
var host = accessor.HttpContext.Request.Host.Value;
var tenant = _tenants.FirstOrDefault(t => t.Host.ToLower() == host.ToLower());
if(tenant != null)
{
_tenant = tenant;
}
}
private void LoadTenants(string connStr, string containerName, string blobName)
{
var storageAccount = CloudStorageAccount.Parse(connStr);
var blobClient = storageAccount.CreateCloudBlobClient();
var container = blobClient.GetContainerReference(containerName);
var blob = container.GetBlobReference(blobName);
blob.FetchAttributesAsync().GetAwaiter().GetResult();
var fileBytes = new byte[blob.Properties.Length];
using (var stream = blob.OpenReadAsync().GetAwaiter().GetResult())
using (var textReader = new StreamReader(stream))
using (var reader = new JsonTextReader(textReader))
{
_tenants = JsonSerializer.Create().Deserialize<List<Tenant>>(reader);
}
}
public Tenant GetTenant()
{
return _tenant;
}
}
動態配置數據上下文
我想在這里,多租戶應用程序不處理軟刪除。現在,為了使用正確的連接字符串,必須修改mult-tenant應用程序的默認數據上下文。
public class PlaylistContext : DbContext
{
private readonly Tenant _tenant;
public DbSet<Playlist> Playlists { get; set; }
public DbSet<Song> Songs { get; set; }
public PlaylistContext(DbContextOptions<PlaylistContext> 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.Entity<Playlist>().HasKey(e => e.Id);
modelBuilder.Entity<Song>().HasKey(e => e.Id);
base.OnModelCreating(modelBuilder);
}
}
在查看代碼時,很容易看出沒有多少代碼,但它會產生大膽靈活的模型,去支持不同的租戶策略。
結束
ASP.NET Core的依賴注入模型和Entity Framework Core的靈活性使得在ASP.NET Core web應用程序中支持更復雜的場景變得非常容易。這篇博文關注的是多租戶的一個方面——如何支持每個租戶數據存儲策略的數據庫。如果需要的話,這個模型也可以擴展到更多的設置。
歡迎轉載,轉載請注明翻譯原文出處(本文章),原文出處(原博客地址),然后謝謝觀看
如果覺得我的翻譯對您有幫助,請點擊推薦支持:)