我正在尝试创建一个有两次多对多关系的模型。
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
类中创建其他不需要的字段。迁移想要将可空的StockItemId
和ArticleId
添加到QualityCheckDefinition
类。
迁移的一部分:
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 Core 2.2
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)
的属性。
咦?他们不一样吗?这就是为什么我认为实体框架感到困惑。由于您没有Article
的配置,Entity Framework会尽力为您生成一个主键,称为“ArticleId”?
你可以通过添加配置并在那里删除.HasPrincipalKey()
来解决问题。
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
:
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()
:
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));
}
}
}
那你应该没问题?