【问题标题】:One-way One to Many relationship using part of a composite key使用组合键的一部分的单向一对多关系
【发布时间】:2019-05-22 22:03:32
【问题描述】:

我在 EF 中有一个类似的模型

Person
    PK Guid Id
    PK DateTime DateSynced

Test
    PK Guid Id
    FK Guid PersonId

在 Entity Framework 6.2 中,我真的只关心 Person 上的 Navigation 属性,它引用了一组测试。我不需要 Test.Persons 的属性或类似的东西。

我真的只想让 Person.Tests 的 Test.PersonId = Id 与 DateSynced 无关。最终会有很多人拥有相同的 Id,每个人都有不同的 DateSynced DateTime。

这是可行的还是我需要一个带有中间表的多对多?

我知道 EFCore 有一个备用键的概念,我想我可以在这项工作中利用它,但 EF 6.2 中似乎没有相应的功能

编辑

我的 OnModelCreating 覆盖中有以下 Fluent 规则。

modelBuilder.Entity<Test>()
    .HasRequired(t => t.Person)
    .WithMany(p => p.Tests)
    .HasForeignKey(t => t.PersonId);

我收到以下异常,抱怨 Dependent 和 Principal 角色约束:

The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.

据我了解,这是因为我仅通过该键的一部分来引用具有 2 部分复合键的实体。我正在寻找的是一种方法来做到这一点。

【问题讨论】:

  • 这是非常标准的东西。你到底有什么问题?什么不工作?也许分享一些你尝试过的代码。
  • @Ben 我已经用其他上下文更新了我的答案。我要做的是与仅使用该键的一部分的复合键实体建立关系。
  • 哦,DateSynced 是您主键的一部分。那是……一种不寻常的方式。我不确定处理此问题的“正确”方法是什么,但我不会定义关系,而是手动处理您在我的代码中所追求的连接(即,只需编写在PersonId 上连接的 LINQ。 .
  • 一个 SQL FK 约束(实际上不是关系,而是关于数据库的事实)表示某些列的子行值出现在它们是 PK/UNIQUE 的其他地方。在引用的表中,它们必须声明为 PK/UNIQUE。列与引用表中的约束有什么关系并不重要。 (我们说 FK 是从引用表和列到被引用的。如果引用的列不是 PK/UNIQUE,那么您没有 FK 和 SQL 无法声明性约束的情况。
  • use text, not images/links, for text--including tables & ERDs. 解释或引用其他文本。仅将图像用于无法表达为文本或增强文本的内容。无法搜索或剪切和粘贴图像。在图像中包含图例/键和说明。让您的帖子自成一体。

标签: entity-framework entity-framework-6 foreign-keys relationship one-to-many


【解决方案1】:

第二个表的外键必须与第一个表的 PK 匹配。

例如,为简单起见,使用 Int 作为键类型。

如果我有一个人的 PK ID:1,同步日期:2019-05-22

然后我添加第二个变体:PK of ID: 1, SyncDate: 2019-05-23

如果我要添加“测试”记录,它将引用哪个人员记录,其 FK 人员 ID 为 1?它将引用两条记录,因此 EF 不支持“HasRequired”引用指向单个 Person 记录。

要引用 Person ID 1 的一种变体,您的测试记录将需要 PersonId 和 SyncDate 来识别记录:

public class Test
{
    public Guid Id { get; set; }
    public Guid PersonId { get; set; }
    public DateTime SyncDate { get; set; }

    public virtual Person Person { get; set; }
}

modelBuilder.Entity<Test>()
    .HasRequired(t => t.Person)
    .WithMany(p => p.Tests)
    .HasForeignKey(t => new { t.PersonId, t.SyncDate });

数据库中的表不能基于部分 FK 相互引用,除非多对 1。即

Person
------
PK: PersonID

PersonComment
-----------
PK: PersonId
PK: CommentDate

在这个例子中,一个 Person 可以有许多基于 PersonID 链接的 PersonComments,而一个 Comment 可以通过 Person ID 解析回 Person。作为 FK。

在一个表结构中:

Person
------
PK: PersonID
PK: Version

PersonComment
-------------
PK: PersonCommentID
PersonId

PersonComment 中的 PersonId 不能是 Person 的 FK,因为它不会将 PK 反映到 Person 表。您可以合法地拥有此表结构,但 PersonId 只是一个愚蠢的、不受约束的列。您可以使用它手动查询所有人员记录,但您将获得人员的所有版本。没有任何约束等可以确保评论上的个人 ID 与个人表上的 ID 匹配。

如果您不关心 Person 的版本,您可以拥有一个带有 Person ID 的 Test 实体,但 EF 无法将其与 Person 实体相关联,您必须从 Context 手动加载 Person 记录。

当谈到架构结构背后的目的时,我建议寻找可能的替代方案。例如,如果您的目标是跟踪版本化数据,我建议您查看以下内容:

Person
PK: PersonId
** Person Fields

PersonHistory
PK: PersonHistoryId
FK: PersonId
VersionDate
** Person Fields

Test
PK: TestId
FK: PersonId (if applies to current person, or PersonHistoryId if a specific version)

然后“Person”反映了处于当前状态的人,其中包含反映历史的集合。从那里您可以防止通过私有设置器和 DDD 样式的方法修改 Person 字段,这些方法将负责在更新 Person 值之前根据当前的 Person 数据组成新的 History 记录。这样一来,个人记录就可以成为历史记录,并为相关实体保留其 ID。

【讨论】:

  • 谢谢。我认为这是不可行的(这些不是我的要求),但想看看是否有任何可能性。我认为我将使用“最新”人员的人员表上的视图。这些将是单个 PK 唯一的,因此将更容易与一对多中的测试相关联。我只是希望有一种更简单的方法来通知权力,因为他们的要求很糟糕。
  • @CodeWarrior 不要因为(SQL 和)SQL DBMS 的可悲缺陷而责备业务管理者。标准 SQL 实际上支持可通过查询表达的任意声明性 DB 约束。没有任何合理的业务只能通过 PK、UNIQUE、NOT NULL、CHECK、FK 和索引来表达其约束。触发器实现的约束是健全实用的 SQL DBMS 软件工程的必要条件。
猜你喜欢
  • 2014-06-19
  • 2019-04-28
  • 2014-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多