Asp.net mvc 多租户访问控制:存储库还是服务层?

Asp.net mvc 多租户访问控制:存储库还是服务层?,asp.net-mvc,repository-pattern,multi-tenant,access-control,Asp.net Mvc,Repository Pattern,Multi Tenant,Access Control,在基于Rob Connery的MVC店面的多租户ASP.NET MVC应用程序中,我应该在存储库或服务层中过滤租户的数据吗 1.筛选存储库中租户的数据: 公共接口存储库 { IQueryable GetJobs(短期租约); } 2.让服务按租户筛选存储库数据: 公共接口IJobService { IList GetJobs(短期租约); } 我的直觉告诉我应该在服务层(选项2)中这样做,但可以说每个租户本质上都应该拥有自己的“虚拟存储库”(选项1),而这一责任在于存储库 哪种方法最优雅:

在基于Rob Connery的MVC店面的多租户ASP.NET MVC应用程序中,我应该在存储库服务层中过滤租户的数据吗

1.筛选存储库中租户的数据:
公共接口存储库
{
IQueryable GetJobs(短期租约);
}
2.让服务按租户筛选存储库数据:
公共接口IJobService
{
IList GetJobs(短期租约);
}
我的直觉告诉我应该在服务层(选项2)中这样做,但可以说每个租户本质上都应该拥有自己的“虚拟存储库”(选项1),而这一责任在于存储库

  • 哪种方法最优雅:选项1、选项2还是有更好的方法

更新: 我尝试了在存储库中进行过滤的想法,但问题是我的应用程序提供了租户上下文(通过子域),并且只与服务层交互。将上下文一直传递到存储库层是一项任务

因此,我选择在服务层过滤数据。我觉得存储库应该使用适当的过滤器来表示存储库中物理上可用的所有数据,以便检索特定于租户的数据,供服务层使用

最后更新:
由于不必要的复杂性,我最终放弃了这种方法。请参阅下面我的答案。

@FreshCode,我们在存储库中执行此操作,并且不将租户作为参数传递。我们采用以下方法:

public IQueryable<Job> GetJobs()
{
    return _db.Jobs.Where(j=>j.TenantId == Context.TenantId);
}
public IQueryable GetJobs()
{
返回_db.Jobs.Where(j=>j.TenantId==Context.TenantId);
}
上下文是存储库的一个依赖项,它是在BeginRequest中创建的,例如,您可以根据url确定租户。 我认为通过这种方式,它是非常透明的,您可以避免
tenantId
参数,它可能会变得有点令人不安


问候。

更新:不采用多租户方法会让我花费数百小时的技术债务。四年过去了,我希望我能先花时间实施一个干净的租户方法。不要犯同样的错误


陈旧过时的答案: 我最终剥离了所有多租户代码,支持为每个租户使用单独的应用程序和数据库。在我的情况下,很少有租户不经常变动,所以我可以这样做

我所有的控制器、会员资格提供商、角色提供商、服务和存储库都趋向于重复
。使用遍布各地的TenantId(…)
代码,这让我意识到我实际上不需要一个
用户
表来访问99%特定于一个租户的数据,因此,使用单独的应用程序更有意义,并且使一切变得更加简单


谢谢你的回答-他们让我意识到我需要重新设计。

+1谢谢你的快速回答。我猜上下文只包含一个租户。你能举一个你的
TenantContext
实现的例子吗?您是否在您的repository constructor中实例化它?是的,它是一个只有TenantId的接口,但它可能包含其他内容,如用户ID等。我们使用structuremap并通过存储库的构造函数注入上下文的实现。太棒了,我是DI新手,但也在使用StructureMap。您是将
TenantContext
实例化为存储库构造函数的参数,还是使用
var TenantContext=ObjectFactory.GetInstance()在构造函数内部?试图找出最佳实践。我们使用构造函数注入,因为在这种情况下,租户上下文是类依赖。让我推荐你一本关于这个话题的书,因为它值得一读。因为它使我不必问几乎完全相同的问题。。。。但是,需要每次应用程序和数据库的每一个实例都需要更新,每次你想添加一个特性,修复一个bug等等。对于其他人来说,这可能是@新代码的情况下的正确方法,但是在遵循相同的路径之前要考虑这些缺点。(我很想听听FreshCode的应用程序在这个问题发布后的过去几年里是如何发展的)是的,这个应用程序已经发展壮大了,后来这个决定让我很恼火。不可避免地,许多用户和大量数据必须在“租户”之间共享,现在存在大量库存问题。然而,这个决定可能是当时最实际的。感谢@FreshCode的非常诚实的更新,我相信这将有助于指导其他人。你能不能不为每个租户使用单独的数据库?在运行时,根据登录用户使用基于租户的连接字符串实例化DbContext,并将其注入存储库。。。
public interface IJobService
{
    IList<Job> GetJobs(short tenantId);
}
public IQueryable<Job> GetJobs()
{
    return _db.Jobs.Where(j=>j.TenantId == Context.TenantId);
}