在当今的云计算环境中,多租户应用变得越来越普遍。这种应用模式允许多个客户共享相同的应用程序实例,但每个客户只能访问自己的数据。确保客户数据不被其他客户或潜在竞争对手看到,是多租户应用设计中的一个重要考虑因素。本文将探讨几种实现多租户的方法,并讨论如何在Blazor应用中使用Entity Framework Core来实现这些方法。
实现多租户的方法有很多,但最常见的两种方法是:为每个客户保留一个单独的数据库,或者在现有数据库中按客户划分数据。这两种方法都得到了Entity Framework Core的支持。
对于使用多个数据库的方法,切换到正确的数据库就像提供正确的连接字符串一样简单。如果数据存储在单个数据库中,使用全局查询过滤器可以确保开发者不会意外编写可以访问其他客户数据的代码。
如果选择在单个数据库中存储所有租户的数据,那么使用查询过滤器是一个合理的选择。这样可以确保每个请求都过滤到租户。
在Blazor应用中使用Entity Framework Core的推荐模式是注册DbContextFactory,然后调用它来为每次操作创建新的DbContext实例。默认情况下,工厂是单例的,这意味着整个应用程序的所有用户都共享一个工厂实例。
在Blazor WebAssembly应用中,单例是针对用户的,因此不会出现问题。但是,Blazor Server应用则面临独特的挑战。尽管它是一个Web应用,但它通过SignalR进行实时通信来“保持活动”。为每个用户创建一个会话,并在初始请求之后持续存在。
对于BlazorServer应用,需要为每个用户会话提供一个新的工厂实例,以便允许新的设置。这种特殊工厂的生命周期被称为Scoped,它为每个用户会话创建一个新的实例。
为了演示BlazorServer应用中的多租户,构建了一个示例应用JeremyLikness/BlazorEFCoreMultitenant。数据库包含存储方法名称和参数的表,这些数据是通过反射填充的。
public class DataParameter
{
public DataParameter() { }
public DataParameter(ParameterInfo parameter)
{
Name = parameter.Name;
Type = parameter.ParameterType.FullName;
}
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public DataMethod Method { get; set; }
}
public class DataMethod
{
public DataMethod() { }
public DataMethod(MethodInfo method)
{
Name = method.Name;
ReturnType = method.ReturnType.FullName;
ParentType = method.DeclaringType.FullName;
Parameters = method.GetParameters()
.Select(p => new DataParameter(p))
.ToList();
foreach (var parameter in Parameters)
{
parameter.Method = this;
}
}
public int Id { get; set; }
public string Name { get; set; }
public string ReturnType { get; set; }
public string ParentType { get; set; }
public IList Parameters { get; set; } = new List();
}
TenantProvider类用于设置用户当前的租户,并在租户更改时提供回调。
DbContext可以管理多租户,具体方法取决于数据库策略。如果选择在单个数据库中存储所有租户的数据,那么可能需要使用查询过滤器。TenantProvider通过依赖注入传递给构造函数,并用于解析和存储租户标识符。
TenantProvider和DbContextFactory在应用程序启动时进行配置。
services.AddScoped();
services.AddDbContextFactory<SingleDbContext>(
opts => opts.UseSqlite(
"Data Source=alltenants.sqlite"
),
ServiceLifetime.Scoped);
MultipleDbContext版本通过为每个租户传递不同的连接字符串来实现。这可以在启动时通过解析服务提供程序并使用它来构建连接字符串来配置。