EF Core 8:创建记录会为每个关系创建一条新记录?

问题描述 投票:0回答:1

当我创建实体的新记录时

Room
我收到此错误:

Microsoft.EntityFrameworkCore.DbUpdateException:发生错误 同时保存实体更改。有关详细信息,请参阅内部异常。

MySqlConnector.MySqlException(0x80004005):字段“密码”不存在 有一个默认值

这是因为

Room
有一个所有者(
Player
实体),它在尝试创建自身时尝试创建该所有者。我想创建房间并能够分配关系,不应该创建它们。

这是其运行的查询:

INSERT INTO `players` (`id`, `created_at`, `email`, `username`)
VALUES (@p0, @p1, @p2, @p3);
INSERT INTO `player_avatar_data` (`id`, `chat_bubble_id`, `figure_code`, `gender`, `motto`, `player_id`)
VALUES (@p4, @p5, @p6, @p7, @p8, @p9);
INSERT INTO `player_data` (`id`, `achievement_score`, `allow_friend_requests`, `credit_balance`, `gotw_points`, `home_room_id`, `is_online`, `last_online`, `pixel_balance`, `player_id`, `respect_points`, `respect_points_pet`, `seasonal_balance`)
VALUES (@p10, @p11, @p12, @p13, @p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21, @p22);
INSERT INTO `player_game_settings` (`id`, `block_camera_follow`, `block_room_invites`, `furniture_volume`, `player_id`, `prefer_old_chat`, `show_notifications`, `system_volume`, `trax_volume`, `ui_flags`)
VALUES (@p23, @p24, @p25, @p26, @p27, @p28, @p29, @p30, @p31, @p32);
INSERT INTO `player_navigator_settings` (`id`, `open_searches`, `player_id`, `window_height`, `window_width`, `window_x`, `window_y`)
VALUES (@p33, @p34, @p35, @p36, @p37, @p38, @p39);
INSERT INTO `rooms` (`created_at`, `description`, `is_muted`, `layout_id`, `max_users_allowed`, `name`, `owner_id`)
VALUES (@p40, @p41, @p42, @p43, @p44, @p45, @p46);
SELECT `id`
FROM `rooms`
WHERE ROW_COUNT() = 1 AND `id` = LAST_INSERT_ID();

