#c# #.net-core #entity-framework-core #entity-framework-migrations
#c# #.net-ядро #entity-framework-core #entity-framework-миграции
Вопрос:
Я пытаюсь создать модель, которая имеет двукратное отношение «многие ко многим».
StockItem * — * QualityCheckDefinition
Статья * — * QualityCheckDefinition
От многих ко многим классам:
public class StockItemQualityCheckDefinition
{
public StockItem StockItem { get; set; }
public QualityCheckDefinition QualityCheckDefinition { get; set; }
public int StockItemId { get; set; }
public int QualityCheckDefinitionId { get; set; }
}
public class ArticleQualityCheckDefinition
{
public Article Article { get; set; }
public QualityCheckDefinition QualityCheckDefinition { get; set; }
public string ArticleId { get; set; }
public int QualityCheckDefinitionId { get; set; }
}
Класс QualityCheckDefinition:
public class QualityCheckDefinition : Entity<int>
{
public List<StockItemQualityCheckDefinition> StockItemQualityCheckDefinitions { get; set; }
public List<ArticleQualityCheckDefinition> ArticleQualityCheckDefinitions { get; set; }
}
Класс статьи:
public class Article : Entity<string>
{
public ICollection<ArticleQualityCheckDefinition> ArticleQualityCheckDefinitions { get; set; }
}
Класс StockItem:
public class StockItem : Entity<int>
{
public List<StockItemQualityCheckDefinition> StockItemQualityCheckDefinitions { get; set; }
}
Сопоставления:
public class ArticleQualityCheckDefinitionMap : IEntityTypeConfiguration<ArticleQualityCheckDefinition>
{
public void Configure(EntityTypeBuilder<ArticleQualityCheckDefinition> builder)
{
builder.HasKey(x => new { x.ArticleId, x.QualityCheckDefinitionId });
builder.HasOne(x => x.Article)
.WithMany(x => x.ArticleQualityCheckDefinitions)
.HasForeignKey(x => x.ArticleId)
.HasPrincipalKey(x => x.Id)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(x => x.QualityCheckDefinition)
.WithMany(x => x.ArticleQualityCheckDefinitions)
.HasForeignKey(x => x.QualityCheckDefinitionId)
.HasPrincipalKey(x => x.Id)
.OnDelete(DeleteBehavior.Restrict);
}
}
public class StockItemQualityDefinitionMap : IEntityTypeConfiguration<StockItemQualityCheckDefinition>
{
public void Configure(EntityTypeBuilder<StockItemQualityCheckDefinition> builder)
{
builder.HasKey(x => new { x.StockItemId, x.QualityCheckDefinitionId });
builder.HasOne(x => x.StockItem)
.WithMany(x => x.StockItemQualityCheckDefinitions)
.HasForeignKey(x => x.StockItemId);
builder.HasOne(x => x.QualityCheckDefinition)
.WithMany(x => x.StockItemQualityCheckDefinitions)
.HasForeignKey(x => x.QualityCheckDefinitionId);
}
}
Связи настроены нормально, и они работают. Однако миграция создает дополнительные ненужные поля внутри QualityCheckDefinition
класса. Миграция требует добавления как nullable StockItemId
, так и ArticleId
into QualityCheckDefinition
class.
Это часть миграции:
migrationBuilder.CreateTable(
name: "QualityCheckDefinitions",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
//other properties
ArticleId = table.Column<string>(nullable: true),
StockItemId = table.Column<int>(nullable: true)
},
Почему он добавляет эти нежелательные ключи?
PS. Я использую ядро EF 2.2
Комментарии:
1. Ваша конфигурация выглядит так, как будто это конфигурация EF, а не конфигурация ядра EF. Смотрите learn.microsoft.com/en-us/ef/core/modeling/relationships для получения дополнительной информации
2. Я не вижу разницы в разделе «Многие-многие»
3. Скорее всего, ваши классы
Article
иStockItem
содержат «вспомогательные» свойства, подобныеpublic IEnumerable<QualityCheckDefinition> QualityCheckDefinitions
, которые обнаруживаются как свойства навигации.
Ответ №1:
Где ваше сопоставление конфигурации для StockItem
и Article
? И я думаю, что способ, который вы использовали .HasPrincipalKey()
внутри ArticleQualityCheckDefinitionMap
, неправильный.
Основной ключ
Если вы хотите, чтобы внешний ключ ссылался на свойство, отличное от первичного ключа, вы можете использовать Fluent API для настройки свойства основного ключа для взаимосвязи. Свойство, которое вы настраиваете в качестве основного ключа, будет автоматически настроено в качестве альтернативного ключа.
Ваш ArticleQualityCheckDefinitionMap
builder.HasOne(x => x.Article)
.WithMany(x => x.ArticleQualityCheckDefinitions)
.HasForeignKey(x => x.ArticleId) // <--
.HasPrincipalKey(x => x.Id) // <--
.OnDelete(DeleteBehavior.Restrict);
Согласно соглашениям, .HasForeignKey(x => x.ArticleId)
будет нацелен на первичный ключ Article
сущности string Id
. И затем вы сообщили Entity Framework, что не хотите настраивать внешний ключ на первичный ключ. Вы хотите настроить таргетинг на другое свойство, вызываемое Id
с помощью .HasPrincipalKey(x => x.Id)
.
Да? Разве они не одинаковы? Вот почему я думаю, что Entity Framework запутался. И поскольку у вас нет конфигурации для Article
, Entity Framework приложила все усилия, чтобы сгенерировать для вас первичный ключ, называемый «articleId»?
Вы можете устранить проблему, просто добавив конфигурации обратно и удалив .HasPrincipalKey()
оттуда.
StockItemConfiguration
using DL.SO.EFCore.Learning.Data.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace DL.SO.EFCore.Learning.Data.Configurations
{
public class StockItemConfiguration : IEntityTypeConfiguration<StockItem>
{
public void Configure(EntityTypeBuilder<StockItem> builder)
{
builder.HasKey(x => x.Id);
builder.ToTable(nameof(StockItem));
}
}
}
Конфигурация статьи
using DL.SO.EFCore.Learning.Data.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace DL.SO.EFCore.Learning.Data.Configurations
{
public class ArticleConfiguration : IEntityTypeConfiguration<Article>
{
public void Configure(EntityTypeBuilder<Article> builder)
{
builder.HasKey(x => x.Id);
builder.ToTable(nameof(Article));
}
}
}
Возможно, вам также потребуется настроить для QualityCheckDefinition
:
QualityCheckDefinitionConfiguration
using DL.SO.EFCore.Learning.Data.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace DL.SO.EFCore.Learning.Data.Configurations
{
public class QualityCheckDefinitionConfiguration : IEntityTypeConfiguration<QualityCheckDefinition>
{
public void Configure(EntityTypeBuilder<QualityCheckDefinition> builder)
{
builder.HasKey(x => x.Id);
builder.ToTable(nameof(QualityCheckDefinition));
}
}
}
Затем удалите .HasPrincipalKey()
:
ArticleQualityCheckDefinitionConfiguration
using DL.SO.EFCore.Learning.Data.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace DL.SO.EFCore.Learning.Data.Configurations
{
public class ArticleQualityCheckDefinitionConfiguration : IEntityTypeConfiguration<ArticleQualityCheckDefinition>
{
public void Configure(EntityTypeBuilder<ArticleQualityCheckDefinition> builder)
{
builder.HasKey(x => new { x.ArticleId, x.QualityCheckDefinitionId });
builder.HasOne(x => x.Article)
.WithMany(x => x.ArticleQualityCheckDefinitions)
.HasForeignKey(x => x.ArticleId);
builder.HasOne(x => x.QualityCheckDefinition)
.WithMany(x => x.ArticleQualityCheckDefinitions)
.HasForeignKey(x => x.QualityCheckDefinitionId);
builder.ToTable(nameof(ArticleQualityCheckDefinition));
}
}
}
Тогда все должно быть в порядке?
Комментарии:
1. Спасибо за длинный ответ. Я переработал код с учетом ваших рекомендаций, но результат все тот же…
2. @miechooy: вы добавили конфигурации для
StockItem
,Article
иQualityCheckDefinition
?