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();
        }
    }
    

    执行结果:

  • 相关阅读:
    OpenStack Cinder组件支持的块存储设备表
    恒天云 3.0:打造基于OpenStack的私有云新模式
    看到一位园友博客有感
    android圆角功能,非常好用,可以用在图片,视频,gif等上面
    Android studio 3.0以上版本无法引入,找不到v4,v7包方案解决
    Android工程运用阿里freeline10秒快速编译分享
    Android监听view的attached或detached状态
    Android弹出Toast工具类总结
    为何IT开发人员如此辛苦?
    android TextView如何换行?
  • 原文地址:https://www.cnblogs.com/xishuai/p/ef-dbcontext-savechanges-foreignkey-value.html
Copyright © 2011-2022 走看看