您将如何避免此数据库架构中的数据冲突?

时间:2009-08-05 08:10:46

标签: database linq-to-sql database-design normalization data-access-layer

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

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

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

**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的原因。

5 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

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

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


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

删除该列,您已删除了问题。

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

或者“客户”是该公司的人?


编辑:关于没有公司的项目的澄清,我会将参考文献分开,但你不会摆脱你所描述的问题而没有阻止多个参考的限制正在制作。

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

答案 2 :(得分:0)

如果你想使用这样的表并避免所有新的查询只是将触发器放在表上,当用户试图插入带有错误数据的行时,触发器会阻止他。 最好的祝福, 约尔丹

答案 3 :(得分:0)

我的第一个想法是为每个公司创建一个名为“No client”的特殊客户记录。然后从Project表中删除CompanyId,如果项目没有客户端,则使用“No client”记录而不是“普通”客户端记录。如果处理此类无客户端是特殊的,则向无客户端记录添加标志以明确标识它。 (我不愿意依赖名字“没有客户”或类似的东西 - 太模糊了。)

然后就无法存储不一致的数据,因此问题就会消失。

答案 4 :(得分:0)

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

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

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

  1. SingleReferenceEntityBase - 标准。只检查(通过反射)是否真的只有一个引用(无论是可传递的还是不及物的)到Companies表。如果是这种情况,对公司表的引用不会变得不一致。

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

  3. 这两种类型实现了一个“CheckReferences”,只要通过部分实现为所有Linq实体自动生成的OnValidate(System.Data.Linq.ChangeAction操作)方法将linq实体写入数据库,我就会调用它。