只是好奇:Entity Framework 如何确定 m:n 关系中连接 table 的命名?
Just Curious: How does Entity Framework determines the naming of a join table in m:n relationships?
我很好奇 Entity Framework 如何确定 m:n 关系中连接 table 的命名。
上下文:
我有如下学生和课程 class。在 VS 解决方案中进行测试时,EF 生成连接 table 作为 StudentCourses。
我的问题:为什么 EF 生成联接 table 作为 StudentCourses 而不是 CourseStudents? join table 的命名是如何确定的?
示例代码:
public class Student
{
public Student()
{
this.Courses = new HashSet<Course>();
}
public int StudentId { get; set; }
[Required]
public string StudentName { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public Course()
{
this.Students = new HashSet<Student>();
}
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
我没能完全找到它,但这是开始。
当您的 DbContext 创建其模型时,OnModelCreating is called. This has a parameter which is a DbModelBuilder。
此 DbModelBuilder 有 Conventions of type ConventionsConfiguration。
你会认为我们快到了。唉,ConventionsConfiguration 没有 属性 来访问约定。
如果你看source code of DbModelBuilder, you can see that the conventions are initialize with V2ConventionSet.Conventions
. You have to do some deep digging, but then you can find that the V2ConventionSet is derived from V1ConventionsSet。绝大多数约定都在 V1ConventionSet 中。
哎呀,虽然 ConventionSet 中的 Conventions 的名称描述性很强,但我找不到定义联结 table 名称定义方式的 Convention。
使用Reflection,可以看到ConventionConfiguration有一些非public字段
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var conventions = modelBuilder.Conventions;
var conventionsType = conventions.GetType();
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
var fields = conventionsType.GetFields(flags);
foreach (var field in fields)
{
var conventionValue = field.GetValue(conventions);
if (conventionValue is IEnumerable sequence)
{
foreach (var item in sequence)
{
Console.WriteLine(item);
}
}
}
}
这也没什么用,你只能看到Convention是我们在V2ConventionSet中找到的。也许如果您检查这些约定,您可能会发现结合这些名称的约定。也许是定义复数名称的那个?
不管怎样,你可以添加自己的约定,看看会发生什么:
public class JunctionTableConvention : IStoreModelConvention<AssociationType>
{
public void Apply(AssociationType item, DbModel model)
{
var associations = model.ConceptualToStoreMapping.AssociationSetMappings;
foreach (var association in associations)
{
var associationSetEnds = association.AssociationSet.AssociationSetEnds;
association.StoreEntitySet.Table = String.Format("{0}_{1}",
GetTableName(associationSetEnds[0].EntitySet.ElementType),
GetTableName(associationSetEnds[1].EntitySet.ElementType));
}
}
private string GetTableName(EntityType type)
{
var result = System.Text.RegularExpressions.Regex
.Replace(type.Name, ".[A-Z]", m => m.Value[0] + "_" + m.Value[1]);
return result.ToLower();
}
}
在 OnModelCreating 中添加此约定:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Add(new JunctionTableConvention());
...
}
在你的程序中:
static void Main(string[] args)
{
using (var dbContext = new SchoolDbContext())
{
dbContext.Database.Initialize(true);
}
}
您的调试器将显示调用了 OnModelCreating,其中添加了联结 table 约定。在 OnModelCreating returns 之后,调用 JunctionModel.Apply。
我有一个学生和老师之间的多对多。 AssociationType 是 {CodeFirstDatabaseSchema.Teacher_Students_Source}
.
我的代码获取 AssociationSetMappings。只有一个这样的映射。
此映射有一个 StoreEntitySet,它有一个字符串 属性 Table,其值为“TeacherStudents”
我的代码获取 AssociationSetEnds 以查找 DbSet 的类型,并为此构造一个漂亮的名称 table。此名称放在 association.StoreEntitySet.Table.
中
所以其他约定之一已经用“TeacherStudents”填充了这个值。
要找出是哪个约定这样做的,我可以一个一个地删除约定,然后查看名称何时更改。
但我把那个留给你了。
另一种方法当然是查看所有这些约定的参考来源,看看哪个定义了 table 的名称。
我很好奇 Entity Framework 如何确定 m:n 关系中连接 table 的命名。
上下文:
我有如下学生和课程 class。在 VS 解决方案中进行测试时,EF 生成连接 table 作为 StudentCourses。
我的问题:为什么 EF 生成联接 table 作为 StudentCourses 而不是 CourseStudents? join table 的命名是如何确定的?
示例代码:
public class Student
{
public Student()
{
this.Courses = new HashSet<Course>();
}
public int StudentId { get; set; }
[Required]
public string StudentName { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public Course()
{
this.Students = new HashSet<Student>();
}
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
我没能完全找到它,但这是开始。
当您的 DbContext 创建其模型时,OnModelCreating is called. This has a parameter which is a DbModelBuilder。
此 DbModelBuilder 有 Conventions of type ConventionsConfiguration。 你会认为我们快到了。唉,ConventionsConfiguration 没有 属性 来访问约定。
如果你看source code of DbModelBuilder, you can see that the conventions are initialize with V2ConventionSet.Conventions
. You have to do some deep digging, but then you can find that the V2ConventionSet is derived from V1ConventionsSet。绝大多数约定都在 V1ConventionSet 中。
哎呀,虽然 ConventionSet 中的 Conventions 的名称描述性很强,但我找不到定义联结 table 名称定义方式的 Convention。
使用Reflection,可以看到ConventionConfiguration有一些非public字段
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var conventions = modelBuilder.Conventions;
var conventionsType = conventions.GetType();
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
var fields = conventionsType.GetFields(flags);
foreach (var field in fields)
{
var conventionValue = field.GetValue(conventions);
if (conventionValue is IEnumerable sequence)
{
foreach (var item in sequence)
{
Console.WriteLine(item);
}
}
}
}
这也没什么用,你只能看到Convention是我们在V2ConventionSet中找到的。也许如果您检查这些约定,您可能会发现结合这些名称的约定。也许是定义复数名称的那个?
不管怎样,你可以添加自己的约定,看看会发生什么:
public class JunctionTableConvention : IStoreModelConvention<AssociationType>
{
public void Apply(AssociationType item, DbModel model)
{
var associations = model.ConceptualToStoreMapping.AssociationSetMappings;
foreach (var association in associations)
{
var associationSetEnds = association.AssociationSet.AssociationSetEnds;
association.StoreEntitySet.Table = String.Format("{0}_{1}",
GetTableName(associationSetEnds[0].EntitySet.ElementType),
GetTableName(associationSetEnds[1].EntitySet.ElementType));
}
}
private string GetTableName(EntityType type)
{
var result = System.Text.RegularExpressions.Regex
.Replace(type.Name, ".[A-Z]", m => m.Value[0] + "_" + m.Value[1]);
return result.ToLower();
}
}
在 OnModelCreating 中添加此约定:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Add(new JunctionTableConvention());
...
}
在你的程序中:
static void Main(string[] args)
{
using (var dbContext = new SchoolDbContext())
{
dbContext.Database.Initialize(true);
}
}
您的调试器将显示调用了 OnModelCreating,其中添加了联结 table 约定。在 OnModelCreating returns 之后,调用 JunctionModel.Apply。
我有一个学生和老师之间的多对多。 AssociationType 是 {CodeFirstDatabaseSchema.Teacher_Students_Source}
.
我的代码获取 AssociationSetMappings。只有一个这样的映射。 此映射有一个 StoreEntitySet,它有一个字符串 属性 Table,其值为“TeacherStudents”
我的代码获取 AssociationSetEnds 以查找 DbSet 的类型,并为此构造一个漂亮的名称 table。此名称放在 association.StoreEntitySet.Table.
中所以其他约定之一已经用“TeacherStudents”填充了这个值。 要找出是哪个约定这样做的,我可以一个一个地删除约定,然后查看名称何时更改。
但我把那个留给你了。
另一种方法当然是查看所有这些约定的参考来源,看看哪个定义了 table 的名称。