Entity framework 只是好奇:实体框架如何确定m:n关系中联接表的命名?

Entity framework 只是好奇:实体框架如何确定m:n关系中联接表的命名?,entity-framework,entity-framework-6,Entity Framework,Entity Framework 6,我很好奇实体框架如何确定m:n关系中联接表的命名 背景: 我的学生和课程如下。在VS解决方案中进行测试时,EF会将联接表生成为StudentCourses 我的问题:为什么EF将联接表生成为StudentCourses而不是CourseStudents?如何确定联接表的命名? 示例代码: public class Student { public Student() { this.Courses = new HashSet<Course>();

我很好奇实体框架如何确定m:n关系中联接表的命名

背景:

我的学生和课程如下。在VS解决方案中进行测试时,EF会将联接表生成为StudentCourses

我的问题:为什么EF将联接表生成为StudentCourses而不是CourseStudents?如何确定联接表的命名?

示例代码:

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; }
}
公共班级学生
{
公立学生()
{
this.Courses=newhashset();
}
公共int StudentId{get;set;}
[必需]
公共字符串StudentName{get;set;}
公共虚拟ICollection课程{get;set;}
}
公共课
{
公共课程()
{
this.Students=newhashset();
}
public int CourseId{get;set;}
公共字符串CourseName{get;set;}
公共虚拟ICollection学生{get;set;}
}

我无法完全找到它,但这是开始

当DbContext创建其模型时,调用。这有一个参数,它是一个

此DbModelBuilder的类型为。 你会以为我们就快到了。遗憾的是,ConventionsConfiguration没有访问约定的属性

如果查看,您可以看到使用
v2conventionstart.conventions
初始化约定。你必须做一些深入的挖掘,但你会发现,这是源于。绝大多数约定都在v1约定集中

唉,尽管ConventionSet中约定的名称是相当描述性的,但我找不到定义如何定义连接表名称的约定

使用反射,您可以看到ConventionConfiguration有一些非公共字段

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);
            }
        }
    }
}
这也没有多大帮助,您只能看到这些约定是我们在v2conventioninite中找到的约定。如果你检查一下这些约定,你可能会发现一个组合了这些名称的约定。也许是定义复数名称的那个

无论如何,您可以添加自己的约定,然后可以看到发生了什么:

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();
    }
}
在您的程序中:

static void Main(string[] args)
{
    using (var dbContext = new SchoolDbContext())
    {
        dbContext.Database.Initialize(true);
    }
}
调试器将显示调用OnModelCreating,其中添加了连接表约定。OnModelCreating返回后,将调用JunctionModel.Apply

我在学生和老师之间有一种多对多的关系。AssociationType是一个
{CodeFirstDatabaseSchema.Teacher\u Students\u Source}

我的代码获取AssociationSetMappings。只有一个这样的映射。 此映射有一个StoreEntitySet,它有一个字符串属性表,该表的值为“TeacherStudents”

我的代码获取AssociationSetEnds以查找数据库集的类型,并为此表构造了一个很好的名称。此名称放在association.StoreEntitySet.Table中

因此,另一个惯例已经用“教师学生”来填充这个值。 为了找出是哪个约定做的,我可以一个接一个地删除约定,并查看名称何时更改

但我把它留给你了


当然,另一种方法是查看所有这些约定的参考源,看看哪一个定义了表的名称。

感谢您的指导!我也在考虑约定,但没有像你那样深入地追踪它!
static void Main(string[] args)
{
    using (var dbContext = new SchoolDbContext())
    {
        dbContext.Database.Initialize(true);
    }
}