使用组合键的一部分的单向一对多关系

时间:2019-05-22 22:03:33

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

我在EF中有一个与此相似的模型

Person
    PK Guid Id
    PK DateTime DateSynced

Test
    PK Guid Id
    FK Guid PersonId

在Entity Framework 6.2中,我实际上只关心有关“测试”集合的“人的导航属性”。我不需要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);

我遇到以下异常,抱怨从属角色和主体角色约束:

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

据我所知,这是因为我仅用键的一部分来引用具有两部分组合键的实体。我正在寻找的是一种完全做到这一点的方法。

1 个答案:

答案 0 :(得分:2)

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

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

如果我的PK的ID为1,则SyncDate:2019-05-22

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

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

要引用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

在此示例中,一个人可以基于PersonID链接具有许多PersonComments,而Comment可以通过Person ID解析回该人。作为FK。

在具有以下内容的表结构中:

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

PersonComment
-------------
PK: PersonCommentID
PersonId
PersonComment中的

PersonId不能是Person的FK,因为它不能反映Person表的PK。您可以合法地使用此表结构,但是PersonId只是一个哑巴,不受限制的列。您可以使用它手动查询所有“个人”记录,但是您将获得该个人的所有“版本”。没有任何约束等可确保注释上的“个人ID”与“个人”表上的ID匹配。

如果您不关心Person的版本,则可以使用带有Person ID的Test实体,但是EF无法将其与Person实体相关联,则必须从上下文中手动加载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)

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