Entity Framework之 Code First

本文详细介绍了如何使用Entity Framework的CodeFirst方法创建实体模型,配置数据库连接,设置导航属性,以及如何通过数据注解和Fluent API定制映射规则。涵盖了主键、外键、数据迁移等内容,帮助读者快速上手CodeFirst模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Entity Framework之Code First

Code First使用步骤

  1. 添加类创建实体模型,并创建导航属性,设置实体模型之间关系。

    public class Grade{
        public int GradeId{get;set;}
        public string GradeName{get;set;}
        //导航属性
        public virtual List<Studnet> Student {get;set;}
    }
    public class Student{
        public int StudentId{get;set;}
        public string StudentName{get;set;}
        public int Age{get;set;} 
        //导航属性
        public virtual Grade Grede {get;set;}
    }
    
  2. 安装Entity Framework包

    1. 通过NuGet在线安装Entity Framework包
    2. 使用“程序包管理器控制台”通过“install-package EntityFramework”命令安装
  3. 创建dbContext上下文

    1. 首先在配置文件中(app.config)中配置数据库链接字符串,代码如下:

      <configuration>
          <connectionStrings>
              <add name="name" connectionString="Data Source=.;Initial Catalog=Test;Persist Security Info=true;User ID=sa;Password=sa";
                   providerName="System.Data.SqlClient"/>
          </connectionStrings>
      </configuration>
      <!--
      必须指定providerName属性
      Initial Catalog:为数据库赋值名称
      Persist Security Info=true:属性的意思是表示是否保存安全信息,其实可以简单的理解为"ADO在数据库连接成功后是否保存密码信息"
      -->
      
    2. 接下来创建dbContext类,此类继承System.Data.Entity.DbContext类,其构造函数调用了积累带参数的构造函数,用来读取配置文件中的数据库链接字符串。

      public class DemoContext:DbContext
          {
              public DemoContext():base("Demo1")
              {
                  //数据库不存在时创建数据库
                  Database.SetInitializer<DemoContext>(new CreateDatabaseIfNotExists<DemoContext>());
                  //模型改变时重新创建数据库
                  Database.SetInitializer<DemoContext>(new DropCreateDatabaseIfModelChanges<DemoContext>());
                  //每次启动应用程序时创建数据库
                  Database.SetInitializer<DemoContext>(new DropCreateDatabaseAlways<DemoContext>());
                  //从不创建数据库
                  Database.SetInitializer<DemoContext>(null);
              }
              public DbSet<Grade> Grade { get; set; }
              public DbSet<Student> Studnent { get; set; }
      
              protected override void OnModelCreating(DbModelBuilder modelBuilder)
              {
                  modelBuilder.Configurations.Add(new StudentConfig());
                  modelBuilder.Configurations.Add(new GradeConfig());
              }
          }
      

  4. 对数据进行操作

    接下来就可以像Database first方式一样,使用dbContext对象对数据库进行操作了。

Code First的映射规则

默认约定
  1. 关于表和字段名的约定

    Code Fisrt约定表名使用实体类名的复数形式来命名表明,属性映射的列使用与类中属性一致的名字命名

  2. 关于主键的约定

    Code First默认规定将命名为Id或[类名]Id的属性是为类的主键,如果是int类型或GUID类型同时会设为标识列。

  3. 关于字符串属性的约定

    Code First将String 类型属性映射为不限长度的非空列中,默认数据类型为nvarchar(max),且允许为空。

  4. 关于布尔属性的约定

    Code First将bool类型属性映射为bit类型,且不允许为空。

  5. 关于外键的约定

    对于外键,如果在类中添加了引用类型,而这个引用类型也在Entity Framework的上下文中,EF会把这种属性成为导航属性。

    一旦EF在类中检索到导航属性,就会寻找对应的外键。EF会认为属性名+Id或者类名+Id可能就是外键属性,如果找到名称一致且类型与导航属性目标类的主键类型一致,则认为是外键。如果类型不一致,EF则会认为该类设置有误。

    如果没有找到符合要求的属性,EF会自己添加一个外键属性。另外,对于不同的关系也有一些不同的约定。

    • 一对一关系:要求导航属性双方都应该具有外键配置。
    • 一对多关系:要求多的一方设置外键。同时如果在一方这边设置了集合类型的导航属性,那么EF会自动到目标类中寻找外键属性。
    • 多对多关系:会生成一个中间关系表。
