Ядро EF от многих ко многим нежелательным столбцам

#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 ?