2

我目前正在开发一个采用共享数据库/共享模式方法的多租户应用程序。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 的一些技术债务。我对更好的实施可能有一些想法,但是如果有人有任何可以分享的指导或经验教训,我将非常感激。

4

1 回答 1

0

CurrentUser 并不是一个单身人士。我不确定你会怎么称呼它。(根据定义,一个单例一次只能存在一个,并且可以由外部代码随意创建任意数量的 UserIdentity 实例,并且可以毫无问题地共存。)

就个人而言,我会采用 CurrentUser.Instance 并将其移动到 UserIdentity.CurrentUser,或者将它与您拥有的任何类似的“获取全局实例”方法和属性放在一起。至少摆脱 CurrentUser 类。当你在它的时候,让属性在同一个地方可设置——它已经是可设置的了,如果两个类没有紧挨着显示,(1)看起来就像魔术一样,(2 ) 使更改当前用户身份的方式变得更加困难。

不会摆脱全局,但如果不将 UserIdentity 传递给每个需要它的函数,你就不会真正解决这个问题。

于 2010-05-29T03:23:52.830 回答