Entity Framework 查漏补缺 (一)
明确EF建立的数据库和对象之间的关系
EF也是一种ORM技术框架, 将对象模型和关系型数据库的数据结构对应起来,开发人员不在利用sql去操作数据相关结构和数据。
以下是EF建立的数据库和对象之间关系
|
关系数据库
|
对象
|
|
数据库
|
DbContext类
|
|
表
|
DbContext中的DbSet<实体类名>
|
|
表间的关联
|
实体类之间的关联
|
|
字段
|
实体类的公有属性
|
|
单条数据
|
单个实体类的对象
|
|
约束(主键、外键默认值)
|
实体类中的特性
|
了解EDM( 实体数据模型)
EF使用概念模型、 映射和存储模型。三个模型来描述映射关系
- 概念模型 (.csdl) ︰ 即为直接使用的对象类,并包含它们之间的关系。
- 存储模型 (.ssdl) ︰ 数据库设计结构,包括表、 视图、 存储的过程和他们的关系和键。
- 映射 (.msl) ︰ 包含将概念模型(对象类)映射到存储模型(关系数据库)的信息。
CodeFirst :实体结构发生变化,如何更新数据库结构?
- 数据准备
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; }
}
- 创建DbContext派生类
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 = ,
Provice="jiangsu",
City="wuxi"
});
context.SaveChanges(); }
}
}
发现在数据库已经建好了相应的表,这时在people类增加Sex属性,随便增加一条数据执行代码,报了如下错,很显然EF并不会帮我们更新数据库结构:

