zoukankan      html  css  js  c++  java
  • 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就到这儿吧!拜了个拜

  • 相关阅读:
    golang 引用相对路径package
    LiteIDE 在 Windows 下为 Go 语言添加智能提示代码补全
    C#代码实现把网页文件保存为mht文件
    AE开发中关于 “无法嵌入互操作类型.........请改用适用的接口”问题的解决方法
    Windows下visual studio code搭建golang开发环境
    Eclipse配置开发Go的插件——Goclipse
    go语言条件语句 if else
    Go语言基础:method
    GO语言基础之method
    go中的接口
  • 原文地址:https://www.cnblogs.com/LittleBai/p/13959476.html
Copyright © 2011-2022 走看看