EF Core 类型配置添加冗余 FK
EF Core type configuration adds redundant FKs
我正在尝试配置 EFCore 中实体之间的关系:Square
和 Position
。 Position 与名为 squares
的 square 具有一对一的关系,以及名为 enPassentTakablePawnOfWhite
和 enPassentTakablePawnOfBlack
的 Square
之间的两个一对一关系。请注意,两个实体中的 FK 和 PK 都是影子属性:
public class PositionTypeConfiguration : IEntityTypeConfiguration<Position>
{
public void Configure(EntityTypeBuilder<Position> builder)
{
builder.ToTable("Positions");
builder.Property<Guid>("BoardId");
builder.Property<Guid?>("CurrentPositionId");
builder.HasOne<Board>().WithMany("previosPositions").HasForeignKey("BoardId");
builder.HasOne<Board>().WithOne("currentPosition").IsRequired(false).HasForeignKey<Position>("CurrentPositionId");
builder.HasMany<Square>("squares").WithOne().HasForeignKey("PositionId");
builder.Property<Guid?>("EnPassentTakableWhitePawnId");
builder.Property<Guid?>("EnPassentTakableBlackPawnId");
builder.HasOne<Square?>("enPassentTakablePawnOfWhite").WithOne().HasForeignKey<Position>("EnPassentTakableWhitePawnId");
builder.HasOne<Square?>("enPassentTakablePawnOfBlack").WithOne().HasForeignKey<Position>("EnPassentTakableBlackPawnId");
builder.Property("canWhiteCastleKingSide");
builder.Property("canWhiteCastleQueenSide");
builder.Property("canBlackCastleKingSide");
builder.Property("canBlackCastleQueenSide");
builder.Property("_50MovesRuleCounter");
builder.Property<Guid>("Id").ValueGeneratedOnAdd();
builder.HasKey("Id");
}
}
public class SqaureTypeConfiguration : IEntityTypeConfiguration<Square>
{
public void Configure(EntityTypeBuilder<Square> builder)
{
builder.ToTable("Sqaures");
builder.Property(s => s.File);
builder.Property<Guid>("PositionId");
builder.Property(s => s.Row);
//this is FK to some other, irrelavant entity.
builder.HasOne(s => s.Occupier).WithOne().IsRequired(false).HasForeignKey<Square>("OccupierId").IsRequired(false);
builder.Property<Guid>("Id");
builder.HasKey("Id");
builder.Property<Guid?>("OccupierId").IsRequired(false);
}
}
问题是,在生成迁移时,迁移在 Square
中为 Position
创建了以下两个 FK:
public partial class ChangedBoard : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
/...
migrationBuilder.AddColumn<Guid>(
name: "PositionId1",
table: "Sqaures",
type: "uniqueidentifier",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Sqaures_PositionId1",
table: "Sqaures",
column: "PositionId1");
migrationBuilder.AddForeignKey(
name: "FK_Sqaures_Positions_PositionId1",
table: "Sqaures",
column: "PositionId1",
principalTable: "Positions",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
//there is also ```"PositionId" in other migration
我建议改为查看此图片,因为它说明得更好:
方块table:
位置table:
如您所见,PositionId1
、PositionId
和 BoardId
都在其中,即使它们没有显示。 (只有 PositionId
应该存在,作为一对多关系的一部分)
这导致了一些实际问题,因为在使用 EFCore 重试 Position
实体时,我得到了所有平方重复(两次)
这是为什么?我该如何解决?我尝试将显式类型和 FK 名称添加到一对多关系,更改配置顺序等。没有任何效果。
以下是实体:
正方形:
public record Square(Piece? Occupier, int Row, int File) : IComparable<Square>
{
private Square() : this(returnNull())
{
}
private static Piece returnNull()
{
return null;
}
public int Row
{
get; set;
} = Row;
public int File
{
get; set;
} = File;
public Square(Piece? occupier) : this(occupier, 0, 0)
{
this.Occupier = occupier;
}
public void SetLocation(int row, int file)
{
Row = row;
File = file;
}
public int CompareTo(Square? other)
{
if(other is null)
return 1;
int result = this.Row.CompareTo(other.Row);
if(result != 0)
return result;
return this.File.CompareTo(other.File);
}
}
职位:
public record Position
{
private List<Square> squares;
public List<Square> Squares
{
get
{
return squares;
}
}
private int At(int row, int file)
{
return row * 8 + file;
}
private bool canWhiteCastleKingSide;
private bool canWhiteCastleQueenSide;
private bool canBlackCastleKingSide;
private bool canBlackCastleQueenSide;
private Square? enPassentTakablePawnOfWhite;
private Square? enPassentTakablePawnOfBlack;
private const int RowAmount = 8;
private const int FileAmount = 8;
private int _50MovesRuleCounter;
private readonly IDictionary<PieceType, Func<int, int, IEnumerable<Ply>>> possibleMovesGenerationFunctionsDictinary;
//.....
编辑:当我尝试删除Positions
和squares
的整个类型配置(PK配置除外)时,从方格到位置的FK仍然存在(只有其中一个)
此外,当我尝试删除 Squares
public 属性 时,在删除所有类型配置后,FK to position 确实消失了,但是 FK to board (根本没有被引用,也不应该在这里)仍然存在。将 public 属性 的名称从 Squares
更改为其他内容并没有任何改变,我仍然需要这个 属性 所以我不能删除它。将其标记为 [NoMapped] 会导致相同的结果
你的PositionTypeConfiguration
class明确定义了BoardId
。如果您不需要它,只需从 PositionTypeConfiguration
.
中删除 builder.Property<Guid>("BoardId");
现在关于 PositionId1
、PositionId
问题。
你用这一行创建外键
builder.HasMany<Square>("squares").WithOne().HasForeignKey("PositionId");
并且您的 Position
class
中的 square
集合有以下字段和 属性
private List<Square> squares;
public List<Square> Squares
{
get
{
return squares;
}
}
问题是您手动为私有字段创建外键,而 EF 自动为 public 属性 创建外键。因此,您配置了 2 个外键,因此配置了 2 个属性 PositionId1
和 PositionId
要解决此问题,请使用此行进行配置
builder.HasMany<Square>(x => x.Squares).WithOne().HasForeignKey("PositionId");
并删除私有字段,如果您希望它是 immutable
,则可以改用私有集
public class Position
{
public List<Square> Squares { get; private set; }
}
我也没有在 Square
class 上看到 PositionId
属性,没有它你将无法使用你的模型,因为您已将此列配置为不可为空。
关于 record
类型的最后一件事。
EF 核心目前不支持记录类型的更改跟踪,因此如果您需要,最好使用常规 classes
编辑:
我在您发布的代码中没有看到任何可以导致 BoardId
出现在 Squares
table 中的内容。但有可能是您项目中的某些其他配置创建了它。
主要原因在这个代码块
public class PositionTypeConfiguration : IEntityTypeConfiguration<Position>
{
public void Configure(EntityTypeBuilder<Position> builder)
{
builder.ToTable("Positions");
builder.Property<Guid>("BoardId");
builder.Property<Guid?>("CurrentPositionId");
builder.HasOne<Board>().WithMany("previosPositions").HasForeignKey("BoardId");
builder.HasOne<Board>().WithOne("currentPosition").IsRequired(false).HasForeignKey<Position>("CurrentPositionId");
builder.HasMany<Square>("squares").WithOne().HasForeignKey("PositionId");
builder.Property<Guid?>("EnPassentTakableWhitePawnId");
builder.Property<Guid?>("EnPassentTakableBlackPawnId");
builder.HasOne<Square?>("enPassentTakablePawnOfWhite").WithOne().HasForeignKey<Position>("EnPassentTakableWhitePawnId");
builder.HasOne<Square?>("enPassentTakablePawnOfBlack").WithOne().HasForeignKey<Position>("EnPassentTakableBlackPawnId");
builder.Property("canWhiteCastleKingSide");
builder.Property("canWhiteCastleQueenSide");
builder.Property("canBlackCastleKingSide");
builder.Property("canBlackCastleQueenSide");
builder.Property("_50MovesRuleCounter");
builder.Property<Guid>("Id").ValueGeneratedOnAdd();
builder.HasKey("Id");
}
}
您已经为 Square 添加了外键,但我认为您可能没有注意到它区分大小写,因此您只需将“s”更改为“S”即可。
builder.HasMany<Square>("Squares").WithOne().HasForeignKey("PositionId");
这将使迁移机制意识到注册与public record Position
处的public List<Square> Squares
字段相同。它不会再注册它。或者,您可以删除代码行。
问题是你想在 private List<Square> squares
上注册 ForeignKey 吗?还是只是个案问题?我不太明白你的设计是什么,所以我保留原来的代码形式。
我使用了您提供的代码进行迁移,但是BoardId
字段没有出现在Sqaures
table中。如果还是找不到原因,能否提供一下Square
?
的其他机型配置
我正在尝试配置 EFCore 中实体之间的关系:Square
和 Position
。 Position 与名为 squares
的 square 具有一对一的关系,以及名为 enPassentTakablePawnOfWhite
和 enPassentTakablePawnOfBlack
的 Square
之间的两个一对一关系。请注意,两个实体中的 FK 和 PK 都是影子属性:
public class PositionTypeConfiguration : IEntityTypeConfiguration<Position>
{
public void Configure(EntityTypeBuilder<Position> builder)
{
builder.ToTable("Positions");
builder.Property<Guid>("BoardId");
builder.Property<Guid?>("CurrentPositionId");
builder.HasOne<Board>().WithMany("previosPositions").HasForeignKey("BoardId");
builder.HasOne<Board>().WithOne("currentPosition").IsRequired(false).HasForeignKey<Position>("CurrentPositionId");
builder.HasMany<Square>("squares").WithOne().HasForeignKey("PositionId");
builder.Property<Guid?>("EnPassentTakableWhitePawnId");
builder.Property<Guid?>("EnPassentTakableBlackPawnId");
builder.HasOne<Square?>("enPassentTakablePawnOfWhite").WithOne().HasForeignKey<Position>("EnPassentTakableWhitePawnId");
builder.HasOne<Square?>("enPassentTakablePawnOfBlack").WithOne().HasForeignKey<Position>("EnPassentTakableBlackPawnId");
builder.Property("canWhiteCastleKingSide");
builder.Property("canWhiteCastleQueenSide");
builder.Property("canBlackCastleKingSide");
builder.Property("canBlackCastleQueenSide");
builder.Property("_50MovesRuleCounter");
builder.Property<Guid>("Id").ValueGeneratedOnAdd();
builder.HasKey("Id");
}
}
public class SqaureTypeConfiguration : IEntityTypeConfiguration<Square>
{
public void Configure(EntityTypeBuilder<Square> builder)
{
builder.ToTable("Sqaures");
builder.Property(s => s.File);
builder.Property<Guid>("PositionId");
builder.Property(s => s.Row);
//this is FK to some other, irrelavant entity.
builder.HasOne(s => s.Occupier).WithOne().IsRequired(false).HasForeignKey<Square>("OccupierId").IsRequired(false);
builder.Property<Guid>("Id");
builder.HasKey("Id");
builder.Property<Guid?>("OccupierId").IsRequired(false);
}
}
问题是,在生成迁移时,迁移在 Square
中为 Position
创建了以下两个 FK:
public partial class ChangedBoard : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
/...
migrationBuilder.AddColumn<Guid>(
name: "PositionId1",
table: "Sqaures",
type: "uniqueidentifier",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Sqaures_PositionId1",
table: "Sqaures",
column: "PositionId1");
migrationBuilder.AddForeignKey(
name: "FK_Sqaures_Positions_PositionId1",
table: "Sqaures",
column: "PositionId1",
principalTable: "Positions",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
//there is also ```"PositionId" in other migration
我建议改为查看此图片,因为它说明得更好:
方块table:
位置table:
如您所见,PositionId1
、PositionId
和 BoardId
都在其中,即使它们没有显示。 (只有 PositionId
应该存在,作为一对多关系的一部分)
这导致了一些实际问题,因为在使用 EFCore 重试 Position
实体时,我得到了所有平方重复(两次)
这是为什么?我该如何解决?我尝试将显式类型和 FK 名称添加到一对多关系,更改配置顺序等。没有任何效果。
以下是实体:
正方形:
public record Square(Piece? Occupier, int Row, int File) : IComparable<Square>
{
private Square() : this(returnNull())
{
}
private static Piece returnNull()
{
return null;
}
public int Row
{
get; set;
} = Row;
public int File
{
get; set;
} = File;
public Square(Piece? occupier) : this(occupier, 0, 0)
{
this.Occupier = occupier;
}
public void SetLocation(int row, int file)
{
Row = row;
File = file;
}
public int CompareTo(Square? other)
{
if(other is null)
return 1;
int result = this.Row.CompareTo(other.Row);
if(result != 0)
return result;
return this.File.CompareTo(other.File);
}
}
职位:
public record Position
{
private List<Square> squares;
public List<Square> Squares
{
get
{
return squares;
}
}
private int At(int row, int file)
{
return row * 8 + file;
}
private bool canWhiteCastleKingSide;
private bool canWhiteCastleQueenSide;
private bool canBlackCastleKingSide;
private bool canBlackCastleQueenSide;
private Square? enPassentTakablePawnOfWhite;
private Square? enPassentTakablePawnOfBlack;
private const int RowAmount = 8;
private const int FileAmount = 8;
private int _50MovesRuleCounter;
private readonly IDictionary<PieceType, Func<int, int, IEnumerable<Ply>>> possibleMovesGenerationFunctionsDictinary;
//.....
编辑:当我尝试删除Positions
和squares
的整个类型配置(PK配置除外)时,从方格到位置的FK仍然存在(只有其中一个)
此外,当我尝试删除 Squares
public 属性 时,在删除所有类型配置后,FK to position 确实消失了,但是 FK to board (根本没有被引用,也不应该在这里)仍然存在。将 public 属性 的名称从 Squares
更改为其他内容并没有任何改变,我仍然需要这个 属性 所以我不能删除它。将其标记为 [NoMapped] 会导致相同的结果
你的PositionTypeConfiguration
class明确定义了BoardId
。如果您不需要它,只需从 PositionTypeConfiguration
.
builder.Property<Guid>("BoardId");
现在关于 PositionId1
、PositionId
问题。
你用这一行创建外键
builder.HasMany<Square>("squares").WithOne().HasForeignKey("PositionId");
并且您的 Position
class
square
集合有以下字段和 属性
private List<Square> squares;
public List<Square> Squares
{
get
{
return squares;
}
}
问题是您手动为私有字段创建外键,而 EF 自动为 public 属性 创建外键。因此,您配置了 2 个外键,因此配置了 2 个属性 PositionId1
和 PositionId
要解决此问题,请使用此行进行配置
builder.HasMany<Square>(x => x.Squares).WithOne().HasForeignKey("PositionId");
并删除私有字段,如果您希望它是 immutable
,则可以改用私有集public class Position
{
public List<Square> Squares { get; private set; }
}
我也没有在 Square
class 上看到 PositionId
属性,没有它你将无法使用你的模型,因为您已将此列配置为不可为空。
关于 record
类型的最后一件事。
EF 核心目前不支持记录类型的更改跟踪,因此如果您需要,最好使用常规 classes
编辑:
我在您发布的代码中没有看到任何可以导致 BoardId
出现在 Squares
table 中的内容。但有可能是您项目中的某些其他配置创建了它。
主要原因在这个代码块
public class PositionTypeConfiguration : IEntityTypeConfiguration<Position>
{
public void Configure(EntityTypeBuilder<Position> builder)
{
builder.ToTable("Positions");
builder.Property<Guid>("BoardId");
builder.Property<Guid?>("CurrentPositionId");
builder.HasOne<Board>().WithMany("previosPositions").HasForeignKey("BoardId");
builder.HasOne<Board>().WithOne("currentPosition").IsRequired(false).HasForeignKey<Position>("CurrentPositionId");
builder.HasMany<Square>("squares").WithOne().HasForeignKey("PositionId");
builder.Property<Guid?>("EnPassentTakableWhitePawnId");
builder.Property<Guid?>("EnPassentTakableBlackPawnId");
builder.HasOne<Square?>("enPassentTakablePawnOfWhite").WithOne().HasForeignKey<Position>("EnPassentTakableWhitePawnId");
builder.HasOne<Square?>("enPassentTakablePawnOfBlack").WithOne().HasForeignKey<Position>("EnPassentTakableBlackPawnId");
builder.Property("canWhiteCastleKingSide");
builder.Property("canWhiteCastleQueenSide");
builder.Property("canBlackCastleKingSide");
builder.Property("canBlackCastleQueenSide");
builder.Property("_50MovesRuleCounter");
builder.Property<Guid>("Id").ValueGeneratedOnAdd();
builder.HasKey("Id");
}
}
您已经为 Square 添加了外键,但我认为您可能没有注意到它区分大小写,因此您只需将“s”更改为“S”即可。
builder.HasMany<Square>("Squares").WithOne().HasForeignKey("PositionId");
这将使迁移机制意识到注册与public record Position
处的public List<Square> Squares
字段相同。它不会再注册它。或者,您可以删除代码行。
问题是你想在 private List<Square> squares
上注册 ForeignKey 吗?还是只是个案问题?我不太明白你的设计是什么,所以我保留原来的代码形式。
我使用了您提供的代码进行迁移,但是BoardId
字段没有出现在Sqaures
table中。如果还是找不到原因,能否提供一下Square
?