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>
第三步,在项目的 \bin\Debug 目录下创建数据库文件:
特别注意: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就到这儿吧!拜了个拜
本文来自博客园,作者:LI小白,转载请注明原文链接:https://www.cnblogs.com/LittleBai/p/13959476.html