-
简介
-
什么是ORM
-
搭建Entity FrameWork CodeFirst应用
-
数据库迁移
-
表属性常见配置
-
Entity FrameWork 一对多、多对多
一、简介
Entity Framework是微软的Object Relational Mapper(对象关系映射器),也就是我们平常说的ORM,它可以让应用程序开发者将关系型数据作为业务模型来使用。
Entity Framework分为 DabaBase First、Code First、Model First。本篇主要是结束Code First的一些应用。
- Database First:这是一种用于已存在数据库模式的方法。使用这种方法,EDM是从数据库模式中生成的,这种方法最适合于使用了已经存在的数据库的应用。
- Code First:这种方法中,所有的领域模型都是以类的形式编写的。这些类会建立我们的EDM,数据库模式会从这些类中创建。这种方法最适合于那些高度以领域为中心并且领域模型类创建优先的应用程序。这里需要的数据库只是为了这些领域模型的持久化机制。
- Model First:这种方法和Code First方法很相似,但是这种情况下我们使用了EDM视觉设计器来设计我们的模型。数据库模式和类将会通过这个概念模型生成。该模型将会给我们创建数据库的SQL语句,然后我们可以使用它来创建数据库并连接应用程序。
下面是使用Entity Framework的一些好处:
- 因为开发者不需要为数据访问编写所有需要的ADO.NET管道代码,因此这可以节省很多开发时间。
- 我们可以使用更高级的语言(例如C#)来编写所有的数据访问逻辑而不是编写SQL查询和存储过程。
- 因为数据库表没有高级的关系(如继承),然而领域实体是可以有的,所以业务模型(也就是概念模型)可以使用实体间的关系来适配应用领域。
- 底层的数据存储可以相对轻松地被取代,因为所有的数据访问逻辑都呈现在应用层而不是数据层。
二、什么是ORM
对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。
三、搭建Entity FrameWork CodeFirst应用
使用 Entity FrameWork必须引用 EntityFrameWork.DLL 文件,
可以通过VS 工具/NuGet管理器/命令控制台,在出现的命令窗口上输入 Install-Pageage EntityFramework 命令,回车自动安装。
在项目中创建 T_User 类
public class T_User { public T_User() { } /// <summary> /// id /// </summary> [Required] [Key] public int Id { get; set; } /// <summary> /// 用户名 /// </summary> [Required] [MaxLength(50)] public string UserName { get; set; } /// <summary> /// 账号 /// </summary> [Required] [MaxLength(50)] public string Account { get; set; } /// <summary> /// Sex /// </summary> [Required] public int Sex { get; set; } /// <summary> /// 密码 /// </summary> [Required] [MaxLength(100)] [Column("Password", TypeName = "varchar")] public string Password { get; set; } /// <summary> /// 地址 /// </summary> [MaxLength(200)] public string Address { get; set; } /// <summary> /// /// </summary> [MaxLength(500)] public string Memo { get; set; } }
设置配置文件,配置数据库连接串
打开项目目录下的APP.Config,(按照项目的实际情况)因为我的项目是普通的类库,
假如你的项目是Web项目,那就配置在 Web.Config。配置数据库的用户名和密码
<connectionStrings> <!-- 假如采用 Window连接方式,则取消注释采用第一个连接串 <add name="DBEntities" connectionString="Server=127.0.0.1;Database=MyDataBase;Integrated Security=SSPI" providerName="System.Data.SqlClient"/> --> <add name="DBEntities" connectionString="Server=127.0.0.1;Database=MyDataBase;uid=sa;password=123" providerName="System.Data.SqlClient"/> </connectionStrings>
添加数据库上下文
接下来我们创建数据库上下文,它是数据库的抽象。目前,我们只有一张表T_User,因而要给该数据库上下文定义一个属性来代表这张表
再者,一张表中一般肯定不止一条数据行,所以我们必须定义一个集合属性,EF使用 DbSet
来实现这个目的。
新建 DBEntitys.CS 类文件(类名按照自己的需要),集成 DbContext 类。
在这里,DbContext 是所有基于EF的上下文基类,通过它可以访问到数据库中的所有表。
上面的代码中调用了父类的构造函数,并且传入了一个键值对,键是name,值是 DBEntities,也就是刚刚配置文件的数据库连接串的Name,
这个键值对是定义在应用程序的配置文件中的,取决于你的应用程序类型,可能是app.config或者web.config。在我们的 程序中就是App.config。
在app.config文件的configuration
的节点下(不要在第一个节点下,否则报错)添加:
public class DBEntitys : DbContext { public DBEntitys() : base("DBEntities") { } public virtual DbSet<T_User> T_User { get; set; } }
做完上面的时候去以后,这个时候 咱们通过控制台程序去获取上下文对象里的T_User。
1 static void Main(string[] args) 2 { 3 using (var context = new DBEntitys()) 4 { 5 context.Database.CreateIfNotExists();//如果数据库不存在时则创建 6 } 7 Console.Write("DB has Created!");//提示DB创建成功 8 Console.Read(); 9 }
打开数据库,你会发现,EF帮我们自动创建了数据库和表
创建数据库的方式有两种:
- 下面我们会通过数据库迁移来实现数据库的创建。
- 通过EF数据库的API来创建。
四、数据库迁移
随着我们项目的进展,我们的数据库结构也可能发生改变时,我们可以使用EF本身来更新数据库。要使用这项技术,我们必须在项目上启用迁移(migration)。
首先我们把刚刚创建的数据删除。
打开 VS 工具/NubGet包管理器/程序包控制管理台,
1、默认项目选择刚才创建的项目 Entity(按照实际代码所在的项目),然后在窗口中输入 Enable-Migrations
,最后按下Enter键即可:
运行该命令后,我们会看到Data项目中多了一个名叫Migrations的文件夹,该文件夹里面有一个类,它指定了迁移配置,通过泛型参数将它连接到我们的数据库上下文。
2、然后在继续在窗口中输入 Add-Migrations migrations
,按下Enter键添加一个名为 FirstMigration 的迁移
3、执行 Update-DataBase
命令,更新数据库架构
如果更新数据库存在冲突而不能执行更新,可以添加 -Force强制执行,例如:“Update-Database -Force”
4、设置自动迁移
每次都通过控制台来进行迁移太过麻烦,可以设置为自动迁移。
打开 Migrations 文件夹下的 Configuration.CS文件,将 构造函数里的 AutomaticMigrationsEnabled=false; 改成 True
执行 Update-DataBase -Force
命令,更新数据库架构
有以下两个参数可以对自动迁移进行设置:
1. AutomaticMigrationsEnabled:获取或设置 指示迁移数据库时是否可使用自动迁移的值。
2. AutomaticMigrationDataLossAllowed:获取或设置 指示是否可接受自动迁移期间的数据丢失的值。如果设置为false,则将在数据丢失可能作为自动迁移一部分出现时引发异常
-------------------------------------------- 注意! ----------------------------------------------------------------------------
默认 Configuration() 方法里只有 AutomaticMigrationsEnabled = true;将 true改为 false,
然后添加 AutomaticMigrationDataLossAllowed = false;
以后再更新模型的时候,直接执行命令 Update-DataBase
命令,更新数据库架构即可。
1 internal sealed class Configuration : DbMigrationsConfiguration<Entity.DBEntitys> 2 { 3 public Configuration() 4 { 5 AutomaticMigrationsEnabled = true; 6 AutomaticMigrationDataLossAllowed = false; 7 } 8 9 protected override void Seed(Entity.DBEntitys context) 10 { 11 // This method will be called after migrating to the latest version. 12 13 // You can use the DbSet<T>.AddOrUpdate() helper extension method 14 // to avoid creating duplicate seed data. 15 } 16 }
5、数据库回滚
假如我们需要将数据库还原到之前的某个迁移版本,还是可以通过命令完成 Update-DataBase -TargetMigration:
命令,Migration
Version
填写迁移的版本:打开生成的数据库里面有个__MigrationHistory表,选择主键即可。 Migration
Version
如果你想回滚一切至空数据库,可以使用 Update-DataBase -TargetMigration:
命令。$initialDataBase
五、表属性常见配置
EF Code-First提供了一组DataAnnotation属性,您可以将其应用于您的域类和属性。 DataAnnotation属性覆盖默认的Code-First约定。
System.ComponentModel.DataAnnotations | |
Key | 映射到相关表的主键 |
Timestamp | 不为空的时间戳列 |
Required | 必填 |
MinLength | 最小长度 |
MaxLength | 最大长度,也是设置数据库列的最大长度 |
StringLength | 字符串长度 |
System.ComponentModel.DataAnnotations.Schema | |
Table | 指定将与该类映射的DB表的名称 |
Column | 指定要与属性映射的列名称和数据类型 |
ForeignKey | 指定导航属性的外键属性 |
public class T_User { public T_User() { } /// <summary> /// id /// </summary> [Required] [Key] public int Id { get; set; } /// <summary> /// 用户名 /// </summary> [Required] [MaxLength(50)] public string UserName { get; set; } /// <summary> /// 账号 /// </summary> [Required] [MaxLength(50)] public string Account { get; set; } /// <summary> /// Sex /// </summary> [Required] public int Sex { get; set; } /// <summary> /// 密码 /// </summary> [Required] [MaxLength(100)] [Column("Password", TypeName = "varchar")] public string Password { get; set; } /// <summary> /// 地址 /// </summary> [MaxLength(200)] public string Address { get; set; } /// <summary> /// /// </summary> [MaxLength(500)] public string Memo { get; set; } /// <summary> /// 班级 /// </summary> public int GradeId { get; set; } /// <summary> /// 班级 /// </summary> [ForeignKey("GradeId")] public virtual T_Grade T_Grade { get; set; } }
六、Entity FrameWork 一对多、多对多
在平时的开发工作中,经常有多表查询,表和表之前的关系有单表查询、一对多查询、多对多查询。
1、一对多
例:在学校里有很多学生,每个学生是一个用户,每个用户都所属一个班级。班级和用户是一对多的关系。下面通过 Code First 实现一对多查询
学生类:
public class T_User { public T_User() { } /// <summary> /// id /// </summary> [Required] [Key] public int Id { get; set; } /// <summary> /// 用户名 /// </summary> [Required] [MaxLength(50)] public string UserName { get; set; } /// <summary> /// 账号 /// </summary> [Required] [MaxLength(50)] public string Account { get; set; } /// <summary> /// Sex /// </summary> [Required] public int Sex { get; set; } /// <summary> /// 密码 /// </summary> [Required] [MaxLength(100)] [Column("Password", TypeName = "varchar")] public string Password { get; set; } /// <summary> /// 地址 /// </summary> [MaxLength(200)] public string Address { get; set; } /// <summary> /// /// </summary> [MaxLength(500)] public string Memo { get; set; } /// <summary> /// 班级 /// </summary> public int GradeId { get; set; } /// <summary> /// 班级 /// </summary> [ForeignKey("GradeId")] public virtual T_Grade T_Grade { get; set; } }
班级类:
/// <summary> /// 班级表 /// </summary> public class T_Grade { public T_Grade() { T_User = new HashSet<T_User>(); } /// <summary> /// id /// </summary> [Key] public int Id { get; set; } /// <summary> /// 班级名字 /// </summary> public string GradeName { get; set; } /// <summary> /// 班级编号 /// </summary> public string GradeCode { get; set; } /// <summary> /// 用户 /// </summary> public virtual ICollection<T_User> T_User { get; set; } }
配置DBContext
public class DBEntitys : DbContext { public DBEntitys() : base("DBEntities") { this.Configuration.LazyLoadingEnabled = false; //是否开启懒加载 [false/关闭] } public DBEntitys(string ConnStr) : base(ConnStr) { } public virtual DbSet<T_User> T_User { get; set; } public virtual DbSet<T_Grade> T_Grade { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<T_User>() .HasRequired<T_Grade>(a => a.T_Grade) .WithMany(a => a.T_User); } }
执行数据库迁移命令以后,生成的表如下:
添加数据进行测试
class Program { public static readonly DBEntitys context = new DBEntitys(); static void Main(string[] args) { AddData(); //添加数据 var model = context.T_Grade.FirstOrDefault(); foreach (var item in model.T_User) { Console.WriteLine("Name:" + item.UserName); } Console.Read(); } /// <summary> /// 添加数据 /// </summary> private static void AddData() { T_Grade t_Grade = new T_Grade() { GradeName = "一年级", GradeCode = "001" }; context.T_Grade.Add(t_Grade); //一个班级对应两个用户 T_User t_User = new T_User() { UserName = "User1", Account = "admin", Sex = 1, Password = "123456", GradeId = t_Grade.Id }; T_User t_User2 = new T_User() { UserName = "User2", Account = "0001", Sex = 1, Password = "987654", GradeId = t_Grade.Id }; context.T_User.Add(t_User); context.T_User.Add(t_User2); int row = context.SaveChanges(); Console.Write("数据添加成功");//提示DB添加成功 } }
2、多对多