Csharp/C#教程:具有DbContext和TenantId的MultiTenancy – 拦截器,filter,EF代码优先分享


具有DbContext和TenantId的MultiTenancy – 拦截器,filter,EF代码优先

我的组织需要一个共享数据库,共享模式多租户数据库。 我们将根据TenantId进行查询。 我们将只有极少数租户(少于10个),并且所有租户都将共享相同的数据库架构,不支持特定于租户的更改或function。 租户元数据将存储在内存中,而不是存储在DB(静态成员)中。

这意味着所有实体现在都需要一个TenantId, DbContext需要知道默认情况下对此进行过滤。

TenantId可能会被标头值或原始域标识,除非有更明智的方法。

我已经看到各种样本利用拦截器,但没有看到TenantId实现的明确示例。


我们需要解决的问题:

  1. 我们如何修改当前架构来支持这个(简单的我想,只需添加TenantId)
  2. 我们如何检测租户(简单 – 基于原始请求的域或标头值 – 从BaseController拉出)
  3. 我们如何将它传播到服务方法(有点棘手…我们使用DI通过构造函数进行水合…希望避免使用tenantId对所有方法签名进行tenantId
  4. 一旦我们有了它,我们如何修改DbContext来过滤这个tenantId(不知道)
  5. 我们如何优化性能。 我们需要什么索引,我们如何确保查询缓存没有做任何与tenantId隔离等相关的时髦(不知道)
  6. 身份validation – 使用SimpleMembership,我们如何隔离User ,以某种方式将他们与租户联系起来。

我认为最大的问题是4 – 修改DbContext。


我喜欢本文如何利用RLS,但我不知道如何以代码优先,dbContext方式处理它:

https://azure.microsoft.com/en-us/documentation/articles/web-sites-dotnet-entity-framework-row-level-security/

我想说的是我想要的是一种方法 – 考虑到性能 – 使用DbContext有选择地查询tenantId隔离的资源,而不用我的调用"AND TenantId = 1"等。


更新 – 我找到了一些选项,但我不确定每个选项的优缺点是什么,或者是否有一些“更好”的方法。 我对选项的评估归结为:


方法A.

这似乎“昂贵”,因为每次我们新建dbContext时,我们都必须重新初始化filter:

Row level security in EntityFramework 6 (EF6)

首先,我设置了我的租户和界面:

 public static class Tenant { public static int TenantA { get { return 1; } } public static int TenantB { get { return 2; } } } public interface ITenantEntity { int TenantId { get; set; } } 

我在任何实体上实现该接口:

  public class Photo : ITenantEntity { public Photo() { DateProcessed = (DateTime) SqlDateTime.MinValue; } [Key] public int PhotoId { get; set; } [Required] public int TenantId { get; set; } } 

然后我更新我的DbContext实现:

  public AppContext(): base("name=ProductionConnection") { Init(); } protected internal virtual void Init() { this.InitializeDynamicFilters(); } int? _currentTenantId = null; public void SetTenantId(int? tenantId) { _currentTenantId = tenantId; this.SetFilterScopedParameterValue("TenantEntity", "tenantId", _currentTenantId); this.SetFilterGlobalParameterValue("TenantEntity", "tenantId", _currentTenantId); var test = this.GetFilterParameterValue("TenantEntity", "tenantId"); } public override int SaveChanges() { var createdEntries = GetCreatedEntries().ToList(); if (createdEntries.Any()) { foreach (var createdEntry in createdEntries) { var isTenantEntity = createdEntry.Entity as ITenantEntity; if (isTenantEntity != null && _currentTenantId != null) { isTenantEntity.TenantId = _currentTenantId.Value; } else { throw new InvalidOperationException("Tenant Id Not Specified"); } } } } private IEnumerable GetCreatedEntries() { var createdEntries = ChangeTracker.Entries().Where(V => EntityState.Added.HasFlag(V.State)); return createdEntries; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Filter("TenantEntity", (ITenantEntity tenantEntity, int? tenantId) => tenantEntity.TenantId == tenantId.Value, () => null); base.OnModelCreating(modelBuilder); } 

最后,在我对DbContext的调用中,我使用了这个:

  using (var db = new AppContext()) { db.SetTenantId(someValueDeterminedElsewhere); } 

我有一个问题,因为我在大约一百万个地方新建了我的AppContext(一些服务方法需要它,有些不需要) – 所以这会使我的代码膨胀一点。 还有关于租户确定的问题 – 我是否传入HttpContext,是否强制我的控制器将TenantId传递给所有服务方法调用,如何处理我没有原始域(webjob调用等)的情况。


方法B.

在这里找到: http : //howtoprogram.eu/question/na,28158

看似相似,但很简单:

  public interface IMultiTenantEntity { int TenantID { get; set; } } public partial class YourEntity : IMultiTenantEntity {} public partial class YourContext : DbContext { private int _tenantId; public override int SaveChanges() { var addedEntities = this.ChangeTracker.Entries().Where(c => c.State == EntityState.Added) .Select(c => c.Entity).OfType(); foreach (var entity in addedEntities) { entity.TenantID = _tenantId; } return base.SaveChanges(); } public IQueryable TenantCodes => this.Codes.Where(c => c.TenantID == _tenantId); } public IQueryable TenantYourEntities => this.YourEntities.Where(c => c.TenantID == _tenantId); 

虽然这看起来像A的愚蠢版本,但同样令人担忧。

我认为,到目前为止,必须有一个成熟的,可取的配置/架构来满足这种需求。 我们该怎么做呢?

我想建议以下方法,1。为每个包含核心业务数据的表创建一个名称为tenant ID的列,这对于任何映射表都不是必需的。

  1. 通过创建返回IQueryable的扩展方法,使用方法B. 此方法可以是dbset的扩展,以便任何编写过滤子句的人都可以调用此扩展方法,然后调用谓词。 这将使开发人员更轻松地编写代码,而无需担心租户IDfilter。 此特定方法将具有代码,以根据正在执行此查询的租户上下文为租户ID列应用过滤条件。

示例 ctx.TenantFilter().Where(....)

  1. 您可以在所有服务方法中传递租户ID,而不是依赖于http上下文,这样就可以轻松地处理Web和Web作业应用程序中的租户联系人。 这使得呼叫免于联系,更容易测试。 多租户实体接口方法看起来很好,我们在我们的应用程序中也有类似的限制,到目前为止工作正常。

  2. 关于添加索引,您需要在具有租户ID的表中添加租户ID列的索引,并且该索引应该处理数据库端查询索引部分。

  3. 关于身份validation部分,我建议使用带有owin管道的asp.net identity 2.0。 该系统具有非常可扩展的可定制性,并且如果将来需要,可以轻松地与任何外部身份提供商集成。

  4. 请查看entity framework的存储库模式,它使您能够以通用方式编写较少的代码。 这将有助于我们摆脱代码重复和冗余,并且非常容易从unit testing用例进行测试

我认为最大的问题是4 – 修改DbContext。

不要修改上下文…

您不必将租户过滤代码与业务代码混合使用。

我认为你需要的只是一个存储库,返回过滤数据
此存储库将根据您从TenantIdProvider获取的ID返回过滤后的数据。
然后,您的服务不必了解有关租户的任何信息

上述就是C#学习教程:具有DbContext和TenantId的MultiTenancy – 拦截器,filter,EF代码优先分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!

 using System; using System.Data.Entity; using System.Linq; namespace SqlServerDatabaseBackup { public class Table { public int TenantId { get; set; } public int TableId { get; set; } } public interface ITentantIdProvider { int TenantId(); } public class TenantRepository : ITenantRepositoty { private int tenantId; private ITentantIdProvider _tentantIdProvider; private TenantContext context = new TenantContext(); //You can abstract this if you want private DbSet filteredTables; public IQueryable
Tables { get { return filteredTables.Where(t => t.TenantId == tenantId); } } public TenantRepository(ITentantIdProvider tentantIdProvider) { _tentantIdProvider = tentantIdProvider; tenantId = _tentantIdProvider.TenantId(); filteredTables = context.Tables; } public Table Find(int id) { return filteredTables.Find(id); } } public interface ITenantRepositoty { IQueryable
Tables { get; } Table Find(int id); } public class TenantContext : DbContext { public DbSet
Tables { get; set; } } public interface IService { void DoWork(); } public class Service : IService { private ITenantRepositoty _tenantRepositoty; public Service(ITenantRepositoty tenantRepositoty) { _tenantRepositoty = tenantRepositoty; } public void DoWork() { _tenantRepositoty.Tables.ToList();//These are filtered records } } }

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/cdevelopment/951967.html

(0)
上一篇 2021年11月19日
下一篇 2021年11月19日

精彩推荐