EF也是一种ORM技术框架, 将对象模型和关系型数据库的数据结构对应起来,开发人员不在利用sql去操做数据相关结构和数据。
如下是EF创建的数据库和对象之间关系sql
关系数据库
|
对象
|
数据库
|
DbContext类
|
表
|
DbContext中的DbSet<实体类名>
|
表间的关联
|
实体类之间的关联
|
字段
|
实体类的公有属性
|
单条数据
|
单个实体类的对象
|
约束(主键、外键默认值)
|
实体类中的特性
|
EF使用概念模型、 映射和存储模型。三个模型来描述映射关系数据库
public class Place { [Key] public int PlaceID { get; set;} public string Provice { get; set; } public string City { get; set; } //导航属性 public List<People> Population { get; set; } }
public class People { [Key] public int PeopleID{ get; set; } public string Name{ get; set; } public int Age{ get; set;} //外键,对应导航属性对象中的标识 [ForeignKey("Place")] public int PlaceID{ get; set;} //导航属性 public Place Place { get; set; } }
public class TestDB:DbContext { public TestDB():base("name=Test") { } public DbSet<Place> Place { get; set; } public DbSet<People> People { get; set; } }
class Program { static void Main(string[] args) { using (var context = new TestDB()) { context.Place.Add(new Place { PlaceID = 2, Provice="jiangsu", City="wuxi" }); context.SaveChanges(); } } }
发如今数据库已经建好了相应的表,这时在people类增长Sex属性,随便增长一条数据执行代码,报了以下错,很显然EF并不会帮咱们更新数据库结构:缓存
有两种状况处理这种状况,手动和自动迁移方式去更新数据库数据结构
一、选择所在的项目,在VS的程序包管理控制台输入命令(Tools菜单中打开Package Manager Console):框架
enable-migrations –EnableAutomaticMigrations
二、执行完毕,项目目录中多出一个名为Migrations的文件夹,里面有个Configuration.cs文件,打开它看到以下代码:数据库设计
internal sealed class Configuration : DbMigrationsConfiguration<EF1.Model.TestDB> { public Configuration() { AutomaticMigrationsEnabled = true; ContextKey = "EF1.Model.TestDB"; } protected override void Seed(EF1.Model.TestDB context) { // This method will be called after migrating to the latest version. // You can use the DbSet<T>.AddOrUpdate() helper extension method // to avoid creating duplicate seed data. } }
ContextKey属性指定了要执行迁移的DbContext,以便多个DbContext共存迁移不会产生冲突。ide
在迁移过程成功后会执行Seed方法,能够利用这个方法添加一些初始化数据函数
在删除属性的时候,会迁移失败,提示操做会形成数据丢失,能够在构造函数中添加以下代码,忽略数据丢失。学习
public Configuration() { AutomaticMigrationsEnabled = true; AutomaticMigrationDataLossAllowed = true; ContextKey = "EF1.Model.TestDB"; }
3.以上步骤已开启支持迁移,每次更新实体结构时,在项目执行前,须要在管理控制台输入如下命令先更新数据库spa
update-database
或是在DbContext构造函数中添加如下代码,每次将自动迁移至最新的数据库Schema
public class TestDB:DbContext { public TestDB():base("name=Test") { //TestDB:dbContext;
//Test:链接字符串名称 //迁移至最新版本 Database.SetInitializer(new MigrateDatabaseToLatestVersion<TestDB, Configuration>("Test")); } public DbSet<Place> Place { get; set; } public DbSet<People> People { get; set; } }
两种迁移方式基本相同,只是相比自动迁移,手动迁移会记录每次更新的状况,容许回滚数据库到某个指定版本,适合于团队开发。
1.同样要先开启支持迁移,并吧生成的Configuration构造函数中AutomaticMigrationsEnabled置为false,表示不使用自动迁移。
2.每次更改实体结构或映射配置时,在程序包管理控制台运行如下代码
Add-Migration ChangeSet1
会自动在前面生成的Migrations文件下生成一个ChangeSet1迁移文件
类的内容就是对于咱们所更改的实体结构或映射配置,EF迁移要执行的内容,如下就是我增长了一个Phone属性,运行Add-Migration ChangeSet1所生成的。此迁移文件后面能够用来回滚到某个版本
public partial class ChangeSet1 : DbMigration { public override void Up() { AddColumn("dbo.People", "Phone", c => c.String()); } public override void Down() { DropColumn("dbo.People", "Phone"); } }
3.成功迁移文件后,运行下面代码更新至最新的迁移文件对应的版本。
Update-Database
或是回滚到指定版本
Update-Database -TargetMigration ChangeSet1
上下文是根据检测实体的EntityState枚举状态,来执行相应的增/删/改操做。
EntityState枚举状态以下:
DBcontext类中的方法:
DbSet类
基本方式
static void Main(string[] args) { using (var context = new TestDB()) { context.Place.Add(new Place { PlaceID = 8, Provice = "jiangsu", City = "wuxi", }); context.SaveChanges(); } }
Entry方式
附:用attch附加到上下文保存无效,由于附加后的状态是unchange,须要使用ChangeObjectState方法更改实体状态,或是再次使用Entry获取状态管理对象更新
static void Main(string[] args) { using (var context = new TestDB()) { var obj = new Place() { PlaceID =9, Provice = "jiangsu", City = "wuxi", }; //加入EF容器,并获取当前实体对象的状态管理对象 var entity = context.Entry<Place>(obj); entity.State = System.Data.Entity.EntityState.Added; context.SaveChanges(); } }
先查询后更新:
static void Main(string[] args) { using (var context = new TestDB()) { var query = context.Place.Where(p => p.PlaceID == 9).FirstOrDefault(); query.City = "suzhou"; context.SaveChanges(); } }
Entry
using (var context = new TestDB()) { var obj = new Place { PlaceID=9, City="changzhou" }; //将obj加入到上下文,并去获取实体对象的状态管理对象 var entity = context.Entry<Place>(obj); //置为未被修改过 entity.State = System.Data.Entity.EntityState.Unchanged; //设置该对象的City属性为修改状态,同时 entity.State由Unchanged-> Modified 状态 entity.Property("City").IsModified = true; context.SaveChanges(); }
Attach
using (var context = new TestDB()) { var obj = new Place { PlaceID=9 }; //将obj附加到上下文,此时实体状态为Unchanged var newobj=context.Place.Attach(obj); //这时City属性为修改状态,同时entity.State自动由Unchanged-> Modified 状态 newobj.City = "yangzhou"; context.SaveChanges(); }
常规操做:先查询后删除
using (var context = new TestDB()) { var queryObj = context.Place.Where(q=>q.PlaceID==8).FirstOrDefault(); //当前的实体对象已置为Deleted状态 context.Place.Remove(queryObj); context.SaveChanges(); }
Entry
using (var context = new TestDB()) { var obj = new Place { PlaceID = 6 }; //将obj附加到上下文,并获取实体对象的状态管理对象 var entity =context.Entry<Place>(obj); //将状态置为Deleted entity.State = System.Data.Entity.EntityState.Deleted; context.SaveChanges(); }
Attach
using (var context = new TestDB()) { var obj = new Place { PlaceID = 7 }; //将obj附加到上下文,此时实体状态为Unchanged var newobj = context.Place.Attach(obj); context.Place.Remove(newobj); context.SaveChanges(); }
对于简单查询,不须要进行修改操做,建议使用AsNoTracking,无跟踪查询来提高效率
using (var context = new TestDB()) { var obj = context.Place.Where(p => p.PlaceID == 9).AsNoTracking().ToList(); }
Find方法 :以前一直使用lambda查询(where,FirstOrDefault),有一个问题就是无论咱们要查询的实体是否被dbconext缓存,都会去查询数据库,而使用Find经过主键来查询,会首先在EF Context中查找是否有被缓存过的实体,若是查找不到才去数据库查找
using (var context = new TestDB()) { var obj = context.Place.Find(9); }
注:更详细的EF查询学习会在查漏补缺(二) 数据加载 中继续记录