EF 生成的来自 SQL 的无效列名错误
Invalid column name error from SQL generated by EF
您可以在下面看到 SQL 应该使用 [ClassId1] 而不是 [Class1_ClassId] 加入,因为后者不存在。
我很确定我可以使用 Fluent API 来纠正这个问题,但不确定使用什么方法。
生成SQL
SELECT ...
FROM [dbo].[School] AS [Extent1]
LEFT OUTER JOIN [dbo].[Student] AS [Extent2] ON [Extent1].[SchoolId] = [Extent2].[SchoolId]
LEFT OUTER JOIN [dbo].[Class] AS [Extent3] ON [Extent2].[Class1_ClassId] = [Extent3].[ClassId]
LEFT OUTER JOIN [dbo].[Class] AS [Extent4] ON [Extent2].[Class2_ClassId] = [Extent4].[ClassId]
WHERE ...
表格
School
- SchoolId
- Name
- StudentId
Student
- StudentId
- Name
- Class1Id
- Class2Id
Class
- ClassId
- Name
型号
public class School
{
[Required]
public long SchoolId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public long StudentId { get; set; }
public virtual Student Student { get; set; }
}
public class Student
{
[Required]
public long StudentId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public long ClassId1 { get; set; }
public long? ClassId2 { get; set; }
public virtual Class Class1 { get; set; }
public virtual Class Class2 { get; set; }
}
public class Class
{
[Required]
public long ClassId { get; set; }
[Required]
public string Name { get; set; }
}
[Required]
public long ClassId1 { get; set; }
public long? ClassId2 { get; set; }
public virtual Class Class1 { get; set; }
public virtual Class Class2 { get; set; }
您还没有设置这些属性之间的任何关系。由于您没有为 Class1
或 Class2
定义外键列,它将为您创建它们:Class1_ClassId
和 Class2_ClassId
。创建迁移 应该 为您创建这些列;但你最终会得到重复项(例如 Class1Id
和 Class1_ClassId
)。
我相信 EntityFramework 如果名称以Id
结尾,将解析属性之间的关系。这意味着您的设置应该是:
[Required]
public long Class1Id { get; set; }
public long? Class2Id { get; set; }
public virtual Class Class1 { get; set; }
public virtual Class Class2 { get; set; }
但是,我发现最好是明确的 - 纯粹为了可读性和确保 EF 不会试图变得太聪明。我会这样写:
[Required]
public long ClassId1 { get; set; }
public long? ClassId2 { get; set; }
[ForeignKey("ClassId1")]
public virtual Class Class1 { get; set; }
[ForeignKey("ClassId2")]
public virtual Class Class2 { get; set; }
这应该正确设置数据库中的外键关系。
我认为 Entity Framework 从你的 linq 构造了这个 SQL,因为你的模型中 classes 之间的关系不清楚。
根据你的模型,aSchool
只有一个Student
,aStudent
不知道他参加了哪个School
,并且必须有一个Class
,可能还有第二个。 class
不知道它在哪个 School
中,也不知道 Class
.
中的哪个 Students
你确定你的型号吗?
我认为 School
会有零个或多个 Students
。一个 School
也有零个或多个 Classes
。每个 class
都是 School
中的 class。
在数据库术语中,这是典型的一对多关系。参见 Entity Framework Configure One-to-Many Relationship
此外,Student
参加了零个或多个 Classes
,Class
参加了一个或多个 Students
。
在数据库术语中,这是典型的多对多关系。参见:Entity Framework configure many-to-many relationship
这些文章还介绍了学校、学生和学校。总结了class定义应该是:
class School
{
public int Id {get; set;}
// a School has many Students:
public virtual ICollection<Student> Students {get; set;}
// a School has many Classes:
public virtual ICollection<Class> Classes {get; set;}
...
}
public class Student
{
public int Id {get; set;}
// A student belongs to one School via Foreign Key
public int SchoolId {get; set;}
public virtual School School {get; set;}
// A student attends many classes
public virtual ICollection<Class> Classes {get; set;}
...
}
class Class
{
public int Id {get; set;}
// a class belongs to one School via foreign key:
public int SchoolId {get; set;}
public virtual School School {get; set;}
// a class has many Students
public virtual ICollection<Student> Students {get; set;}
...
}
此后 DbContext 将如下所示:
class MyDbContext : DbContext
{
public DbSet<School> Schools {get; set;}
public DbSet<Student> Students {get; set;}
public DbSet<Class> Classes {get; set;}
}
如果您这样建模,entity framework 将自动识别 School 和 Students 之间的一对多关系,并为其创建适当的外键。它还会识别 Students 和 类 之间的多对多关系。它甚至会为多对多创建一个 table,您在 LINQ 查询中不需要它。
Entity Framework uses default conventions 如果你遵循它们,你就不需要告诉模型关于 Table 名称和列名,关于主键和外键等。
回到你的问题
你想告诉你的模型它应该使用特定的列名作为 属性 而不是它根据你的 class 关系构造的列名。
这可以在 class 中使用数据注释或在 DbContext 中使用 Fluent API 来完成。我更喜欢使用 Fluent Api,因为它允许您在不同的数据库结构中使用相同的 classes 而无需更改 classes。如果你想要不同的 table 名称,或者主键的不同名称,小数的不同精度等,你所要做的就是创建一个新的 DbContext。您不必更改 classes,classes 的用户不会注意到这些更改。
在你的情况下:指定一个 table 名称而不是默认的 table 名称。
在我的示例中,A Class
将放在 table Classs
中,而您当然希望它放在 table Classes
中:
class MyDbContext : DbContext
{
public DbSet<School> Schools {get; set;}
public DbSet<Student> Students {get; set;}
public DbSet<Class> Classes {get; set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Entity Class should be put in table Classes
modelBuilder.Entity<Class>().ToTable("Classes");
// property Student.ClassId in column "ClassId1"
modelBuilder.Entity<Student>() // from class Student
.Property(student => student.ClassId) // take property ClassId
.HasColumnName("ClassId1"); // give it the column name "ClassId1"
}
}
您可以在下面看到 SQL 应该使用 [ClassId1] 而不是 [Class1_ClassId] 加入,因为后者不存在。
我很确定我可以使用 Fluent API 来纠正这个问题,但不确定使用什么方法。
生成SQL
SELECT ...
FROM [dbo].[School] AS [Extent1]
LEFT OUTER JOIN [dbo].[Student] AS [Extent2] ON [Extent1].[SchoolId] = [Extent2].[SchoolId]
LEFT OUTER JOIN [dbo].[Class] AS [Extent3] ON [Extent2].[Class1_ClassId] = [Extent3].[ClassId]
LEFT OUTER JOIN [dbo].[Class] AS [Extent4] ON [Extent2].[Class2_ClassId] = [Extent4].[ClassId]
WHERE ...
表格
School
- SchoolId
- Name
- StudentId
Student
- StudentId
- Name
- Class1Id
- Class2Id
Class
- ClassId
- Name
型号
public class School
{
[Required]
public long SchoolId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public long StudentId { get; set; }
public virtual Student Student { get; set; }
}
public class Student
{
[Required]
public long StudentId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public long ClassId1 { get; set; }
public long? ClassId2 { get; set; }
public virtual Class Class1 { get; set; }
public virtual Class Class2 { get; set; }
}
public class Class
{
[Required]
public long ClassId { get; set; }
[Required]
public string Name { get; set; }
}
[Required]
public long ClassId1 { get; set; }
public long? ClassId2 { get; set; }
public virtual Class Class1 { get; set; }
public virtual Class Class2 { get; set; }
您还没有设置这些属性之间的任何关系。由于您没有为 Class1
或 Class2
定义外键列,它将为您创建它们:Class1_ClassId
和 Class2_ClassId
。创建迁移 应该 为您创建这些列;但你最终会得到重复项(例如 Class1Id
和 Class1_ClassId
)。
我相信 EntityFramework 如果名称以Id
结尾,将解析属性之间的关系。这意味着您的设置应该是:
[Required]
public long Class1Id { get; set; }
public long? Class2Id { get; set; }
public virtual Class Class1 { get; set; }
public virtual Class Class2 { get; set; }
但是,我发现最好是明确的 - 纯粹为了可读性和确保 EF 不会试图变得太聪明。我会这样写:
[Required]
public long ClassId1 { get; set; }
public long? ClassId2 { get; set; }
[ForeignKey("ClassId1")]
public virtual Class Class1 { get; set; }
[ForeignKey("ClassId2")]
public virtual Class Class2 { get; set; }
这应该正确设置数据库中的外键关系。
我认为 Entity Framework 从你的 linq 构造了这个 SQL,因为你的模型中 classes 之间的关系不清楚。
根据你的模型,aSchool
只有一个Student
,aStudent
不知道他参加了哪个School
,并且必须有一个Class
,可能还有第二个。 class
不知道它在哪个 School
中,也不知道 Class
.
Students
你确定你的型号吗?
我认为 School
会有零个或多个 Students
。一个 School
也有零个或多个 Classes
。每个 class
都是 School
中的 class。
在数据库术语中,这是典型的一对多关系。参见 Entity Framework Configure One-to-Many Relationship
此外,Student
参加了零个或多个 Classes
,Class
参加了一个或多个 Students
。
在数据库术语中,这是典型的多对多关系。参见:Entity Framework configure many-to-many relationship
这些文章还介绍了学校、学生和学校。总结了class定义应该是:
class School
{
public int Id {get; set;}
// a School has many Students:
public virtual ICollection<Student> Students {get; set;}
// a School has many Classes:
public virtual ICollection<Class> Classes {get; set;}
...
}
public class Student
{
public int Id {get; set;}
// A student belongs to one School via Foreign Key
public int SchoolId {get; set;}
public virtual School School {get; set;}
// A student attends many classes
public virtual ICollection<Class> Classes {get; set;}
...
}
class Class
{
public int Id {get; set;}
// a class belongs to one School via foreign key:
public int SchoolId {get; set;}
public virtual School School {get; set;}
// a class has many Students
public virtual ICollection<Student> Students {get; set;}
...
}
此后 DbContext 将如下所示:
class MyDbContext : DbContext
{
public DbSet<School> Schools {get; set;}
public DbSet<Student> Students {get; set;}
public DbSet<Class> Classes {get; set;}
}
如果您这样建模,entity framework 将自动识别 School 和 Students 之间的一对多关系,并为其创建适当的外键。它还会识别 Students 和 类 之间的多对多关系。它甚至会为多对多创建一个 table,您在 LINQ 查询中不需要它。
Entity Framework uses default conventions 如果你遵循它们,你就不需要告诉模型关于 Table 名称和列名,关于主键和外键等。
回到你的问题
你想告诉你的模型它应该使用特定的列名作为 属性 而不是它根据你的 class 关系构造的列名。
这可以在 class 中使用数据注释或在 DbContext 中使用 Fluent API 来完成。我更喜欢使用 Fluent Api,因为它允许您在不同的数据库结构中使用相同的 classes 而无需更改 classes。如果你想要不同的 table 名称,或者主键的不同名称,小数的不同精度等,你所要做的就是创建一个新的 DbContext。您不必更改 classes,classes 的用户不会注意到这些更改。
在你的情况下:指定一个 table 名称而不是默认的 table 名称。
在我的示例中,A Class
将放在 table Classs
中,而您当然希望它放在 table Classes
中:
class MyDbContext : DbContext
{
public DbSet<School> Schools {get; set;}
public DbSet<Student> Students {get; set;}
public DbSet<Class> Classes {get; set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Entity Class should be put in table Classes
modelBuilder.Entity<Class>().ToTable("Classes");
// property Student.ClassId in column "ClassId1"
modelBuilder.Entity<Student>() // from class Student
.Property(student => student.ClassId) // take property ClassId
.HasColumnName("ClassId1"); // give it the column name "ClassId1"
}
}