我目前正在开发一个采用共享数据库/共享模式方法的多租户应用程序。IOW,我们通过在所有表上定义TenantID列来强制租户数据隔离。按照惯例,所有 SQL 读取/写入都必须包含Where TenantID = '?' 条款。不是一个理想的解决方案,但事后看来是 20/20。
无论如何,由于我们应用程序中的几乎每个页面/工作流程都必须显示特定于租户的数据,因此我在项目开始时做出了(糟糕的)决定,即使用单例来封装当前用户凭据(即 TenantID 和 UserID)。我当时的想法是,我不想为我的数据层中的每个方法签名添加一个 TenantID 参数。
这是基本伪代码的样子:
public class UserIdentity
{
public UserIdentity(int tenantID, int userID)
{
TenantID = tenantID;
UserID = userID;
}
public int TenantID { get; private set; }
public int UserID { get; private set; }
}
public class AuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest +=
new EventHandler(context_AuthenticateRequest);
}
private void context_AuthenticateRequest(object sender, EventArgs e)
{
var userIdentity = _authenticationService.AuthenticateUser(sender);
if (userIdentity == null)
{
//authentication failed, so redirect to login page, etc
}
else
{
//put the userIdentity into the HttpContext object so that
//its only valid for the lifetime of a single request
HttpContext.Current.Items["UserIdentity"] = userIdentity;
}
}
}
public static class CurrentUser
{
public static UserIdentity Instance
{
get { return HttpContext.Current.Items["UserIdentity"]; }
}
}
public class WidgetRepository: IWidgetRepository{
public IEnumerable<Widget> ListWidgets(){
var tenantId = CurrentUser.Instance.TenantID;
//call sproc with tenantId parameter
}
}
如您所见,这里有几种代码异味。这是一个单例,所以它已经不是单元测试友好的。最重要的是,您在 CurrentUser 和 HttpContext 对象之间有一个非常紧密的耦合。通过扩展,这也意味着我在我的数据层中引用了 System.Web(不寒而栗)。
由于上述原因,我想通过摆脱这个单例来偿还这个 sprint 的一些技术债务。我对更好的实施可能有一些想法,但是如果有人有任何可以分享的指导或经验教训,我将非常感激。