修改映射规则
  1. 数据注解

    数据注解方式是通过给类或者属性增加特性来解决的,需要引入两个命名空间:

    • System.ComponentModel.DataAnnotations命名空间下的特性是控制表中列的属性的。

      包括:Key、Required、MinLength、MaxLength、StringLength、Timestamp、ConcurrencyCheck等

    • System.ComponentModel.DataAnnotations.Schema命名空间下的特性是控制数据库结构的。

      包括:Table、Column、ForeignKey、NotMapped等

      特性说明
      Key声明主键
      Required非空声明
      MinLength/MaxLength设置string类型的最大长度和最小长度,数据库对应Nvarchar
      StringLenth设置string类型的长度,数据库对应Nvarchar
      TimeStamp将byte[]类型设置为timestamp类型
      ConcurrencyCheck并发检查,执行Update操作时,会检查并发性(乐观锁)
      Table给代码中类的属性换一个名来映射数据库表中的列名,(还可以设置表的架构名称[Table(“myAddress”,Schema=“admin”)])
      Column给代码种类的属性换一个名来映射数据库中表的列名(还可以设置列的类型、列在表中的显示顺序[Column(“myAddressName”,Order=1,TypeName=“varchar”)])
      ForeignKey设置外键
      NotMapped类中的列名不在数据库表中映射生成。(还可以只设置get属性或者只设置set属性,在数据库中也不映射)

      数据注解示例:

      [Table("Students")]//设置类映射的数据库表名
      public class Student{
          [Key]//主键声明
          public string StudentId{get;set;}
          
          [required]//非空声明
          [MaxLength(20),MinLength(10)]//设置最大长度和最小长度
          public string StudentName{get;set;}
          
          [Column("Address",Order=1,TypeName="varchar")]//设置映射数据库中表的列名、顺序、类型
          public string StudentAddress{get;set;}
          
          [NotMapped]//不映射列
          public int Age{get;set;}
          
          public int GradeId{get;set;}
          
          [ForeignKey("GradeId")]//设置外键
          public virtual Grade Grade{get;set;}
      }
      
  2. Fluent API

    使用Fluent ApI方式,需要重写DbContext类中的OnModelCreating()方法来实现。

    配置Fluent API说明
    实体相关配置HasIndex()实体的索引
    HasKey()实体的主键
    HasMany()1对多的或者多对多关系
    HasOptional()一个可选的关系,这样配置会在数据库中生成一个可控的外键
    HasRequired()一个必要的关系,这样配置会在数据库中生成一个不能为空的外键
    Ignore()实体或者尸体的属性不映射到数据库
    Map()设置一些优先的配置
    ToTable为实体设置表名
    配置Fluent API方法说明
    属性相关配置IsRequired()属性不能为空
    IsOptional()可选的,在数据库中生成可控的列
    HasDatabaseGeneratedOption()配置数据库中对应列的值怎样生成的,如计算、自增等
    HasColumnType()配置数据库中对应列的数据类型
    HasColumnName()配置数据库中对应列的列名
    IsConcurrencyToken()配置数据库中对应列用于乐观并发检测

    Fluent API示例:

    //数据上下文类
    public class MySchoolContext:DbContext
    {
    	public MySchoolContext():base("MySchool")
        {
        }
        
        //对象集合
        public Dbset<Grade> Grade{get;set;}
        public DbSet<Student> Student{get;set;}
        
        protect override coid OnModelCreating(DbModelBuilder modelBuilder)
        {
        	//获取Student类的配置
        	var stuConfig=modelbuilder.Entity<Student>();
        	stuConfig To Table("Student");
        	stuConfig.HasKey(s=>s.StudentNo);
        	stuConfig.Property(s=>s.Name).HasColumnName("StudentName")
        		.HasMaxLength(20).IdRequired();
        	stuConfig.Property(s=>s.Address).HasMaxLength(50);
        	stuConfig.Ignore(s=>s.Age);
        	base.OnModelCreating(modelbuilder);
        	
        }
    }
    

    但实际在开发中,项目中有许多实体类,这就不便于所有Fluent API配置写在OnModelCreating()方法中,我们可以为每个实体类创建一个单独的配置类。

    //Student的配置类
    public class StudentConfig:EntityTypeConfiguration<Studnet>
    {
    	public StudentConfig()
        {
        	this.ToTable("Student");
        	this.HasKeys(s=>s.StudentNo);
        	this.Property(s=>s.Name).HasColumnName("StudentName")
        	.HasMaxLength(20).IsRequired();
        	this.Property(s=>s.Address).HasMaxLenth(50);
        	this.Ignore(s=>s.Age);
        }
    }
    //DbContext类的OnModelCreateing()方法
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //加载配置文件
        modelBuilde.Configurations.Add(new StudentConfig());
    }
    
