EF6自动生成Author_Books1表名引发SaveChanges报错,求原因与解决
问题原因
你遇到的dbo.Author_Books1表不存在的问题,本质是EF6同时识别了两个多对多关系:
- 你在
Author和Book实体中定义了双向集合导航属性(Author.Books和Book.Authors),EF6会默认将其识别为一个自动管理的多对多关系,并自动生成默认命名的中间表(规则是两个实体名按字母排序拼接,这里就是Author_Books,但因为你手动定义了同名的中间实体,EF会自动加后缀1避免冲突,所以变成了Author_Books1)。 - 你又手动定义了
AuthorBook实体,并在DbSet中注册、配置了对应表Author_Books,EF将这视为另一个独立的关系。
两种关系冲突导致EF在保存时尝试访问自动生成的、实际不存在的Author_Books1表。
解决方法
你需要二选一:要么用EF自动管理的多对多,要么用手动定义的中间实体,不能混合使用。
方案一:使用EF自动管理的多对多关系(推荐,代码更简洁)
这种方式不需要手动定义中间实体,由EF自动维护中间表:
- 删除
AuthorBook实体类。 - 删除
AppDBContext中的public DbSet<AuthorBook> AuthorBooks{ get; set; }。 - 在
OnModelCreating中配置多对多关系,指定中间表名和外键列名:
// 配置Author和Book的多对多关系 modelBuilder.Entity<Author>() .HasMany(a => a.Books) .WithMany(b => b.Authors) .Map(m => { m.ToTable("Author_Books"); // 指定中间表名 m.MapLeftKey("Author_AuthorId"); // 对应Author表的外键列名 m.MapRightKey("Book_BookId"); // 对应Book表的外键列名 });
- 关联作者和书籍的代码保持不变,但去掉不必要的
db.Entry(newbook).State = System.Data.Entity.EntityState.Modified;(因为只是添加关联,没有修改书籍本身):
// Parse column for multiple possible books if (worksheet.Cells[i, 3].Value != null) { string[] possibleBooks = Regex.Split(worksheet.Cells[i, 3].Value.ToString(), ", "); foreach (string book in possibleBooks) { Book newbook = db.Books.Where(c => c.BookName == book).FirstOrDefault(); if (newbook != null) // 避免空引用 { Author.Books.Add(newbook); } } }
方案二:使用手动定义的中间实体(适合需要在中间表存储额外字段的场景)
如果需要在中间表添加更多字段(比如关联时间),可以用这种方式:
- 修改
Author实体,将List<Book>改为List<AuthorBook>:
[Table("Authors")] public class Author { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid AuthorId{ get; set; } public string AuthorName{ get; set; } public List<AuthorBook> AuthorBooks { get; set; } }
- 修改
Book实体,将List<Author>改为List<AuthorBook>,同时修正Bookname的类型错误(原代码是Guid,应该是string):
[Table("Books")] public class Book { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid BookId{ get; set; } public string BookName{ get; set; } // 修正类型为string public List<AuthorBook> AuthorBooks { get; set; } }
- 完善
AuthorBook实体,添加外键对应的导航属性(可选但更规范):
[Table("Author_Books")] public class AuthorBook { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid AuthorBookId{ get; set; } public Guid Author_AuthorId{ get; set; } public Guid Book_BookId{ get; set; } // 添加导航属性,关联到Author和Book [ForeignKey("Author_AuthorId")] public Author Author { get; set; } [ForeignKey("Book_BookId")] public Book Book { get; set; } }
- 在
OnModelCreating中配置外键关系,替换原有的AuthorBook配置:
// 配置AuthorBook与Author的关联 modelBuilder.Entity<AuthorBook>() .HasRequired(ab => ab.Author) .WithMany(a => a.AuthorBooks) .HasForeignKey(ab => ab.Author_AuthorId); // 配置AuthorBook与Book的关联 modelBuilder.Entity<AuthorBook>() .HasRequired(ab => ab.Book) .WithMany(b => b.AuthorBooks) .HasForeignKey(ab => ab.Book_BookId); // 保留字段类型配置 modelBuilder.Entity<AuthorBook>().Property(p => p.AuthorBookId).HasColumnType("UniqueIdentifier").IsRequired(); modelBuilder.Entity<AuthorBook>().Property(p => p.Author_AuthorId).HasColumnType("UniqueIdentifier").IsRequired(); modelBuilder.Entity<AuthorBook>().Property(p => p.Book_BookId).HasColumnType("UniqueIdentifier").IsRequired();
- 修改关联作者和书籍的代码,创建
AuthorBook实例添加到集合:
// Parse column for multiple possible books if (worksheet.Cells[i, 3].Value != null) { string[] possibleBooks = Regex.Split(worksheet.Cells[i, 3].Value.ToString(), ", "); foreach (string book in possibleBooks) { Book newbook = db.Books.Where(c => c.BookName == book).FirstOrDefault(); if (newbook != null) { Author.AuthorBooks.Add(new AuthorBook { Author_AuthorId = Author.AuthorId, Book_BookId = newbook.BookId }); } } }
额外注意事项
- 原
Book实体中的Bookname字段类型为Guid,这明显不符合业务逻辑(书籍名称是字符串),必须修正为string,否则无法匹配Excel中读取的书籍名称。 - 保存作者时,不需要设置关联书籍的
Modified状态,除非你确实修改了书籍的属性。
内容的提问来源于stack exchange,提问作者SalarianEngineer
相关产品推荐
相关产品推荐

