如何在版本化实体之间配置一对多/多对一关系



我得到了一个数据库(因此我无法更改它),其中包含使用version编号列进行版本控制的实体。

实体是个人住所地址。每个实体都有一个使用 id 和版本组成的复合主键。

一个人可以有多个住所,一个住所引用一个地址一个地址可以由不同的住所引用。因此,我们在个人与其住所之间具有一对多关系,住所地址之间存在多对多关系。

每个实体仅使用 id 引用另一个实体,因此每个实体都单独进行版本控制。

让我给你一个表格的例子:

|id|version|etc.
|1 |1      |...
|1 |2      |...

住所

|id|version|person_id|address_id|etc...
|2 |1      |1        |3         |...
|2 |2      |1        |3         |...
|3 |1      |1        |4         |...

地址

|id|version|etc...
|3 |1      |...
|3 |2      |...
|4 |1      |...

如您所见,每个实体仅使用 id 相互引用,并且同一实体可以有多个版本,因此您可以多次使用相同的 id。

我希望我的模型实体看起来像这样:

public class Person {
public long Id {get; set;}
public long Version {get; set;}
public List<Domicile> Domiciles {get; set;}
}
public class Domicile {
public long Id {get; set;}
public long Version {get; set;}
public long AddressId {get; set;}
public List<Address> AddressVersions {get; set;}
}
public class Address {
public long Id {get; set;}
public long Version {get; set;}
}

配置一个人与其住所之间的关系很简单:

modelBuilder.Entity<Person>(entity => {
//...
entity.HasMany(p => p.Domiciles)
.WithOne()
.HasForeignKey(d => d.PersonId)
.HasPrincipalKey(p => p.Id);
});

现在是我不知道如何配置的部分:住所与其地址版本之间的关系。理论上,这种关系应该是多对一的,因为一个地址被多个住所使用。

我想在住所上拥有所有地址版本,所以我已声明属性public List<Address> AddressVersions,但现在我必须配置导航属性。

如果我尝试将关系配置为:

modelBuilder.Entity<Domicile>(entity => {
//...
entity.HasMany(p => p.AddressVersions)
.WithMany());
});

这不会编译,因为多对多关系需要两端的导航属性。

如果我尝试使用一对多配置对关系进行建模:

modelBuilder.Entity<Domicile>(entity => {
//...
entity.HasMany(p => p.AddressVersions)
.WithOne()
.HasPrincipalKey(p => p.AddressId);
});

它不起作用,因为我在地址上没有引用住所的外键,因为一个地址被多个住所使用。

如果我在关系上反其道而行之:

modelBuilder.Entity<Domicile>(entity => {
//...
entity.HasOne(p => p.Address)
.WithMany()
.HasForeignKey(p => p.AddressId)
.HasPrincipalKey(d => d.Id);
});

尽管有这个作品,我只得到了地址的许多可能版本之一,丢失了信息。

如何对地址住所之间的关系进行建模,并在委托人实体上提供相关实体的所有版本?

编辑:

关于数据的一些澄清:

每个实体都有一个复合主键,定义如下:

PRIMARY KEY (id, version);

此复合主键唯一标识特定记录和版本。

每个实体都引用另一个实体,外键定义如下:

CREATE TABLE Domicile(
--etc.
CONSTRAINT fk_address FOREIGN KEY (address_id) REFERENCES address (id)
);

如您所见,外键仅引用主键的一部分。

在我上面提供的示例表中,我希望当我选择具有 id1和版本2的人时,我会返回 3 个住所(id2和两个版本12和 id3只有一个版本).
然后,对于每个带有 id2的住所版本, 我希望有 2 个地址(ID3版本为12),对于带有 id 的单一版本的住所3我排除 1 个地址(ID4版本为1)

在代码中,我们可以将我对 ID2Domicile的期望写成:

Assert.That(domicile.Addresses.Count, Is.EqualTo(3));
Assert.That(domicile.Addresses[0].Id, Is.EqualTo(3));
Assert.That(domicile.Addresses[0].Version, Is.EqualTo(1));
Assert.That(domicile.Addresses[1].Id, Is.EqualTo(3)); //Same address id as before...
Assert.That(domicile.Addresses[0].Version, Is.EqualTo(2)); //...but different version

问题似乎是外键不引用唯一行,因为它只使用主键的一部分,因此无法使用实体框架核心对这种关系进行建模(也许是?

希望这能更好地澄清我必须使用什么以及我想得到什么。

简短的回答是你想要的不能用内置功能完成。 也许第三方扩展/工具可以提供帮助。

Id对于三个实体中的任何一个都不是唯一的,因此不能仅使用Id属性作为主键在它们之间建立多对一或一对多关系(因为主键必须唯一标识相关记录)。

选项:

  1. 手动联接这些实体并投影结果
  2. 创建数据库视图(如果允许)以提供 EF 可用于多对多关系的联结实体。

最新更新