1

我正在构建一个多租户 MVC 应用程序,其中有一个应用程序池和一个数据库。我有一个 Tenant 表,我的每个模型都有一个 TenantId 标识。

每个租户都有一个字符串“Url”,用于标识用于访问该租户数据的完整 URL。

我可以使用以下(粗略的近似)从我的 BaseController 访问它:

HttpRequest request = HttpContext.Current.Request;
Uri requestUrl = request.Url;
_tenant = _tenantService.GetTenantByUrl(requestUrl);

现在,我需要将租户传递到服务层以执行业务逻辑。我可以做到这一点的一种方法是遍历所有服务(约 200 个方法)中的每一个方法并添加一个租户参数。我必须触及对服务层的每个调用,以及每个服务层方法。这会起作用,但它很乏味并且混淆了代码。

例如,我之前的一种方法:

    public void DeleteUserById(int userId)
    {
        using (var db = CreateContext())
        {
            var user = db.Users.FirstOrDefault(u => u.UserId.Equals(userId));
            InternalDeleteUser(db, user);
        }
    }

之后(如果我通过租户):

    public void DeleteUserById(Tenant tenant, int userId)
    {
        using (var db = CreateContext())
        {
            var user = tenant.Users.FirstOrDefault(u => u.UserId.Equals(userId));
            InternalDeleteUser(db, user);
        }
    }

我想要实现的目标(通过从我的 BaseController 设置租户,上一层):

    public void DeleteUserById(int userId)
    {
        using (var db = CreateContext())
        {
            var user = _tenant.Users.FirstOrDefault(u => u.UserId.Equals(userId));
            InternalDeleteUser(db, user);
        }
    }

有什么方法可以使用我的 BaseService (所有其他服务都继承自此)或其他模式从控制器定义租户,并让服务方法将其拾取,而不将其作为参数传递给每个人?这样我只需要触摸基本控制器(或者甚至可能是 global.asax),而不需要其他任何东西。

简而言之:如何通过从 MVC 控制器定义对象来使所有服务都可以访问该对象,而不将其直接传递给服务?

4

2 回答 2

3

我猜您所说的拥有基础服务(请参阅Layer Supertype)是有道理的。该基类将依赖于在同一服务层中定义的接口(例如 IUserSession、IContext 或其他),并且该接口将具有将返回您的租户的方法或属性。

此接口的实现将驻留在您的 Web 应用程序中,它会按照您的描述执行操作,从HttpContext.

如果您有一个后台进程、控制台应用程序或任何不在 Web 上下文中运行的东西,您将有一个不同的实现,它将根据您想要的任何其他标准创建租户。

总而言之,您将在服务层中拥有:

abstract class BaseService 
{    
    protected IContext Context {get; private set;}

    public BaseService(IContext context)
    {
            Context = context;
    }
}

public interface IContext
{
    Tenant GetTenant();
}

然后在您的网络层中,您将拥有:

public IWebContext : IContext
{
    public Tenant GetTenant()
    {
        //your code to return create the tenant based on the url.
    }
}

希望这可以帮助。

于 2013-11-06T12:26:42.383 回答
0

我也有同样的“问题”,因为我也在构建一个多租户应用程序。但是,我很简单地解决了这个问题,IMO:每个存储库/服务都定义了一个 TenantId 属性,在使用该服务时必须设置该属性。TenantId 是一个值对象,如果为 null,它将抛出。

现在,关键是任何存储库/服务都可以在请求之外使用,例如在后台线程或应用程序中。我正在使用消息驱动的方法,因此任何必需的信息(如租户 ID)都是消息的一部分,因此可供服务的使用者(消息处理程序)使用。另一个好处是可测试性。

我建议不要将您的服务与请求特定的对象(如 HttpContext、Session 或 Cache)耦合。

于 2013-11-03T21:45:11.287 回答