Entity Framework 实体数据模型——Code First

    之前大致总结了下EF 设计器的概念和实战操作,接下来来总结下 Code First 构建的实体数据模型。

  EF 设计器创建实体数据模型文件(.emdx)整合 Entity Framework,并通过 DBContext 和 DbSet 类来实现支持数据库功能的开发,

  然而实体数据模型的构建与维护并不容易,难以应付开发大型的商业应用所需。因此,Entity Framework 在后续版本推出了Code First

  开发模式,以相对简单明了的类设计取代了EF 设计器构建的实体数据模型文件(.emdx)。

  一、传统数据类对象

    Code First 通过典型的传统类对象(Plain Old ClassObject,POCO)映射到数据结构,简化了 EF 的开发过程,开发人员

    只需创建DbContext 和 DbSet 继承的对象,EF 会自动处理其中的转换细节。与 EF 设计器 相比,Code First 只保留了原来

    edmx实体数据模型文件中的 DBContext、DbSet派生文件。

    无论是从数据库生成模型,还是从模型创建数据库,EF 一旦确认DbSet 对象属性与数字表字段结构间的映射正确,便会自动

    维护数据查询变动等的相关操作。

  二、创建项目

    创建一个控制台应用程序的项目,项目名称为:CodeFirstProject 

    

    然后选择项目 ==> 单击鼠标右键 ==> 选择 管理NuGet程序包 ==> 选择 浏览 页签 ==> 在搜索框输入 回车搜索 ==> 点击 安装

    动态图在这儿:

    

    接着在项目中添加一个名为 Model 的文件夹,并在该文件夹中添加两个类文件:Student.cs 和 StudentInfoModel.cs 。

    

     Student 类中定义以下属性:

      public class Student
      {
          public int Id { get; set; }
          public string Name { get; set; }
          public int Age { get; set; }
          public bool Sex { get; set; }
          public string Hobby { get; set; }
          public DateTime Birthday { get; set; }
          public decimal Tuition { get; set; }
      }

     StudentInfoModel 类需要继承 DbContext,并定义 Students 属性:

      using System.Data.Entity;

      namespace CodeFirstProject.Model
      {
          public class StudentInfoModel : DbContext  //继承了 DbContext  
          {
              public DbSet<Student> Students { get; set; }
          }
      }

    接下来,我们需要在Main 方法中实例化 StudentInfoModel,并获取 Students 属性的总数:

      using CodeFirstProject.Model;
      using System;
      using System.Linq;

      namespace CodeFirstProject
      {
          class Program
          {
              static void Main(string[] args)
              {
                  StudentInfoModel stuModel = new StudentInfoModel();
                  int stuCount = stuModel.Students.Count();
                  Console.WriteLine(stuCount);
                  Console.Read();
              }
          }
      }

       第一次执行程序时,SQL Express 会自动创建名为 CodeFirstProject.Model.StudentInfoModel 的数据库,

    并创建了 Student 表,结构由 Student 类文件中配置的属性决定。我们可以通过 "SQL Server 对象资源管理器"(可在 视图 菜单下找到)

    来查看程序自动生成的数据库:

    

    我们可以通过查看数据库的属性,来找到程序创建的数据库所在的目录:

    选中数据库 ==> 单击鼠标右键 ==> 属性

    

       数据库连接:

    如果不在 StudentInfoModel 类中指定数据库的连接信息,SQL Express 将会创建所需的数据库。

    而在实际开发中,通常我们会通过连接字符串的设置来自行创建所需的数据库。

    首先,我们需要在 StudentInfoModel 类中添加一个构造函数,并配置数据库的连接名称:

      using System.Data.Entity;
      namespace CodeFirstProject.Model
      {
          public class StudentInfoModel : DbContext
          {
        
              public StudentInfoModel() : base("name=StuInfoModelConnection") //添加构造函数,并配置数据库的连接名称
              {

              }

              public DbSet<Student> Students { get; set; }
          }
      }

    其次打开 App.config 文件,在 configuration 节点中添加如下内容:

      <connectionStrings>
            <!--DataDirectory 表示数据库路径的替换字符串。-->
            <add name="StuInfoModelConnection" connectionString="Data Source=(LocalDB)MSSQLLocalDB;
                                        AttachDbFilename=|DataDirectory|CodeFirstProjectModel.mdf;                                         Integrated Security=True;Connect Timeout=30;
                                        MultipleActiveResultSets=True;App=EntityFramework"

                                        providerName
="System.Data.SqlClient"/> </connectionStrings>

    第三步,在项目的 inDebug 目录下创建数据库文件:

    

     特别注意:App.config 中配置的数据库名称,需要和创建时的数据库名称保持一致哦!!!

    

     最后,可以通过添加现有项的方式,把数据库拷贝到项目的根目录:

    

    App.config 文件的连接配置也要相应的调整下,才能使用根目录中的数据库文件哟!

      <connectionStrings>
            <!--将 AttachDbFilename=|DataDirectory|CodeFirstProjectModel.mdf; 这个配置替换成 
          Initial Catalog=CodeFirstProjectModel; 这个就好了!
