1

我正在使用 SQL Server 2008 / LinqToSQL / 定制存储库作为 DAL 开发一个多用户 Internet 数据库驱动的网站。我遇到了一个规范化问题,如果利用得当,可能会导致数据库状态不一致,我想知道如何处理这个问题。

问题:几家不同的公司可以访问我的网站。他们应该能够在我的网站上跟踪他们的项目和客户。一些(但不是全部)项目应该可以分配给客户。

这将产生以下数据库架构:

**Companies:**
ID
CompanyName

**Clients:** 
ID 
CompanyID (not nullable)
FirstName
LastName


**Projects:**
ID
CompanyID (not nullable)
ClientID (nullable)
ProjectName

这导致以下关系:

Companies-Clients (1:n)
Companies-Projects (1:n)
Clients-Projects(1:n)

现在,如果用户是恶意的,他可能会插入一个项目,该项目具有他自己的 CompanyID,但其 ClientID 属于另一个用户,从而使数据库处于不一致状态。

这个问题在我的数据库模式中以类似的方式出现,所以如果可能的话,我想以一种通用的方式解决这个问题。我有以下两个想法:

  • 检查可能导致 DAL 不一致的数据库写入。这将是通用的,但在执行更新和创建查询之前需要一些额外的数据库查询,因此会导致性能下降。

  • 为 clients-Projects 关系创建一个附加表,并确保以这种方式创建的关系是一致的。这也需要一些额外的选择查询,但比第一种情况要少得多。另一方面,它不是通用的,因此从长远来看更容易遗漏某些东西,尤其是在向数据库添加更多表/依赖项时。

你会怎么办?我错过了更好的解决方案吗?

编辑:您可能想知道为什么 Projects 表有 CompanyID。这是因为我希望用户能够添加有和没有客户的项目。我需要跟踪无客户端项目属于哪个公司(以及哪个网站用户),这就是项目需要 CompanyID 的原因。

4

5 回答 5

2

我会选择后者,有一个或多个表来定义实体之间允许的关系。

于 2009-08-05T08:24:37.447 回答
1

请注意,您拥有的参考文献没有循环性,因此标题具有误导性。

你所拥有的是数据冲突的可能性,那是不同的。


为什么项目表中有“CompanyID”?所涉及公司的 ID 由您链接到的客户隐含地给出。你不需要它。

删除该列,您就解决了问题。

此外,客户表中“名称”列的用途是什么?您可以有一个名称与公司名称不同的客户吗?

还是“客户”是那家公司的人?


编辑:好的,关于没有公司的项目的澄清,我会分开引用,但如果没有阻止多次引用的限制,你不会摆脱你描述的问题。

现有表的一个简单约束是项目行的 CompanyID 和 ClientID 字段不能同时为非空。

于 2009-08-05T11:37:32.037 回答
0

如果您想使用这样的表并避免所有新查询,只需将触发器放在表上,当用户尝试插入包含错误数据的行时,触发器会阻止他。最好的问候, 约旦

于 2009-08-05T08:23:32.770 回答
0

我的第一个想法是为每个名为“No client”的公司创建一个特殊的客户记录。然后从 Project 表中删除 CompanyId,如果项目没有客户,则使用“无客户”记录而不是“正常”客户记录。如果对此类无客户端的处理很特殊,请在无客户端记录中添加一个标志以明确标识它。(我不想依赖“No Client”之类的名字——太模糊了。)

那么就没有办法存储不一致的数据,所以问题就会消失。

于 2009-08-06T16:41:38.710 回答
0

最后,我实现了一个完全通用的解决方案,它解决了我的问题,没有太多的运行时开销,也不需要对数据库进行任何更改。我会在这里描述它以防其他人有同样的问题。

首先,该方法之所以有效,是因为其他表通过多个路径引用的唯一表是 Companies 表。由于我的数据库中就是这种情况,因此我只需检查要创建/更新/删除的每个实体的所有 n:1 引用实体是否引用同一公司(或根本没有公司)。

我通过从以下类型之一派生我的所有 Linq 实体来执行此操作:

  1. SingleReferenceEntityBase - 规范。仅检查(通过反射)是否确实只有一个对 Companies 表的引用(无论是传递的还是不传递的)。如果是这种情况,对公司表的引用不能变得不一致。

  2. MultiReferenceEntityBase - 用于特殊情况,例如上面的项目表。询问所有直接引用的实体他们引用的公司 ID。如果存在不一致,则引发异常。这会花费我每个 CRUD 操作的一些选择查询,但由于 MultiReferenceEntities 比 SingleReferenceEntities 少得多,因此可以忽略不计。

这两种类型都实现了“CheckReferences”,每当将 linq 实体写入数据库时​​,我都会通过部分实现为所有 Linq 实体自动生成的 OnValidate(System.Data.Linq.ChangeAction action) 方法来调用它。

于 2009-08-07T09:56:02.160 回答