关系
  1. 一对一关系

    如果我们要将两个类的配置为一对一的关系,则两个类中都要配置相应的引用属性,使用数据注解确定主从表。

    使用Fluent API方式

    modelBuilder.Entity<Photo>().HasRequired(p=>p.StudentOf)..WithOptional(p=>p.Photo);
    

  2. 一对多关系

    Code First能通过一些引用属性、导航属性等检测到模型之间的关系,自动为我们生成外键。EF会将符合以下规则命名的导航属性识别为外键。

    • 目标类型的键名
    • 目标类型名称+目标类型键名称
    • 导航属性名称+目标类型名称

    使用数据注解方式在实体类中添加外键特。

    使用Fluent API方式

    modelBuilder.Entity<Student>().HasRequired(s=>s.StudentId).WithMany(g=>g.Students).HasForeignKey(s=>s.GradeId);
    
  3. 多对多关系

    如果两个类中,各自都是结合类型的导航属性指向另一个类,Code First会认为这两个类之间是多对多关系。

    使用Fluent API方式

    modelBuilder.Entity<Teacher>().HasMany(t=>t.Course).WithMany(t=>t.Teachers).Map(m=>{
    m.ToTable("TeacherCourse");
    m.MapLeftKey("TeacherId");
    m.MapRightKey("CourseId");
    });
    

    或者

    modelBuilder.Entity<Course>().HasMany(c=>c.Teachers).WithMany(t=>t.Course).Map(m=>{
       m.ToTalbe("TeacherCousers");
       m.MapLeftKey("CourseId");
       m.MapRightKey("TeacherId");
    });
    
数据迁移

​ 在使用Entity Framework的Code First是,如果模型发生了变化,就会删除数据库,根据模型重新创建数据库,这样,数据库中原先的数据会丢失。“数据迁移”就是解决这个问题。

​ 数据迁移是在“程序包管理器控制台”中通过数据迁移命令实现的。

自动数据迁移

​ 在Migrations文件夹中打开Configuration.cs文件,将AutomaticMigrationsEnabled = false;改为true为自动迁移,false为手动迁移。(手动数据迁移可以回滚,防止出现意外丢失数据)

  1. 启动自动数据迁移

    AutomaticMigrationsEnabled=true;

  2. 执行迁移

    Update-Database (自动迁移只执行这个)

手动数据迁移
  1. 启用数据迁移

    命令:Enable-Migration

    执行该命令,Entity Framework首先会检查数据库是否存在,然后会在项目中创建一个Migrations文件夹及继承一个自DbMigrationsCOnfiguration的Configuration类。

    Enable-Migrations只需执行一次,在以后的数据迁移中就不需要执行了.

  2. 创建迁移

    命令: Add-Migration 名称

    执行上面命令后,会在文件夹Migrations下自动生成一个以时间和名称组合作为文件名的类(类名为迁移的名称).

    生成的迁移类中有两个方法,分别为Up()和Down()。

    在Up()中,记录了需要升级的修改,在执行迁移时会调用这个方法完成和数据库的同步。

    在Down()中,是为了回滚修改而设计的,如果用户希望恢复到某一个迁移点,程序会自动根据已经执行的迁移,判断回滚哪些迁移,执行他们的Down()方法.

  3. 执行迁移

    命令:Update-Database [-参数]

    执行命令不带参数,会执行所有迁移,如果指定迁移直接加上名称,相当于版本回滚操作.

    如果参数为-Verbose可以查看升级的明细,即同步数据库需要执行的SQL语句.

    如果需要回滚操作,在操作完成后,需要将版本之后的迁移使用remove-migration命令删除.例如,有版本 1,2,3的三个数据迁移,此时想回到版本1,就直接Update-database 1,执行完成后,数据库中就已经更新到1版本了,然后在执行两次remove-migration把2和3的迁移文件删除

  4. 删除迁移

    命令:remove-migration

    用来移除最后一次迁移。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值