--> <add name="StuInfoModelConnection" connectionString="Data Source=(LocalDB)MSSQLLocalDB;                                         Initial Catalog=CodeFirstProjectModel;                                         Integrated Security=True;Connect Timeout=30;
                                        MultipleActiveResultSets=True;App=EntityFramework"

                                        providerName
="System.Data.SqlClient"/> </connectionStrings>

     好了,在空白项目中使用 Code First 创建程序, EF 会自动完成其中与数据库的互动细节。

    与 EF 设计器相比,Code First 直接采用类取代 EMD 文件,内容更为简洁,而实体类与数据库

    底层数据结构的映射设置由惯例原则、属性标记以及 Fluent API 等程序设置的方式来取代 XML

    格式的 SSDL、CSDL与MSL设置。

  三、实体类映射

    1、映射惯例

    泛型 DBSet<TEntity>属性构成DBContext对象内容,并反映连接的数据表结构。

    此处的 TEntity  则是构成DbSet集合内容的实体类,在执行期间每个对象会映射到特定的数据表结构并封装特定的数据。

    比如上面的例子,实体类Student自动映射到Students数据表,Student的属性自动映射到数据表中相应的字段。

    实体类与数据表的映射有专用规则,Code First 采用惯例优于预先设置的设计,在没有任何设置的情况下,自动检测

    模型结构并推导出默认设置以简化类的设计,因此不需要特别设置类属性即可完成模型设计。

      using System.Data.Entity;
      namespace CodeFirstProject.Model
      {
          public class StudentInfoModel : DbContext
          {
              public StudentInfoModel() : base("name=StuInfoModelConnection") 
              {  }
              public DbSet<Student> Students { get; set; }   //按照惯例,会以复数类名称为映射的数据表名称。因此这里用的是Students
          }
      }

    Student 实体类的属性会映射到数据表中的同名字段:

    public class Student
    {
        public int Id { get; set; }      //Id 字段不区分大小写(ID 和 ID等同)自动设置为逐渐,类名 + Id 同样会被设置为主键(如: StudentId)
        public string Name { get; set; }
        public int Age { get; set; }
        public bool Sex { get; set; }
        public string Hobby { get; set; }
        public DateTime Birthday { get; set; }
        public decimal Tuition { get; set; }
    }

    EF会在映射的过程中自动推导出属性与字段的映射类型,下表是常用的映射惯例:

