EF Core 6 报错Invalid column name 'CustomerId1' 及关联配置问题
要构建客户间的父子关联关系,定义实体类后遇到EF Core配置错误,以下是问题过程及解决方案:
初始实体类与运行错误
最初定义的实体类:
public class Customer { [Key] public int Id {get; set;} [Required(ErrorMessage = "Name is required")] [MaxLength(100, ErrorMessage="Name cannot be longer than 100 characters")] public string Name {get; set;} public IEnumerable<CustomerLink> CustomerLinks{get; set;} [MaxLength(200, ErrorMessage="Create User cannot be longer than 200 characters")] public string CreateUser {get; set;} public DateTime? CreateDate {get; set;} [Required(ErrorMessage = "Modify User is required")] [MaxLength(200, ErrorMessage="Modify User cannot be longer than 200 characters")] public string ModifyUser {get; set;} public DateTime? ModifyDate {get; set;} } public class CustomerLink { [Key] public int Id {get; set;} [Required(ErrorMessage = "Customer is required")] public Customer Customer {get; set;} [Required(ErrorMessage = "Child is required")] public virtual Customer Child {get; set;} [MaxLength(200, ErrorMessage="Create User cannot be longer than 200 characters")] public string CreateUser {get; set;} public DateTime? CreateDate {get; set;} [Required(ErrorMessage = "Modify User is required")] [MaxLength(200, ErrorMessage="Modify User cannot be longer than 200 characters")] public string ModifyUser {get; set;} public DateTime? ModifyDate {get; set;} }
运行时抛出错误:
Microsoft.Data.SqlClient.SqlException (0x80131904): Invalid column name 'CustomerId1'.
初始的OnModelCreating配置:
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<CustomerLink>() .HasOne(l => l.Customer) .WithMany() .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity<CustomerLink>() .HasOne(l => l.Child) .WithMany() .OnDelete(DeleteBehavior.Restrict); }
调整配置后又出现新错误:
'Customer' cannot be used as a property on entity type 'CustomerLink' because it is configured as a navigation.
'ReferenceNavigationBuilder<CustomerLink, Customer>' does not contain a definition for 'HasForeignKey'...
添加外键属性后的尝试
根据建议修改实体类,添加CustomerId和ChildId外键属性:
public class Customer { [Key] public int Id {get; set;} [Required(ErrorMessage = "Name is required")] [MaxLength(100, ErrorMessage="Name cannot be longer than 100 characters")] public string Name {get; set;} public List<CustomerLink> CustomerLinks{get; set;} [MaxLength(200, ErrorMessage="Create User cannot be longer than 200 characters")] public string CreateUser {get; set;} public DateTime? CreateDate {get; set;} [Required(ErrorMessage = "Modify User is required")] [MaxLength(200, ErrorMessage="Modify User cannot be longer than 200 characters")] public string ModifyUser {get; set;} public DateTime? ModifyDate {get; set;} } public class CustomerLink { [Key] public int Id {get; set;} public int CustomerId {get; set;} [Required(ErrorMessage = "Customer is required")] public Customer Customer {get; set;} public int ChildId {get; set;} [Required(ErrorMessage = "Child is required")] public virtual Customer Child {get; set;} [MaxLength(200, ErrorMessage="Create User cannot be longer than 200 characters")] public string CreateUser {get; set;} public DateTime? CreateDate {get; set;} [Required(ErrorMessage = "Modify User is required")] [MaxLength(200, ErrorMessage="Modify User cannot be longer than 200 characters")] public string ModifyUser {get; set;} public DateTime? ModifyDate {get; set;} }
更新后的配置:
modelBuilder.Entity<CustomerLink>() .HasOne(l => l.Customer) .WithMany(c => c.CustomerLinks) .HasForeignKey(l => l.CustomerId); modelBuilder.Entity<CustomerLink>() .HasOne(l => l.Child) .WithMany(c => c.CustomerLinks) .HasForeignKey(l => l.ChildId);
生成迁移时再次报错:
Cannot create a relationship between 'Customer.CustomerLinks' and 'CustomerLink.Child' because a relationship already exists between 'Customer.CustomerLinks' and 'CustomerLink.Customer'. Navigations can only participate in a single relationship. If you want to override an existing relationship call 'Ignore' on the navigation 'CustomerLink.Child' first in 'OnModelCreating'.
现有数据库结构
当前数据库的建表语句:
CREATE TABLE [QMR].[Customers]( [Id] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](100) NOT NULL, [CreateUser] [nvarchar](200) NULL, [CreateDate] [datetime2](7) NULL, [ModifyUser] [nvarchar](200) NOT NULL, [ModifyDate] [datetime2](7) NULL, CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF ) ON [PRIMARY] ) ON [PRIMARY] GO CREATE TABLE [QMR].[CustomerLinks]( [Id] [int] IDENTITY(1,1) NOT NULL, [CustomerId] [int] NOT NULL, [ChildId] [int] NOT NULL, [CreateUser] [nvarchar](200) NULL, [CreateDate] [datetime2](7) NULL, [ModifyUser] [nvarchar](200) NOT NULL, [ModifyDate] [datetime2](7) NULL, CONSTRAINT [PK_CustomerLinks] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF ) ON [PRIMARY] ) ON [PRIMARY] GO ALTER TABLE [QMR].[CustomerLinks] WITH CHECK ADD CONSTRAINT [FK_CustomerLinks_Customers_ChildId] FOREIGN KEY([ChildId]) REFERENCES [QMR].[Customers] ([Id]) GO ALTER TABLE [QMR].[CustomerLinks] CHECK CONSTRAINT [FK_CustomerLinks_Customers_ChildId] GO ALTER TABLE [QMR].[CustomerLinks] WITH CHECK ADD CONSTRAINT [FK_CustomerLinks_Customers_CustomerId] FOREIGN KEY([CustomerId]) REFERENCES [QMR].[Customers] ([Id]) GO ALTER TABLE [QMR].[CustomerLinks] CHECK CONSTRAINT [FK_CustomerLinks_Customers_CustomerId] GO
正确的实体与配置方案
问题根源在于:Customer和CustomerLink之间存在两个独立的关联关系(一个是Customer作为父节点,一个是Customer作为子节点),而每个导航属性只能属于一个关系。因此需要给Customer添加两个不同的导航属性,分别对应这两个关系:
修改后的Customer实体
public class Customer { [Key] public int Id {get; set;} [Required(ErrorMessage = "Name is required")] [MaxLength(100, ErrorMessage="Name cannot be longer than 100 characters")] public string Name {get; set;} // 作为父节点的关联集合(关联到CustomerLink.Customer) public List<CustomerLink> ParentLinks { get; set; } // 作为子节点的关联集合(关联到CustomerLink.Child) public List<CustomerLink> ChildLinks { get; set; } [MaxLength(200, ErrorMessage="Create User cannot be longer than 200 characters")] public string CreateUser {get; set;} public DateTime? CreateDate {get; set;} [Required(ErrorMessage = "Modify User is required")] [MaxLength(200, ErrorMessage="Modify User cannot be longer than 200 characters")] public string ModifyUser {get; set;} public DateTime? ModifyDate {get; set;} }
CustomerLink实体保持修改后的版本不变(已包含CustomerId、ChildId外键属性)。
最终的OnModelCreating配置
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 配置Customer作为父节点的关系 modelBuilder.Entity<CustomerLink>() .HasOne(l => l.Customer) .WithMany(c => c.ParentLinks) .HasForeignKey(l => l.CustomerId) .OnDelete(DeleteBehavior.Restrict); // 配置Customer作为子节点的关系 modelBuilder.Entity<CustomerLink>() .HasOne(l => l.Child) .WithMany(c => c.ChildLinks) .HasForeignKey(l => l.ChildId) .OnDelete(DeleteBehavior.Restrict); }
这样配置后,EF Core就能正确识别两个独立的关联关系,匹配现有的数据库结构,不会再出现导航属性冲突的错误。
内容的提问来源于stack exchange,提问作者pachyderm94

