Итак, дилемма:
A) Должен ли я оставить два FK в Version
или
B) построить две таблицы DocumentVersion
и RegisterVersion
вместо просто Version
?
Ну, правда в том, что вы можете сделать и то, и другое.Вам просто нужно решить, какой подход лучше подходит вашей системе.Давайте кратко рассмотрим.
Подход A
Чтобы ответить на ваш вопрос;да, подход EF по умолчанию правильный.Создание двух FK и создание двух таблиц приведет к созданию двух FK.Это создаст дополнительную таблицу только в случае промежуточной таблицы для многих со многими участниками.
Я всегда рекомендую создавать все FK самостоятельно, а не позволять EF делать это за нас.Таким образом, мы получаем больший контроль над поведением отношений и можем также получить доступ к FK в нашем приложении, поскольку они являются свойством сущности.
public class Version
{
[Key]
public int VersionNumber { get; set; }
public int? DocumentID { get; set; }
public virtual Document Document { get; set; }
public int? RegisterID { get; set; }
public virtual Register Register { get; set; }
//Other properties
}
Поскольку Version
имеет PK, он может создавать записибез какой-либо из ФК, имеющих какое-либо значение.Если это разрешено в вашей бизнес-модели, оставьте все как есть.Позже вы можете предоставить пользовательский интерфейс для назначения «Версий» либо «Документам», либо «Регистрам».
Если вы хотите добавить некоторые правила в вашу таблицу Version
;например, каждая запись должна иметь хотя бы один FK или только один FK, это можно сделать, переопределив метод ValidateEntity
вашего класса DbContext
(или, возможно, с помощью некоторого ограничения sql в базе данных).
protected override DbEntityValidationResult ValidateEntity(
DbEntityEntry entityEntry, IDictionary<object, object> items)
{
// validate entity from annotations
var result = base.ValidateEntity(entityEntry, items);
// custom validation rules
if (entityEntry.Entity is Version &&
(entityEntry.State == EntityState.Added || entityEntry.State == EntityState.Modified))
{
Version version = ((Version)entityEntry.Entity);
if (version.DocumentID == null && version.RegisterID == null)
result.ValidationErrors.Add(new DbValidationError(null, "A document or register must be specified."));
}
return result;
}
Обратите внимание, что вы можете создавать свои собственные аннотации для проверки свойств вашей сущности.Но они ограничены одной собственностью.Если вы хотите добавить проверки, которые объединяют более одного свойства, метод ValidateEntity
- единственный известный мне способ.
Подход B
Есть два способа реализации этого подхода.Первый - сохранить таблицу Version
и добавить две промежуточные таблицы сверху.
public class Document
{
public virtual List<DocumentVersion> Versions { get; set; }
// other properties
}
public class Register
{
public virtual List<RegisterVersion> Versions { get; set; }
// other properties
}
public class Version
{
[Key]
public int VersionNumber { get; set; }
//Other properties
}
public class DocumentVersion
{
public int DocumentID { get; set; }
public virtual Document Document { get; set; }
public int VersionID { get; set; }
public virtual Version Version { get; set; }
// other properties
}
public class RegisterVersion
{
public int RegisterID { get; set; }
public virtual Register Register { get; set; }
public int VersionID { get; set; }
public virtual Version Version { get; set; }
// other properties
}
На самом деле это разрешает отношения многие ко многим, но вы можете использовать это как один ко многим.Второй способ - сделать Version
абстрактным (не таблица базы данных) и построить две новые таблицы для наследования от Version
:
public class Document
{
public virtual List<DocumentVersion> Versions { get; set; }
// other properties
}
public class Register
{
public virtual List<RegisterVersion> Versions { get; set; }
// other properties
}
// don't make this a DbSet
public abstract class Version
{
[Key]
public int VersionNumber { get; set; }
//Other properties
}
public class DocumentVersion : Version
{
public int DocumentID { get; set; }
public virtual Document Document { get; set; }
// other properties
}
public class RegisterVersion : Version
{
public int RegisterID { get; set; }
public virtual Register Register { get; set; }}
// other properties
}
Это правильное и понятное отношение один ко многим.
Заключение Суть в том, что вы можете использовать любой из двух подходов и с изменениями, которые соответствуют вашим потребностям.
Я успешно использовал оба подхода, но я предпочитаю второй (и с наследованием абстрактного класса).Первый подход кажется более эффективным способом сокращения ресурсов базы данных или упрощения разработки, но современные базы данных вовсе не подвержены влиянию нескольких таблиц, и разработка может стать излишне сложной.Более того, второй подход позволяет расширить функциональность отношений, добавляя дополнительные свойства к каждой таблице соединений отдельно.И для 6 сущностей, с которыми вам приходится иметь дело, мне кажется более безопасным перейти ко второму подходу.Я использовал этот подход в приложении со многими типами файлов и взаимосвязями, и он всегда был очень простым и расширяемым.Эти дополнительные свойства в каждой таблице relashion тоже оказались очень полезными.
Надеюсь, я смогу помочь, веселое кодирование!