EDIT: Issue doesn’t exist if I remove RelatedItems
property.
Upon creating a record of entity type PlayerFurnitureItemPlacementData
, it tries to insert using a non existing column.
Signature generated:
INSERT INTO `player_furniture_item_placement_data` (`created_at`, `direction`, `player_furniture_item_id`, `player_furniture_item_wired_data_id`, `position_x`, `position_y`, `position_z`, `room_id`, `wall_position`)
player_furniture_item_wired_data_id
has never existed, where has EF Core got this from?
Full code:
new PlayerFurnitureItemPlacementData
{
RoomId = room.Id,
PlayerFurnitureItemId = playerItem.Id,
PlayerFurnitureItem = playerItem,
PositionX = x,
PositionY = y,
PositionZ = z,
WallPosition = string.Empty,
Direction = (HDirection) direction,
CreatedAt = DateTime.Now
};
dbContext.Entry(roomFurnitureItem.PlayerFurnitureItem).State = EntityState.Unchanged;
dbContext.RoomFurnitureItems.Add(roomFurnitureItem);
Entities (truncated if unrelated):
public class PlayerFurnitureItemPlacementData
{
[Key]
public int Id { get; init; }
public int PlayerFurnitureItemId { get; init; }
public required PlayerFurnitureItem PlayerFurnitureItem { get; init; }
public int RoomId { get; init; }
public Room? Room { get; init; }
}
public class PlayerFurnitureItem
{
public int Id { get; init; }
public int PlayerId { get; set; }
public Player? Player { get; set; }
public FurnitureItem? FurnitureItem { get; init; }
public PlayerFurnitureItemPlacementData? PlacementData { get; set; }
public PlayerFurnitureItemWiredData? WiredData { get; set; }
public DateTime CreatedAt { get; init; }
}
public class PlayerFurnitureItemWiredData
{
[Key]
public int Id { get; init; }
public int PlayerFurnitureItemId { get; init; }
public required PlayerFurnitureItem PlayerFurnitureItem { get; init; }
public required ICollection<PlayerFurnitureItemPlacementData> RelatedItems { get; init; }
}
When you introduced:
public required ICollection<PlayerFurnitureItemPlacementData> RelatedItems
on the Player Furniture Item Wired Data, EF is looking for a way to wire that up. It either forms a many-to-one or a many-to-many relationship which you have not configured, so EF assumes a many-to-one. For one of these related items to be associated to the wired data, it needs a FK to the wired data so EF created one. If there is a different column on the table that EF should use you can either create the FK and associate it, as per Esanju’s answer, or if you just want the association to use the column without the FK property exposed or a navigation property on the other end, you can use a shadow property for the FK:
modelBuilder.Entity<PlayerFurnitureItemWiredData>()
.HasMany(x => x.RelatedItems)
.WithOne() // no navigation property on other side.
.HasForeignKey("PlayerFurnitureItemWiredDataId"); // Insert whichever FK which points back to the wired data.
This tells EF which column to use on the table for the FK without exposing it or the other side of the relationship in the entity.
The Entity Framework (EF) automatically constructs foreign keys and joins from the navigation properties and their related entities so with explicitly definition, it should solve the issue.
PlayerFurnitureItemPlacementData class
public int? PlayerFurnitureItemWiredDataId { get; set; }
public PlayerFurnitureItemWiredData? PlayerFurnitureItemWiredData { get; set; }
Define the relationship explicitly on the OnModelBinding Method
modelBuilder.Entity<PlayerFurnitureItemPlacementData>()
.HasOne(p => p.PlayerFurnitureItemWiredData)
.WithMany(w => w.RelatedItems)
.HasForeignKey(p => p.PlayerFurnitureItemWiredDataId);