У меня есть объект с объектом значения ExternalSystemName и родительским типом Deployment, который является другим объектом. Важная часть модели выглядит так:
public sealed class ExternalSystem : Entity
{
public ExternalSystemName Name { get; private set; }
public Deployment Deployment { get; private set; }
}
Уникальность этой сущности определяется комбинацией идентификатора развертывания (хранящегося в классе сущности развертывания) и имени (которое является значением объекта значения ExternalSystemName). Другими словами, в развертывании не может быть 2 внешних систем с одинаковыми именами.
Я столкнулся с проблемой при попытке настроить этот комбинированный уникальный индекс с реализацией IEntityTypeConfiguration:
internal sealed class ExternalSystemsConfiguration :
IEntityTypeConfiguration<ExternalSystem>
{
public void Configure(EntityTypeBuilder<ExternalSystem> builder)
{
builder.ToTable("TblExternalSystems");
builder.OwnsOne(e => e.Name, navigationBuilder =>
{
navigationBuilder.Property(e => e.Value)
.HasColumnName("Name");
});
builder.HasIndex(e => new { e.Name, e.Deployment }).IsUnique();
}
}
Я получаю это исключение при запуске моего API:
System.InvalidOperationException: ''Name' cannot be used as a property on entity type 'ExternalSystem' because it is configured as a navigation.'
Вместо этого я попытался указать индекс на e.Name.Value, и я получаю эту ошибку:
System.ArgumentException: 'The expression 'e => new <>f__AnonymousType0`2(Value = e.Name.Value, Deployment = e.Deployment)' is not a valid member access expression. The expression should represent a simple property or field access: 't => t.MyProperty'. When specifying multiple properties or fields, use an anonymous type: 't => new { t.MyProperty, t.MyField }'. (Parameter 'memberAccessExpression')'
Я также попробовал уникальный индекс только для одного из этих свойств, и все равно получаю ошибку навигации. Боюсь, я уже знаю ответ, но означает ли это, что EF Core поддерживает индексы только для столбцов, которые не относятся к типу non-entity, non-valueObject? Означает ли это, что моя модель должна иметь свойство Guid, представляющее идентификатор развертывания, а не само развертывание?
ОБНОВИТЬ
Я узнал, что EF Core прекрасно справляется с парами эталон/примитив. Имея это в виду, моя сущность ExternalSystem теперь может иметь ОБА этих свойства:
public Deployment Deployment { get; private set; }
public Guid DeploymentId { get; private set; }
Это свойство Guid не является частью конструктора, и поскольку они в конечном итоге получают одно и то же имя столбца, все работает нормально. Теперь я могу просто добавить это в свою конфигурацию для этого объекта, и индекс будет создан правильно:
builder.HasIndex(e => new { e.DeploymentId}).IsUnique();
Моя проблема теперь связана с объектом значения. Полагаю, используя тот же подход, я мог бы сделать что-то подобное?
public ExternalSystemName NameV { get; private set; }
public string Name { get; private set; }
Я должен переименовать свойство объекта значения, поскольку они, очевидно, не могут иметь одно и то же имя. Это не то, что мне нужно было делать с типом объекта, поскольку EF Core изначально знал, что нужно добавить «Id» к имени столбца. При такой настройке EF Core дублирует столбцы. У одного есть имя «Имя», а у другого — «ExternalSystem_Name». Очевидно, что все остальное не работает, поскольку этот столбец не принимает нулевые значения. Почему это происходит?
чтобы эти свойства Id могли жить параллельно со связанным типом объекта и хорошо работать с базовой БД?
Да, базовая база данных уже должна иметь эти столбцы, поскольку FK и EF знают, как работать с парами ссылочных/примитивных свойств (также известными как ассоциации внешних ключей).
Это работает для типа объекта, но я не уверен, как затем справиться с объектом значения в том же подходе. Я обновлю свой оригинальный пост.





Вам нужно вручную определить ключ как внешний ключ, используя Fluent API.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ExternalSystem>()
.HasOne(g => g.NameV)
.WithOne()
.HasForeignKey<ExternalSystem>(j => j.Name);
}
Поместите этот метод в свой класс контекста БД
Я исправил это, определив преобразователь для свойства ExternalSystemName:
builder.Property(e => e.Name)
.HasConversion(
e => e.Value,
v => ExternalSystemName.Create(v).Value);
Теперь я могу объявить свой уникальный индекс как таковой:
builder.HasIndex(e => new { e.DeploymentId, e.Name}).IsUnique();
Я также обновился до .NET 7 и EF Core 7 после того, как задал этот вопрос, так что это тоже могло быть фактором.
Добавьте свойства
NameIdиDeploymentIdвExternalSystemи добавьте индекс для этих свойств.