实体框架和多租户数据库设计

时间:2010-05-30 12:33:17

标签: entity-framework database-design saas multi-tenant

我正在研究SaaS概念的多租户数据库架构设计。它将是ASP.NET MVC - > EF,但这不是那么重要。

下面您可以看到示例数据库架构(租户是公司)。在整个架构中复制CompanyId,并且主键已放置在自然键和租户ID上。

当我将表添加到实体模型文件(Model1.edmx)中时,将此模式插入实体框架会产生以下错误:

  
      
  • 关系'FK_Order_Customer'使用部分包含在'Order'表的主键'{OrderId,CompanyId}'中的外键'{CustomerId,CompanyId}'。外键集必须完全包含在主键集中,或者完全不包含在要映射到模型的主键集中。
  •   
  • 关系'FK_OrderLine_Customer'使用部分包含在'OrderLine'表的主键'{OrderLineId,CompanyId}'中的外键'{CustomerId,CompanyId}'。外键集必须完全包含在主键集中,或者完全不包含在要映射到模型的主键集中。
  •   
  • 关系'FK_OrderLine_Order'使用部分包含在'OrderLine'表的主键'{OrderLineId,CompanyId}'中的外键'{OrderId,CompanyId}'。外键集必须完全包含在主键集中,或者完全不包含在要映射到模型的主键集中。
  •   
  • 关系'FK_Order_Customer'使用部分包含在'Order'表的主键'{OrderId,CompanyId}'中的外键'{CustomerId,CompanyId}'。外键集必须完全包含在主键集中,或者完全不包含在要映射到模型的主键集中。
  •   
  • 关系'FK_OrderLine_Customer'使用部分包含在'OrderLine'表的主键'{OrderLineId,CompanyId}'中的外键'{CustomerId,CompanyId}'。外键集必须完全包含在主键集中,或者完全不包含在要映射到模型的主键集中。
  •   
  • 关系'FK_OrderLine_Order'使用部分包含在'OrderLine'表的主键'{OrderLineId,CompanyId}'中的外键'{OrderId,CompanyId}'。外键集必须完全包含在主键集中,或者完全不包含在要映射到模型的主键集中。
  •   
  • 关系'FK_OrderLine_Product'使用部分包含在'OrderLine'表的主键'{OrderLineId,CompanyId}'中的外键'{ProductId,CompanyId}'。外键集必须完全包含在主键集中,或者完全不包含在要映射到模型的主键集中。
  •   

问题分为两部分:

  1. 我的数据库设计不正确吗?我应该避免使用这些复合主键吗?我正在质疑我对基本模式设计(疲惫的大脑综合症)的理智。请随意提出“理想化”架构。
  2. 或者,如果数据库设计正确,那么EF无法匹配密钥,因为它将这些外键视为潜在的错误配置1:1关系(错误)?在这种情况下,这是一个EF错误,我该如何解决它?
  3. Multi-tenancy database schema http://i46.tinypic.com/23si52u.png

5 个答案:

答案 0 :(得分:4)

在快速扫描EF的错误信息时,它显然不喜欢你设置复合键的方式,我认为它可能会推动你朝着正确的方向前进。再次考虑一下使主键唯一的原因。如果没有CompanyID,OrderID是不是唯一的?没有CompanyID,ProductID不是唯一的吗? OrderLine当然应该是唯一的,没有CompanyID,因为OrderLine应该只与一个订单相关联。

如果您确实需要所有这些的CompanyID,这可能意味着有问题的公司正在为您提供ProductID和OrderID,那么您可能希望采用不同的方向,并生成您自己的非固有主键对数据。只需为主键设置一个自动增量列,然后将它们作为内部OrderID,OrderLineID,ProductID,CompanyID等。此时,OrderLine将不需要客户的OrderID或CompanyID;对订单的外键引用将是它的起点。 (并且CustomerID永远不应该是订单行的属性;它是订单的属性,而不是订单行。)

复合键只是杂乱无章。尝试在没有它们的情况下设计模型,看看它是否简化了事情。

答案 1 :(得分:2)

我认为在每个表格中存储公司编号对您的伤害不仅仅是帮助。我可以理解你为什么要这样做(作为程序员/ dba,你可以进入任何一个表并'看'哪些数据属于哪个人很舒服),但这会影响你设置数据库的方式应该如何。

避免复合键,您的设计变得更加简单。

答案 2 :(得分:2)

我认为错误不在设计中。 不在EF。 是在Sql Server关系中。

阅读EF消息:

  

关系'FK_Order_Customer'   使用外键集   '{CustomerId,CompanyId}'即是   部分包含在集合中   主键'{OrderId,CompanyId}'的   表'订单'。这套外国人   密钥必须完全包含在   一组主键,或完全没有   包含在主键集中   要映射到模型。

错误

Actualy,Order和Customer之间的关系只使用一个字段(可能是您用鼠标将“CustomerId”字段从订单表拖到Customer表的“Id”)

<强>解

右键单击连接订单和客户的电汇,并在关系中添加CompanyId


PS:设计正确。

将CompanyId放在每个表中是多租户架构中的rith解决方案,因为有助于扩展(通常总是只想从loggedIn公司中选择记录)。

答案 3 :(得分:0)

如果必须将CompanyID列绝对添加到每个表中,请将其添加为常规列而不是复合键。当您必须实现多对多关系时,主要使用复合键。

如上所述,还会在CompanyID上创建非聚集索引,因此加入公司表会受益。

谢谢!

答案 4 :(得分:0)

首先:像其他人所说,在引用外键时,使用另一个表中的整个主键(即两个字段)。

其次,我无法想象在严肃的应用程序中大多数表中都不使用CompanyID列。在这种情况下,Orderdetail可能可能是一个例外(也可能是全局查找表,除非它们依赖于租户)。事实上,你不能在没有添加CompanyID的情况下在桌面上进行任何安全的自由格式搜索,也不能在你到达具有该列的表格之前进行JOIN。后者显然会降低性能。也许在这种情况下,你可以为orderdetail做一个例外,只在连接版本中搜索(只有两个表)。然后,它不是真的一致。

同样关于将它作为复合键:它可能,但是在bug的持续时间内,错误地将错误信息写入(不存在或其他人的管理部门)中的可能性。尝试在生产中修复它,更不用说向客户解释为什么在他们的系统中看到他们的竞争对手订单。