有两种情况处理这种情况,手动和自动迁移方式去更新数据库
自动迁移
1、选择所在的项目,在VS的程序包管理控制台输入命令(Tools菜单中打开Package Manager Console):
enable-migrations –EnableAutomaticMigrations
2、执行完毕,项目目录中多出一个名为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共存迁移不会产生冲突。
在迁移过程成功后会执行Seed方法,可以利用这个方法添加一些初始化数据
在删除属性的时候,会迁移失败,提示操作会造成数据丢失,可以在构造函数中添加如下代码,忽略数据丢失。
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
ContextKey = "EF1.Model.TestDB";
}
3.以上步骤已开启支持迁移,每次更新实体结构时,在项目执行前,需要在管理控制台输入以下命令先更新数据库
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枚举状态如下:
- Detached:对象存在,但未由对象服务跟踪。在创建实体之后、但将其添加到对象上下文之前,该实体处于此状态;
- Unchanged:对象添加到上下文中后,还未被修改过;
- Added:对象已添加到对象上下文,还没有调用SaveChanges() 方法;
- Deleted:将对象从上下文中删除;
- Modified:对象已更改,还没有调用SaveChanges() 方法;
DBcontext类中的方法:
- Entry:将对象加入EF容器,并获取当前实体对象的状态管理对象。
- Set<T>:获取实体相应的DbSet类对象,如context.Set<Student>().Attach(student);
DbSet类
- Attach:是把一个已存在于数据库中,但没有被 dbContext 跟踪的对象,以EntityState.Unchanged 状态附加到 dbCotext 中,对于已有相同key的对象存在于EF Context的情况,如果这个已存在对象状态为Unchanged则不进行任何操作,否则将其状态更改为Unchanged。
- Add:将一个已存在于数据库中的对象,以Added实体状态添加到EF Context中。
- Remove:将一个已存在于EF Context中的对象标记为Deleted,当SaveChanges(已经存在于EF Context)时,这个对象对应的数据库条目被删除。
- Find:按主键去获取一个实体,首先在EF Context中查找是否有被缓存过的实体,如果查找不到再去数据库查找,如果数据库中存在则缓存到EF Context并返回,否则返回null。
- AsNoTracking:无跟踪查询,不受EFcontext管理,所以查询出来的数据不能做修改,对于只查询显示的功能,加上AsNoTracking可以提升效率。
- 。。。。。
增加
基本方式
static void Main(string[] args)
{
using (var context = new TestDB())
{
context.Place.Add(new Place
{
PlaceID = ,
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 =,
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 == ).FirstOrDefault();
query.City = "suzhou";
context.SaveChanges();
}
}
Entry
using (var context = new TestDB())
{
var obj = new Place
{
PlaceID=,
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=
};
//将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==).FirstOrDefault();
//当前的实体对象已置为Deleted状态
context.Place.Remove(queryObj);
context.SaveChanges();
}
Entry
using (var context = new TestDB())
{
var obj = new Place
{
PlaceID =
};
//将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 =
};
//将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 == ).AsNoTracking().ToList();
}
Find方法 :之前一直使用lambda查询(where,FirstOrDefault),有一个问题就是不管我们要查询的实体是否被dbconext缓存,都会去查询数据库,而使用Find通过主键来查询,会首先在EF Context中查找是否有被缓存过的实体,如果查找不到才去数据库查找
using (var context = new TestDB())
{
var obj = context.Place.Find();
}
注:更详细的EF查询学习会在查漏补缺(二) 数据加载 中继续记录
Entity Framework 查漏补缺 (一)的更多相关文章
- Entity Framework 查漏补缺 (三)
Code First的数据库映射 有两种方式来实现数据库映射: 数据属性:Data Annotation 映射配置: Fluent API 有继承关系的实体如何映射? Code First在生成数据库 ...
- Entity Framework 查漏补缺 (二)
数据加载 如下这样的一个lamda查询语句,不会立马去查询数据库,只有当需要用时去调用(如取某行,取某个字段.聚合),才会去操作数据库,EF中本身的查询方法返回的都是IQueryable接口. 链接: ...
- 【Android面试查漏补缺】之事件分发机制详解
前言 查漏补缺,查漏补缺,你不知道哪里漏了,怎么补缺呢?本文属于[Android面试查漏补缺]系列文章第一篇,持续更新中,感兴趣的朋友可以[关注+收藏]哦~ 本系列文章是对自己的前段时间面试经历的总结 ...
- Flutter查漏补缺1
Flutter 基础知识查漏补缺 Hot reload原理 热重载分为这几个步骤 扫描项目改动:检查是否有新增,删除或者改动,直到找到上次编译后发生改变的dart代码 增量编译:找到改变的dart代码 ...
- 《CSS权威指南》基础复习+查漏补缺
前几天被朋友问到几个CSS问题,讲道理么,接触CSS是从大一开始的,也算有3年半了,总是觉得自己对css算是熟悉的了.然而还是被几个问题弄的"一脸懵逼"... 然后又是刚入职新公司 ...
- js基础查漏补缺(更新)
js基础查漏补缺: 1. NaN != NaN: 复制数组可以用slice: 数组的sort.reverse等方法都会改变自身: Map是一组键值对的结构,Set是key的集合: Array.Map. ...
- 2019Java查漏补缺(一)
看到一个总结的知识: 感觉很全面的知识梳理,自己在github上总结了计算机网络笔记就很累了,猜想思维导图的方式一定花费了作者很大的精力,特共享出来.原文:java基础思维导图 自己学习的查漏补缺如下 ...
- 20165223 week1测试查漏补缺
week1查漏补缺 经过第一周的学习后,在蓝墨云班课上做了一套31道题的小测试,下面是对测试题中遇到的错误的分析和总结: 一.背记题 不属于Java后继技术的是? Ptyhon Java后继技术有? ...
- 今天開始慢下脚步,開始ios技术知识的查漏补缺。
从2014.6.30 開始工作算起. 如今已经是第416天了.不止不觉.时间过的真快. 通过对之前工作的总结.发现,你的知识面.会决定你面对问题时的态度.过程和结果. 简单来讲.知识面拓展了,你才干有 ...
随机推荐
- BZOJ_3747_[POI2015]Kinoman_线段树
BZOJ_3747_[POI2015]Kinoman_线段树 Description 共有m部电影,编号为1~m,第i部电影的好看值为w[i]. 在n天之中(从1~n编号)每天会放映一部电影,第i天放 ...
- nginx与Elasticsearch结合使用
Elasticsearch是一种先进的,高性能的,可扩展的开源搜索引擎,提供全文搜索和实时分析的结构化和非结构化的数据. 它的特定是可以通过HTTP使用 RESTful API,很容易的融入现有的we ...
- "unresolved reference 'appium' "问题解决
根据github的教程安装好"Appium-Python-Client"后,代码里写入"from appium import webdriver"就报错&quo ...
- NumPy 超详细教程(2):数据类型
系列文章地址 NumPy 最详细教程(1):NumPy 数组 NumPy 超详细教程(2):数据类型 NumPy 超详细教程(3):ndarray 的内部机理及高级迭代 文章目录 NumPy 数据类型 ...
- netcore程序部署到docker
1.基础准备 1. ubuntu 18.04 2. docker version 18.09 3. netcore 2.1 2.简介 自从netcore支持跨平台之后,以及现在很多公司都是采用容器化部 ...
- 拼多多大数据开发工程师SQL实战解析
不久前,裸考国内知名电商平台拼多多的大数据岗位在线笔试,问答题(写SQL)被虐的很惨,完了下来默默学习一波.顺便借此机会复习一下SQL语句的用法. 本文主要涉及到的SQL知识点包括CREATE创建数据 ...
- 面向对象之三个基本特征(javaScript)
1. 前言 2. 封装 3. 继承 4. 多态 5. 总结 1. 前言 了解过面向对象的同学应该都知道,面向对象三个基本特征是:封装.继承.多态,但是对于这三个词具体可能不太了解. 2. 封装 在说封 ...
- C# 错误:空对象不能转换为值类型
最近在做项目的时候出现了一个错误 当从数据库中获取值的时候 报错:空对象不能转换为值类型 因为数据库你查询数据的时候不是所有的字段都是存在数据的,有些字段可能是Null值,也就是没有数据 当你在类型转 ...
- HTML 练习清除浮动 :after
为 clearfix 类所在的 div 内部最后处添加一个 div 标签,内容为 . ,高度为0, 隐藏 <!DOCTYPE html> <html lang="en&qu ...
- CODING 研发管理系统上线全球加速,助力企业跨区域协作
CODING 研发管理系统现已全面支持全类型代码仓库的 全球加速访问. 随着国内互联网红利的日趋枯竭与全球互联网的加速普及.越来越多的企业开始走出国门,将目光投向全世界,搭建跨国体系.跨出国门的中国企 ...