zoukankan      html  css  js  c++  java
  • EntityFramework 外键值映射

    如果在 EF OnModelCreating 中配置了实体外键映射,也就是 SQL Server 中的 ForeignKey,那么我们在添加实体的时候,主实体的主键值会自动映射到子实体的外键值,并且这个操作在一个 SaveChanges 中,但如果没有在 OnModelCreating 中进行外键映射配置,我们添加实体的时候,就不会自动映射外键值了,什么意思呢?我们先看一个示例代码:

    public class SchoolDbContext : DbContext
    {
        public SchoolDbContext()
            : base("db_school")
        { }
    
        public DbSet<Student> Students { get; set; }
        public DbSet<Class> Classs { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Student>()
                .HasKey(n => n.StudentId);
    
            modelBuilder.Entity<Class>()
                .HasKey(n => n.ClassId);
    
            base.OnModelCreating(modelBuilder);
        }
    }
    
    public class Student
    {
        public int StudentId { get; set; }
        public int ClassId { get; set; }
        public string Name { get; set; }
    }
    
    public class Class
    {
        public int ClassId { get; set; }
        public string Name { get; set; }
    }
    

    示例很简单,Class 和 Student 是一对多关系,但我们并没有在 OnModelCreating 中进行外键映射配置,所以生成到 SQL Server 的 db_school 数据库,会是这样:

    可以看到 Student 中的 ClassId 字段并不是外键,下面我们添加 Student 和 Class 实体:

    static void Main(string[] args)
    {
        using (var context = new SchoolDbContext())
        {
            var entityClass = new Class() { Name = "calss1" };
            var entityStudent = new Student() { ClassId = entityClass.ClassId, Name = "student1" };
            context.Classs.Add(entityClass);
            context.Students.Add(entityStudent);
            context.SaveChanges();
        }
    }
    

    执行结果:

    可以看到,Student 表中的 ClassId 值是 0,而并不是我们预想的 1,这是一个问题,在不增加外键的情况下,我们一般会这样解决:

    static void Main(string[] args)
    {
        using (var context = new SchoolDbContext())
        {
            var entityClass = new Class() { Name = "calss2" };
            context.Classs.Add(entityClass);
            context.SaveChanges();
    
            var entityStudent = new Student() { ClassId = entityClass.ClassId, Name = "student2" };
            context.Students.Add(entityStudent);
            context.SaveChanges();
        }
    }
    

    执行结果:

    这种处理方式,虽然“解决”上面的问题,但其实有很多的隐患,多执行一次 SaveChanges,EF 就会多发起一次请求,增加了性能开销,并且 SaveChanges 是事务性的,如果第一个执行成功了,第二个执行失败了,这时候第一个事务并不会回滚,因为它独立于第二个,所以,最后就会造成数据的不一致性,虽然几率非常点,但我们应该尽量避免。

    那有没有更好的解决方式呢?这个问题我之前有点想复杂了,其实解决非常简单,就是在 Student 实体中添加 virtual 修饰的 Class 属性,就可以了,如下:

    public class Student
    {
        public int StudentId { get; set; }
        public int ClassId { get; set; }
        public string Name { get; set; }
    
        public virtual Class Class { get; set; }//添加属性
    }
    

    需要注意的是,我们并不需要在 OnModelCreating 中进行 Class 和 ClassId 的映射配置,EF 会自动查找 ClassId(属性名 + Id),所以,“外键”命名要注意规范统一,如果命名为 Class_Id 就无效了。

    再次执行添加实体的代码,发现会报错:

    什么意思呢?就是实体更改了,需要进行 EF 迁移,如果你进行 EF 迁移的话,会发现,虽然我们没有在 OnModelCreating 中进行 ClassId 外键映射配置,但 EF 也会自动映射 ForeignKey 到数据库的,所以代码命名尽量规范些,EF 是比较“智能”的。

    我们解决这个问题的前提条件是“不增加外键配置”,所以我们要让 EF 忽略实体更改:

    public SchoolDbContext()
        : base("db_school")
    {
        Database.SetInitializer<SchoolDbContext>(null);//忽略映射
    }
    

    再次执行添加实体代码:

    static void Main(string[] args)
    {
        using (var context = new SchoolDbContext())
        {
            var entityClass = new Class() { Name = "calss3" };
            var entityStudent = new Student() { ClassId = entityClass.ClassId, Name = "student3" };
            context.Classs.Add(entityClass);
            context.Students.Add(entityStudent);
            context.SaveChanges();
        }
    }
    

    执行结果:

  • 相关阅读:
    Leetcode NO.110 Balanced Binary Tree 平衡二叉树
    Leetcode NO.226 Invert Binary Tree 翻转二叉树
    Leetcode NO.215 Kth Largest Element In An Array 数组中的第K个最大元素
    根据特征的浏览器判断
    Cygwin在打开在当前目录
    【转帖】科学对待 健康养猫 打造快乐孕妇
    解决chrome浏览器安装扩展、应用程序一直处在“检查中”的问题
    对【SQL SERVER 分布式事务解决方案】的心得补充
    关于“点击这里继续访问您选择的百度XXX”
    VBA一例:如何保持文本框焦点
  • 原文地址:https://www.cnblogs.com/xishuai/p/ef-dbcontext-savechanges-foreignkey-value.html
Copyright © 2011-2022 走看看