You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core插入Item时遇IDENTITY_INSERT及对象名无效问题求助

让我们一步步拆解你的问题,先搞定之前的IDENTITY_INSERT坑,再解决现在的Invalid object name 'Game'错误。


关于IDENTITY_INSERT的问题

你一开始的误区在于完全没必要给Tournaments和Games表开启IDENTITY_INSERT——你根本没有插入这两个表的新数据,只是从它们各自的上下文里取出已存在的实体关联到Item上。

为什么会触发关联表的IDENTITY_INSERT提示?大概率是EF把你从其他上下文获取的Tournament/Game实体当成了新实体,尝试自动插入它们。解决这个的正确姿势是:把这些关联实体附加到ItemDbContext里,并标记为Unchanged,明确告诉EF它们已经存在于数据库中,不需要插入。

另外,SQL Server有个硬性规定:一个数据库连接会话里只能给一个表开启IDENTITY_INSERT,这就是你尝试给第二个表开启时报错的核心原因。

你后来删迁移重弄的方法虽然解决了问题,但其实更优雅的写法应该是这样(不需要手动管理连接,用事务确保IDENTITY_INSERT的开关在同一会话内):

if (!context.Items.Any())
{
    // 从其他上下文取已存在的实体
    var tournament = _tournamentDbContext.Tournaments.First(x => x.Id == 1);
    var game = _gameDbContext.Games.First(x => x.Id == 1);
    var itemType = context.ItemTypes.First(x => x.Id == 1);

    // 把关联实体附加到当前上下文,标记为已存在状态
    context.Attach(tournament).State = EntityState.Unchanged;
    context.Attach(game).State = EntityState.Unchanged;

    // 如果Item的Id是自增列,建议不要手动指定Id,除非有特殊业务需求
    var item = new Models.Items.Item
    {
        Name = "Test",
        ItemType = itemType,
        Price = 100,
        Short_Description = "TestShort",
        Tournament = tournament,
        Game = game,
        DateCreated = DateTime.Now
    };

    context.Items.Add(item);

    // 必须手动指定Item的Id时,用事务包裹IDENTITY_INSERT的开关操作
    using (var transaction = context.Database.BeginTransaction())
    {
        context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT [Items].[Items] ON;");
        context.SaveChanges();
        context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT [Items].[Items] OFF;");
        transaction.Commit();
    }
}

现在的Invalid object name 'Game'错误

这个问题的核心是你的ItemDbContext不知道Game和Tournament实体的数据库映射规则。虽然你从其他上下文获取了这些实体,但当ItemDbContext生成保存Item的SQL时,它需要知道Game表的正确名称和Schema,否则会默认去找不带Schema的Game表,而你的Game表显然是在Games Schema下的。

推荐两种解决办法:

方法1:在ItemDbContext里配置关联实体的映射

不需要添加DbSet,只需要在OnModelCreating里告诉EF这两个实体的表位置:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Item>().ToTable("Item", Schema);
    modelBuilder.Entity<ItemType>().ToTable("ItemType", Schema);
    modelBuilder.Entity<ItemTag>().ToTable("ItemTag", Schema);

    // 配置Tournament和Game的表映射(根据你的实际Schema和表名调整)
    modelBuilder.Entity<Tournament>().ToTable("Tournaments", "Tournaments");
    modelBuilder.Entity<Game>().ToTable("Game", "Games");

    // 显式配置外键关联(如果Tournament/Game和Item是一对多关系,调整WithMany的参数)
    modelBuilder.Entity<Item>()
        .HasOne(i => i.Tournament)
        .WithMany()
        .HasForeignKey("TournamentId");

    modelBuilder.Entity<Item>()
        .HasOne(i => i.Game)
        .WithMany()
        .HasForeignKey("GameId");
}

方法2:给Item类添加显式外键字段(更推荐)

这种方式能避免EF的隐式外键推断错误,代码可读性也更高:

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Short_Description { get; set; }
    public decimal Price { get; set; }
    
    // 添加显式外键字段
    public int ItemTypeId { get; set; }
    public int TournamentId { get; set; }
    public int GameId { get; set; }
    
    public ItemType ItemType { get; set; }
    public Tournament Tournament { get; set; }
    public Game Game { get; set; }
    [DataType(DataType.Date)]
    public DateTime DateCreated { get; set; }
}

之后在OnModelCreating里配置外键关联,EF就能准确生成SQL,不会找不到Game表了。

额外小技巧:用Id关联而非实体

跨上下文操作时,直接用Id赋值比传递实体更简单,还能避免上下文跟踪的问题:

var item = new Models.Items.Item
{
    Name = "Test",
    ItemTypeId = 1,
    TournamentId = 1,
    GameId = 1,
    Price = 100,
    Short_Description = "TestShort",
    DateCreated = DateTime.Now
};
context.Items.Add(item);
context.SaveChanges();

内容的提问来源于stack exchange,提问作者Ashley

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:47:20