INSERT INTO `rooms` (`id`, `created_at`, `description`, `is_muted`, `layout_id`, `max_users_allowed`, `name`, `owner_id`)
VALUES (@p47, @p48, @p49, @p50, @p51, @p52, @p53, @p54),
(@p55, @p56, @p57, @p58, @p59, @p60, @p61, @p62);
INSERT INTO `room_settings` (`id`, `access_type`, `allow_pets`, `can_pets_eat`, `can_users_overlap`, `floor_thickness`, `hide_walls`, `password`, `room_id`, `trade_option`, `walk_diagonal`, `wall_thickness`, `who_can_ban`, `who_can_kick`, `who_can_mute`)
VALUES (@p63, @p64, @p65, @p66, @p67, @p68, @p69, @p70, @p71, @p72, @p73, @p74, @p75, @p76, @p77),
(@p78, @p79, @p80, @p81, @p82, @p83, @p84, @p85, @p86, @p87, @p88, @p89, @p90, @p91, @p92);
[08:20:30 ERR] Failed executing DbCommand (126ms) [Parameters=[@p0='?' (DbType = Int32), @p1='?' (DbType = DateTime), @p2='?' (Size = 4000), @p3='?' (Size = 4000), @p4='?' (DbType = Int32), @p5='?' (DbType = Int32), @p6='?' (Size = 4000), @p7='?' (Size = 4000), @p8='?' (Size = 4000), @p9='?' (DbType = Int32), @p10='?' (DbType = Int32), @p11='?' (DbType = Int32), @p12='?' (DbType = Boolean), @p13='?' (DbType = Int32), @p14='?' (DbType = Int32), @p15='?' (DbType = Int32), @p16='?' (DbType = Boolean), @p17='?' (DbType = DateTime), @p18='?' (DbType = Int32), @p19='?' (DbType = Int32), @p20='?' (DbType = Int32), @p21='?' (DbType = Int32), @p22='?' (DbType = Int32), @p23='?' (DbType = Int32), @p24='?' (DbType = Boolean), @p25='?' (DbType = Boolean), @p26='?' (DbType = Int32), @p27='?' (DbType = Int32), @p28='?' (DbType = Boolean), @p29='?' (DbType = Boolean), @p30='?' (DbType = Int32), @p31='?' (DbType = Int32), @p32='?' (DbType = Int32), @p33='?' (DbType = Int32), @p34='?' (DbType = Boolean), @p35='?' (DbType = Int32), @p36='?' (DbType = Int32), @p37='?' (DbType = Int32), @p38='?' (DbType = Int32), @p39='?' (DbType = Int32), @p40='?' (DbType = DateTime), @p41='?' (Size = 4000), @p42='?' (DbType = Boolean), @p43='?' (DbType = Int32), @p44='?' (DbType = Int32), @p45='?' (Size = 4000), @p46='?' (DbType = Int32), @p47='?' (DbType = Int32), @p48='?' (DbType = DateTime), @p49='?' (Size = 4000), @p50='?' (DbType = Boolean), @p51='?' (DbType = Int32), @p52='?' (DbType = Int32), @p53='?' (Size = 4000), @p54='?' (DbType = Int32), @p55='?' (DbType = Int32), @p56='?' (DbType = DateTime), @p57='?' (Size = 4000), @p58='?' (DbType = Boolean), @p59='?' (DbType = Int32), @p60='?' (DbType = Int32), @p61='?' (Size = 4000), @p62='?' (DbType = Int32), @p63='?' (DbType = Int32), @p64='?' (DbType = Int32), @p65='?' (DbType = Boolean), @p66='?' (DbType = Boolean), @p67='?' (DbType = Boolean), @p68='?' (DbType = Int32), @p69='?' (DbType = Boolean), @p70='?' (Size = 4000), @p71='?' (DbType = Int32), @p72='?' (DbType = Int32), @p73='?' (DbType = Boolean), @p74='?' (DbType = Int32), @p75='?' (DbType = Int32), @p76='?' (DbType = Int32), @p77='?' (DbType = Int32), @p78='?' (DbType = Int32), @p79='?' (DbType = Int32), @p80='?' (DbType = Boolean), @p81='?' (DbType = Boolean), @p82='?' (DbType = Boolean), @p83='?' (DbType = Int32), @p84='?' (DbType = Boolean), @p85='?' (Size = 4000), @p86='?' (DbType = Int32), @p87='?' (DbType = Int32), @p88='?' (DbType = Boolean), @p89='?' (DbType = Int32), @p90='?' (DbType = Int32), @p91='?' (DbType = Int32), @p92='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
INSERT INTO `players` (`id`, `created_at`, `email`, `username`)
VALUES (@p0, @p1, @p2, @p3);
INSERT INTO `player_avatar_data` (`id`, `chat_bubble_id`, `figure_code`, `gender`, `motto`, `player_id`)
VALUES (@p4, @p5, @p6, @p7, @p8, @p9);
INSERT INTO `player_data` (`id`, `achievement_score`, `allow_friend_requests`, `credit_balance`, `gotw_points`, `home_room_id`, `is_online`, `last_online`, `pixel_balance`, `player_id`, `respect_points`, `respect_points_pet`, `seasonal_balance`)
VALUES (@p10, @p11, @p12, @p13, @p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21, @p22);
INSERT INTO `player_game_settings` (`id`, `block_camera_follow`, `block_room_invites`, `furniture_volume`, `player_id`, `prefer_old_chat`, `show_notifications`, `system_volume`, `trax_volume`, `ui_flags`)
VALUES (@p23, @p24, @p25, @p26, @p27, @p28, @p29, @p30, @p31, @p32);
INSERT INTO `player_navigator_settings` (`id`, `open_searches`, `player_id`, `window_height`, `window_width`, `window_x`, `window_y`)
VALUES (@p33, @p34, @p35, @p36, @p37, @p38, @p39);
INSERT INTO `rooms` (`created_at`, `description`, `is_muted`, `layout_id`, `max_users_allowed`, `name`, `owner_id`)
VALUES (@p40, @p41, @p42, @p43, @p44, @p45, @p46);
SELECT `id`
FROM `rooms`
WHERE ROW_COUNT() = 1 AND `id` = LAST_INSERT_ID();

INSERT INTO `rooms` (`id`, `created_at`, `description`, `is_muted`, `layout_id`, `max_users_allowed`, `name`, `owner_id`)
VALUES (@p47, @p48, @p49, @p50, @p51, @p52, @p53, @p54),
(@p55, @p56, @p57, @p58, @p59, @p60, @p61, @p62);
INSERT INTO `room_settings` (`id`, `access_type`, `allow_pets`, `can_pets_eat`, `can_users_overlap`, `floor_thickness`, `hide_walls`, `password`, `room_id`, `trade_option`, `walk_diagonal`, `wall_thickness`, `who_can_ban`, `who_can_kick`, `who_can_mute`)
VALUES (@p63, @p64, @p65, @p66, @p67, @p68, @p69, @p70, @p71, @p72, @p73, @p74, @p75, @p76, @p77),
(@p78, @p79, @p80, @p81, @p82, @p83, @p84, @p85, @p86, @p87, @p88, @p89, @p90, @p91, @p92);

以下是需要理解的实体:

public class Room
{
    public int Id { get; init; }
    public string? Name { get; set; }
    public int LayoutId { get; init; }
    public RoomLayout? Layout { get; init; }
    public int OwnerId { get; init; }
    public Player? Owner { get; init; }
    public int MaxUsersAllowed { get; set; }
    public string? Description { get; set; }
    public bool IsMuted { get; init; }
    public RoomSettings? Settings { get; set; }
    public RoomPaintSettings? PaintSettings { get; set; }
    public RoomChatSettings? ChatSettings { get; set; }
    public DateTime CreatedAt { get; init; }
}

这是我创建新房间的方法

var newRoom = new Room
{
    Name = eventParser.Name,
    LayoutId = layout.Id,
    Layout = layout,
    OwnerId = client.Player.Id,
    Owner = client.Player,
    MaxUsersAllowed = 50,
    Description = eventParser.Description,
    CreatedAt = DateTime.Now
};

newRoom.Settings = new RoomSettings
{
    RoomId = newRoom.Id,
    Password = ""
};

newRoom.ChatSettings = new RoomChatSettings
{
    RoomId = newRoom.Id
};

newRoom.PaintSettings = new RoomPaintSettings
{
    RoomId = newRoom.Id,
};

dbContext.Rooms.Add(newRoom);
roomRepository.AddRoom(newRoom);
client.Player.Rooms.Add(newRoom);

await dbContext.SaveChangesAsync();
c# entity-framework-core ef-core-8.0
1个回答
0
投票

当您关联当前

DbContext
未跟踪的实体时,会发生这种情况。

这里一点:

var newRoom = new Room
{
    // ...
    OwnerId = client.Player.Id,
    Owner = client.Player,
    // ...
};

...无论“client.Player”是什么,它都表示此代码正在使用的

DbContext
未跟踪的 Player 实例。因此,虽然它可能代表现有的 Player,但此 DbContext 对待它的方式与您所写的没有什么不同:

Owner = new Player { Id = client.Player.Id, ... },

在引用实体时,您需要使用

DbContext 
正在跟踪的实例。如果您要传递实例来使用,您可以
Attach
它们,以便
DbContext
知道将它们视为现有记录,但您首先需要确保 DbContext 尚未跟踪具有该 Id 的实体,否则您尝试附加它时会出现异常。

 var existingPlayer = dbContext.Players.Local.FirstOrDefault(x => x.Id == client.Player.Id);
 if (existingPlayer = null)
 {
     dbContext.Attach(client.Player);
     existingPlayer = client.Player;
 }

var newRoom = new Room
{
    // ...
    Owner = existingPlayer,
    // ...
};

通过访问

.Local
,这不会访问数据库,它只是告诉 DbContext 检查跟踪缓存。如果我们找到匹配的球员,我们就会使用该参考。如果我们找不到,我们
Attach
我们拥有的参考资料并使用它。

接下来,这一切都太过分了:

dbContext.Rooms.Add(newRoom);
roomRepository.AddRoom(newRoom);
client.Player.Rooms.Add(newRoom);

如果您正在使用

DbContext
及其
DbSet
那么拥有存储库绝对没有意义。
dbContext.Rooms.Add(newRoom);
足以确保添加房间。如果您确实需要继续
client.Player.Rooms
,那么可能也需要该行,但我不建议缓存实体,因为这会带来保持多个事实来源同步的问题。通过 Id 从数据库中获取数据非常高效,因此无需缓存/传递整个实体图,这可能会导致数据的陈旧或不完整表示。一个更简单的选择是,每当您想要更新数据的当前状态并传递所需数据的简化投影而不是整个实体及其关系时,只需转到
DbContext

© www.soinside.com 2019 - 2024. All rights reserved.