Отношение «один-ко-многим» с использованием части составного ключа - PullRequest
0 голосов
/ 23 мая 2019

У меня есть модель в 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

Edit, похоже, нет соответствующей функциональности. Edit

У меня есть следующееСвободное правило в моем переопределении OnModelCreating.

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.

Насколько я понимаю,это потому, что я ссылаюсь на сущность с составным ключом из 2 частей только одной частью этого ключа.То, что я ищу, это способ сделать именно это.

1 Ответ

2 голосов
/ 23 мая 2019

Внешний ключ на второй таблице должен совпадать с PK на первой.

Например, для простоты используйте Int для типа ключа.

Если у меня есть Person с PKID: 1, SyncDate: 2019-05-22

Затем я добавляю второй вариант: PK ID: 1, SyncDate: 2019-05-23

Если я иду, чтобы добавить«Тестовая» запись, на какую запись Person она будет ссылаться с идентификатором FK Person 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

В этом примере Человек может иметь многоPersonComments на основе ссылки PersonID, в то время как комментарий может быть преобразован обратно в Person через Person ID.Как FK.

В структуре таблицы, которая имеет:

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

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

PersonId в PersonComment не может быть FK для Person, поскольку он не отражает PK для таблицы Person.Вы можете легально иметь эту структуру таблицы, но PersonId - это просто тупой, неограниченный столбец.Вы можете запросить все записи человека вручную, используя его, но вы получите все версии человека.Нет никаких ограничений и т. Д., Чтобы гарантировать, что идентификатор человека в комментарии совпадает с идентификатором в таблице Person.

Если вам не нужны версии Person, вы можете иметь тестовую сущность с PersonID, но 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)

Тогда «Персона» отображает личность в ее текущем состоянии, содержащую коллекцию, отражающую историю.Оттуда вы можете предотвратить изменение полей Person с помощью частных сеттеров и методов в стиле DDD, которые будут отвечать за составление новой записи истории на основе текущих данных Person перед обновлением значений Person.Таким образом, запись Person может быть исторической и сохранять свой идентификатор для связанных объектов.

...