SQL Server数据库引擎类型 .NET Framework 类型
image, timestamp Byte[]
bigint Int64
int Int32
float   Double
bit Boolean
char, nchar, ntext, varchar, nvarchar, text String / Char[]
date, datetime, datetime2 DateTime
decimal, numeric, money Decimal
time TimeSpan
uniqueidentifier Guid

    2、数据注解

    当然,惯例规则是有局限性的;当惯例规则不能满足我们的要求时,我们可以使用数据注解来创建更合适的数据模型。

    接下来,来总结下数据注解的使用技巧。

    首先,需要新建个项目,项目命名为DataProject;

    接着添加 ADO.NET 实体数据模型,选择 “空 Code First 模型”:

    

     其次,在项目中创建新的类文件 Student.cs:

    public class Student
    {
        public int Id { get; set; }  
        public string Name { get; set; }
        public int ClassNo { get; set; }public decimal Tuition { get; set; }
    }

    最后,打开 StudentDataModel.cs 进行调整:

    using DataProject.Model;
    using System.Data.Entity;

    namespace DataProject
    {
        public class StudentDataModel : DbContext
        {
            public StudentDataModel()
                : base("name=StudentDataModel")
            {
            }

            public virtual DbSet<Student> Students { get; set; }   //新增Students 属性
        }
    }

    完成以上步骤之后,我们就可以使用数据注解来取代惯例原则了。

    使用数据注解时需要在实体类(Student.cs)中引入一下两个命名空间:

    //[Table("MStudent")]可以理解为将 Student 类映射到名为 MStudent 的数据表。
    //在惯例规则中,类名默认会映射到数据表名,通过该方法可以调整映射的默认行为,自由指定要映射的数据表名。
    [Table("MStudent")]
    public class Student
    {
        // [Key] 将 SID 属性 强制设置为主键
        // 注意这里的SID 不符合惯例规则,通过数据注解可将其设置为主键
        [Key]
        // DatabaseGeneratedOption 是个枚举对象,它有三个枚举值:Identity、Computed、None
        // Identity 在插入行时,数据库将生成值。
        // Computed 在插入或更新行时,数据库将生成值。
        // None 数据库不生成值
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int SID { get; set; }


        // [Column("StudentName")] 将 Name 属性强制映射到 StudentName 字段
        [Column("StudentName")]
        // 指定字段长度
        [StringLength(50)]
        // [Required] 将 StudentName 字段强制设置成不为Null,一旦为Null报错
        [Required]
        // [Index] 指定该属性所映射的字段为索引字段
        // [Index("StudentNameIndex")] 将索引名设置为: StudentNameIndex, 如果不设置,其默认索引名为:IX_StudentNameIndex
        // 2 设置多重键时,需指定索引顺序  这里同时设值了 Name 属性和下面的 ClassNo 属性以建立多重索引,StudentNameIndex 将被指定为第一个键
        [Index("StudentNameIndex", 2)]
        public string Name { get; set; }


        // [Index("ClassNoIndex")]指定该属性所映射的字段为索引字段, 将索引名设置为: ClassNoIndex
        // 1 设置多重键是,需指定索引顺序  这里同时设值了 Name 和 ClassNo 以建立多重索引,ClassNoIndex 将被指定为第二个键
        // IsUnique = true 指定一个具有唯一值特性的索引
        [Index("ClassNoIndex", 1, IsUnique = true)]
        public int ClassNo { get; set; }

        // TypeName= "decimal" 强制将 decimal 数据类型映射成 Money 类型
        [Column("StuTuition", TypeName = "Money")]
        public decimal Tuition { get; set; }

        public int Age { get; set; }
        public bool Sex { get; set; }

        [Column("HobbyDesc")]
        // [MaxLength(50, ErrorMessage = "HobbyDesc 字段的最大限制长度为:50"), MinLength(2)]
        // 将 HobbyDesc 字段的最大长度限制设置为 50,并配置了错误消息 ErrorMessage,
        // 当最大限制长度被超出并尝试保存到数据库时,将返回 ErrorMessage 指定的错误消息。
        // 将 StudentName 字段的最小长度限制设置为 2
        [MaxLength(50, ErrorMessage = "HobbyDesc字段的最大限制长度为:50"), MinLength(2)]
        public string Hobby { get; set; }

        [Required]
        [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
        public DateTime CreateDate { get; set; }

        // [NotMapped] 意思是:STuition 属性不存在映射字段
        // 该属性是基于程序运算需求建立的,所以不需要存在映射字段
        [NotMapped]
        public decimal STuition { get; set; }
    }

    配置完后,在Main 方法中添加如下代码:

    static void Main(string[] args)
    {
        StudentDataModel stus = new StudentDataModel();
        int stuCount = stus.Students.Count();
        Console.WriteLine(stuCount);
        Console.Read();
    }

    执行项目,打开 SQL Server 资源管理器 可以看到,数据表中新增了相应的表:

    

     

    好了,MStudent 表的表结构已经按照 Student 类配置的信息生成了!

  3、Fluent API

    Fluent API 是另一种支持实体配置设置的方式,与数据注解相比,它提供了更广泛的功能与设置弹性。

    要注意的是,实体类若同时设置了数据注解,则三者的优先级为:Fluent API > 数据注解 > 惯例规则。

    因此一旦设置了 Fluent API,无论数据注解还是惯例规则均会被覆写。

    DbContext 类定义的 OnModelCreating 方法是最常调用 Fluent API 的地方。

    using System.Data.Entity;
    using System.ComponentModel.DataAnnotations.Schema;

    using DataFAPIProject.Model;

    namespace DataFAPIProject
    {
        public class StudentFAPIModel : DbContext
        {
            public StudentFAPIModel()
                : base("name=StudentFAPIModel")
            {
            }

            public virtual DbSet<Student> Students { get; set; }

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {

                // 由于 DbSet<Student> 会映射到 Students 表,如果想要覆写默认规则,则需要进行如下设置:
                // 避免将 Student 类映射到任何数据表结构
                // modelBuilder.Ignore<Student>();
                // 指定将 Student 类映射到名称为 FStudent 的数据表
                // modelBuilder.Entity<Student>().ToTable("FStudent");
                // ToTable() 的第二个参数是指定 Schema 名称,默认情况下 Schema 的名称为 dbo
                modelBuilder.Entity<Student>().ToTable("FStudent", "stu");


                // 指定 Sid 属性为主键字段
                modelBuilder.Entity<Student>().HasKey(s => s.Sid);
                // 指定 Sid 属性的字段 不进行自增长设置
                modelBuilder.Entity<Student>().Property(s => s.Sid).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

                // 指定 Name 属性映射到 ProductName 字段
                modelBuilder.Entity<Student>().Property(s => s.Name).HasColumnName("ProductName");
                // 指定 Name 属性的字段存储长度为50
                modelBuilder.Entity<Student>().Property(s => s.Name).HasMaxLength(50);
                // 指定 Name 属性的字段不允许为Null
                modelBuilder.Entity<Student>().Property(s => s.Name).IsRequired();
                // 指定 Name 属性的字段类型为 varchar
                modelBuilder.Entity<Student>().Property(s => s.Name).HasColumnType("varchar");
                // 指定 Name 属性的字段不支持 Unicode
                modelBuilder.Entity<Student>().Property(s => s.Name).IsUnicode(false);

                // 指定 Tuition 属性映射到 StuTuition 字段
                modelBuilder.Entity<Student>().Property(s => s.Tuition).HasColumnName("StuTuition");
                // 忽略映射 CulTuition 属性
                modelBuilder.Entity<Student>().Ignore(s => s.CulTuition);

            }
        }
    }

    

     ok!today就到这儿吧!拜了个拜

原文地址:https://www.cnblogs.com/LittleBai/p/13959476.html