https://docs.microsoft.com/zh-cn/ef/ef6/get-started
(微软官方文档)
https://github.com/aspnet/EntityFramework6/
(github的源码)
写在前面的话:关于SQL和LINQ,需看下官方的教程和说明,看官方是如何解释这两个语法,包括一些概念和术语,比如SQL中的select后面应该写的是什么东西 ,LINQ中的from后面写的是什么东西。
1 Entity Framework
EF是基于ADO.NET 数据库访问技术的ORM框架
底层访问呢数据库的实质依然是ADO.NET
ORM框架Object Relational Mapping ,框架包含了实例模型,数据模型,映射关系三部分
上下文类Dbcontext:内部封装了ado.net操作,用于对数据进行crud(增删改查)
子上下文类的属性,其实就是一个集合,可以通过父类的DbSet<>属性获得,利用编写抽象代码
Database First
根据数据库,建立数据库模型的时候,会生成相应代码程序集
Class MyContext:DbContext//MyContext 连接字符串名字的类名
{
public DbSet<UserInfo> UserInfo{get;set;} //数据库中有多少个表就有多少个DbSet<>泛型集合成员
}
public partial class UserInfo
{
public int Uid{get;set;}//根据表里面的字段生成相应的属性
public string UName{get;set}
}
MyContext myContext = new MyContext();
//查询语法 from in
var list = from xxx in myContext.UserInfo select xxx;
//Select
var list = myContext.UserInfo.Select(u=>u);
EF查询 linq
- 查询语法 查询语法的顺序和sql语句顺序稍微有点不一样
- 方法语法
1 基本查询
2 单条件,多条件
3 连接查询**(导航属性)**
4 多from查询(仅查询语法使用,结合导航属性使用)
5 部分列查询:匿名对象
6 分页查询:Skip,Take(仅lambda,一定要县排序后使用此方法)
7 对于一个命令语句,可以将查询语法,方法语法混合使用
8 返回值:IQueryable类型
查询语法
MyContext myContext = new MyContext();
//DbSet<UserInfo> UserInfo{get;set;}
//建议父类型的变量指向子类型对象,多态,解偶
DbContext dbContext = new MyContext();
//DbSet<TEntity> Set<TEntity>();
dbContext.Set<UserInfo>();
IQueryable<UserInfo> list;
//创建上下文对象
DbContext dbContext = new MyContext();
//基本查询
list = from xxx in dbContext.Set<UserInfo>()
select xxx;
//单条件查询
list = from xxx in dbContext.Set<UserInfo>()
where xxx.uid>3
select xxx;
//多条件查询
list = from xxx in dbContext.Set<UserInfo>()
where xxx.uid>3 && xxx.name.length >5;
select xxx;
//查询单列
var list1 = from xxx in dbContext.Set<UserInfo>()
select xxx.uid;
//查询多列 ()
var list1 = from xxx in dbContext.Set<UserInfo>()
select new xxx_viewmodel//具体对象
{
uid = xxx.uid,
name = xxx.name
};
var list1 = from cin dbContext.Set<UserInfo>()
select new //匿名对象
{
uid = xxx.uid,
name = xxx.name
};
方法语法
MyContext myContext = new MyContext();
//DbSet<UserInfo> UserInfo{get;set;}
//建议父类型的变量指向子类型对象,多态,解偶
DbContext dbContext = new MyContext();
//DbSet<TEntity> Set<TEntity>();
dbContext.Set<UserInfo>();
IQueryable<UserInfo> list;
//创建上下文对象
DbContext dbContext = new MyContext();
//基本查询
list = dbContext.Set<UserInfo>();
//单条件查询
list = dbContext.Set<UserInfo>().Where(u => uid>2);
//多条件查询
list = dbContext.Set<UserInfo>().Where(u => (u.uid > 2)m && (u.name.length < 6));
//当记录中没有数据时,FirstOrDefault不会抛异常,会返回一个Null,First会抛异常
list = dbContext.Set<UserInfo>().Where(u => (u.uid > 2)).Where(u => u.name.length < 6).FirstOrDefault();
list = dbContext.Set<UserInfo>().Where(u => (uid > 2)m || (name.length < 6));
//查询单列
var list = dbContext.Set<UserInfo>().Select(u => u.uid);
//查询多列 ()
var list1 = dbContext.Set<UserInfo>().Select(u => new XXX()
{
uid = u.uid;
name = u.name;
})
//分页查询
list_1 = dbContext.Set<UserInfo>().OrderByDescending(u=>u.uid).Skip(2).Take(3);
一对多的表间关系
当在创建数据库的时候,建立外键关系后
EF会自动将表间关系(1对多,1对1,多对多)生成导航属性
var list = from newsInfo in dbContext.Set<NewsInfo>()
select new
{
info = newsInfo.text;
type = newsInfo.NewsType.text;//在 NewsInfo类对象中有一个导航属性NewsType,存放着相应的记录对象。
}
var list = from newsType in dbContext.Set<NewsInfo>()
select new
{
type= newsType.text;
infos= newsType.NewsInfo.Select(u=>u.text);//在NewsType类对象中有一个导航属性NewsInfo,类型是ICollection<NewsInfo>,存放着相应的记录对象列表。
}
上面的父表和子表都具有导航属性,只是父表的导航属性是ICollection集合,而子表是单类型
//CRUD的两种方式,运用CRUD的方法和状态跟踪,本质都是状态跟踪(没仔细听...)
//增加
dbContext.Set<NewsInfo>().Add(newsInfo);
int result = dbContext.SaveChanges();
//删除
var userinfo = dbContext.Set<NewsInfo>().Where(u=>u.id = iddelete).FirstOrDefalut();//先查出来
dbContext.Set<UserInfo>().Remove(userinfo );//再删除
int result = dbContext.SaveChanges();
//修改
dbContext.Set<NewsInfo>().AddOrUpdate(userinfo);
//状态追踪修改方式 针对导航属性
//需要将外键值有效化,不能没有外键值,在对子表进行操作时,需要考虑外键值的有效否,需要引用父表
dbContext.Set<NewsInfo>().Attach(userinfo);
dbContext.Entry(userinfo).State = EntityState.Modified;
dbContext.Entry(userinfo).Property("xxx").IsModified = true;
dbContext.Entry(userinfo).Property("xxx").CurrentValue = userinfo.yyyy;
dbContext.Set<NewsInfo>().AddOrUpdate(userinfo);
EF特性-延迟加载
(这里有个疑问:和ADO的两个查询方式:DataSet和SqlDataReader 有关系吗)
查询语句返回值如果是IQueryable可以完成延迟加载
//延迟加载:如果不使用数据,则只是拼接sql语句,不会讲结果集加载到内存中来。
DbContext dbContext = new MyContext();
IQueryable<UserInfo> list1 = dbContext.Set<UserInfo>()//这里默认返回的是IQueryable<T>类型对象
.OrderByDescending(u=>u.id)//这里的OrderByDescending有多种重载函数,参数写lanmba表达式的,默认将表达式封装成Expression对象,默认是IQueryable<T>的扩展方法(OrderByDescending默认接受,IQueryable<T>类型的this指针)
.Skip(2)
.Take(3)
.Select();//这里的Select有多种重载函数,参数写lanmba表达式的,默认将表达式封装成Expression对象,默认是IQueryable<T>的扩展方法
IEnumerable<UserInfo> list2 = dbContext.Set<UserInfo>()
.AsEnumerable()//这里先将返回值对象强制转换成IEnumerable<T>类型
.OrderByDescending(u=>u.id)
.Skip(2)
.Take()
上述两种方式都只是在拼接sql语句,
但是IQueryable可以拼接完整的sql语句,每次使用List时,会向数据库执行完整的sql语句;(推荐使用IQueryable查询)
IEnumerable不能拼接完整sql语句,每次使用List时,它只是将第一个sql语句去数据库执行,其他都sql语句在内存中操作
IQueryable和IEnumerable的差异是:IQueryable继承IEnumerable,并且多了个Expression类型的属性,可以拼接树形命令。
集合类型的导航属性也采用延迟加载,,即导航属性在使用的时候,才回去执行sql语句去执行(这一点说起来感觉有点奇怪,感觉这句话有点多余,可能有什么点我没有Get到),可能在正常查询子表时,会自动查询父表的信息。
上面都是延迟加载,那如何禁止延迟加载呢?
- 如果查询结果是个集合,在查询语句的结尾调用**ToList()**方法
- 如果查询结果是个单值,在查询语句的结尾调用**FirstOrDefault()**方法
- 对于导航属性,可以在查询结果上使用**Include()**方法
IQueryable<UserInfo> list1 = dbContext.Set<UserInfo>().OrderByDescending(u=>u.id).Skip(2).Take(3).ToList();
IQueryable<UserInfo> list1 = dbContext.Set<UserInf().OrderByDescending(u=>u.id).Skip(2).Take(3).Include(n=>n.NewsType).ToList();
//导航属性的非延迟加载
延迟加载的优缺点
- 优点:用的时候才请求数据库获取数据,可以实时获取数据,适用于数据变化大的情况;
- 缺点:每次用数据时就会发生一次数据库请求,增加了数据库的压力,若数据没有变化性小的情况,延迟加载就不适用了;
总结:
EF实现curd
- 两种查询方式:查询语法和方法语法
- 增加,修改,删 有两种操作:方法版和状态版,本质都是状态版
- 导航属性:当两个边之间有外键关系时,在生产程序集时,会生产相关的导航属性,根据映射关系的不同,导航属性的类型不同,有集合类型和单值类型
- 延迟加载:IQueryable,IEnumerable ,导航属性默认延迟加载;同时可以禁止延迟加载
相关链接
https://www.cnblogs.com/wujingtao/p/5401113.html
http://www.cnblogs.com/VolcanoCloud/p/4517310.html
2 基本sql语句
1 sql 语句字符串用单引号
2 sql语句对大小写不敏感 (字段名,关键词,表名)
3 表名建议加前缀
insert into t_student(name,age) values('jay',19);--插入一条记录
select id,name,age from t_student;--表的选中字段列
select * from t_student;--表的所有字段列
select * from t_student where uid = "123";
delete from t_student where name = 'JAY';--删除name字段为JAY的记录
delete from t_student where age > 11 or height >170;--删除name字段为JAY的记录
delete from t_student where age > 11 and height >170;--删除name字段为JAY的记录
delete from t_student;--删除所有记录
update t_student set age = age+1;
update t_student set height = 100;
update t_student set height = 100,age = age+1;
update t_student where height < 170 set age =11;
update t_student where uid = '123' set age =11;
select name as name1,age as age1 from t_student;--查询结果看到的字段名为name1和age1,但数据库字段名并没有改动
select name,1 from t_student; --将t_student表里面的name字段列全为1
select GETDATE() as updatetime from t_student;--sql语句内置的函数
--数据汇总
select MAX(age) from t_student;--利用sql语句内置函数获取年龄的最大值单独成一列
select MAX(age) from t_student;
select MAX(age),MIN(age),AVG(age),COUNT(*) from t_student;--利用sql语句内置函数获取年龄的最大值,最小值,平均值,总数量呈四列
select COUNT(*) from t_student where Age > 100;
select COUNT(*) from t_student where Age > 100 && Height < 180;
//通配符
select * from t_student where Name like 'y%';--模拟搜索开头为y的字符,
select * from t_student where Name like 'y%k';--模拟搜索y开头k结尾字符
select * from t_student where Name like '%k%';--模拟搜索含有k字符
--like查询会很耗时间,一般在大型项目中慎用,一般用全文检索方式代替
--数据排序
select *from t_student order by Age;--按照年龄排序,order从小到大
select *from t_student order by Age Desc;--降序
select *from t_student order by Age,Height;--先Age升序 ,后Height升序
select *from t_student order by Age,Height Desc;--先Age升序 ,后Height降序
//先where过滤数据,在用order by排序
3 EF-edmx
1
2
3
4
5
6
7
4 EF-model first
1
2
3
4
5
6
7
8
9
10
11
12
5 EF-code first
运行后,数据库中就有相应的表