那么现在我们已经大概从本质上了解了ef
巴拉巴拉说了一大堆之后
总算要进入ef的正题了
总在口头说也太不行了是吧~
没错,现在要用ef进行一些实际的操作
做什么呢?
就做一个入门级的增删改查操作吧
废话不说,开搞~
首先
操作的数据库还是之前建立的例子
关系图:
已经是我们的老朋友啦
简单又好用~
还是建立一个控制台应用程序
根据数据库生成ADO.NET实体数据模型
准备工作做完之后
现在可以放开那个鼠标
放手敲代码吧~
使用EF最最开始要做什么事情?
准备一个EF上下文对象
因为EF的所有操作都要通过这个上下文对象来完成
之前我们说过
这个Model.Context.cs文件就是EF的上下文
后缀名是cs的是什么文件
是不是有一种似曾相识的赶脚?
不就是C#中的类嘛!
双击点开它瞧瞧
是不是一个很标准的名字叫做Entities的类?
其中包含了两个类型为DbSet集合的属性,分别是T_Products和T_Users
是不是又很熟悉
没错
这就是上下文对象中保存的
对应数据库表的属性
我们可以直接通过这个Entities.属性的方式方便快速的操作数据库
爽吧?
具体怎么爽
马上你就知道~
实例化一个Entities上下文对象
那么现在我们来想一想
使用ADO.NET进行新增操作的时候需要做什么?
无非就是new一个实体类对象,为该对象的各个属性赋值
在根据这么属性的值来生成sql语句发送到数据库
这种思路也通用于EF
但是更加简便
不信?
上证据:
然后使用之前实例化的EF上下文对象,找到T_Users属性,在点一下,是不是有一个很显眼的Add方法?
再看看这个Add方法的参数
是不是很明显的一个T_Users实体对象!
没错,不要怀疑了,就是它了
将之前new的实体对象传给Add方法
然后调用一下dbEntities的SaveChanges方法
注意,EF非查询操作只有在调用了这个方法之后才会向数据库发送请求!
这是一个很重要的方法,以后我们会详细的针对它展开讨论
现在暂时放过它吧~
整个新增方法如下:
没了
没了?!
怎么就没了?
这才几行代码
放心吧~
不骗你
不行走一个试试~
在Main方法中调用Create方法,运行
你还不确定的话回头去看数据库
没有这个数据你来找我~
那么此时你就会觉得EF很简单
因为有新增的方法肯定会有删除啊,修改啊之类的方法,到时候直接调不就行了~
先别着急下定论~
一是一横,二是两个横,三是三个横,难道四就是四个横吗?
很明显不是
先来看看Delete删除方法
EF中实现删除有两种方式
版本一:
版本二:
这不是有一个Remove吗,一看就知道是删除用的呀
但是这个Attach是什么鬼东西?
直接删除不就好了
干嘛还要附加,然后在删除
这不是闲着蛋疼吗?
Attach是真的附加方法
但是Remove就不是真的删除方法了
没证据我会乱说?
我们在Attach方法之后加一句代码
在Remove和SaveChanges方法分别设上断点,运行进行调试
程序执行完Attach之后在Remove停下
注意看下面的监视区域
user实体的状态信息为Unchanged
从字面上的意思来理解就是没有改变的
那么接着运行,执行Remove方法,停在SaveChanges
看到那个大红的Deleted了吗?
这就证明了
Remove方法并不是将实体从数据库中删除
只是将实体的State属性设置成Deleted
在上下文对象调用SaveChanges方法的时候
它一看到这个实体的State是Deleted
那么它就会向数据库发送删除的请求
其实第二个版本本质上还是继续这第一个版本的操作
只是简便了一些
至于查询方法
那就更爽了
怎么爽?
听说过Lambda吗?
没有?
那你就out了!
真的,赶紧去冲一下电,不骗你
因为在EF中使用Lambda真的是太爽了(当然也可以用Linq,这也很爽!)
怎么爽?
看看下面的代码你就知道了
就两行代码~
这里需要注意的是
Where方法的参数是一个Lambda表达式,并且返回的是IQueryable的泛型接口
所以如果只是查询一条数据,记得用FirstOrDefault方法,只返回一条数据
但是在这里又牵扯到了前面我们所说过的EF的延迟加载功能
详细介绍实在太长~
想了解的同志们小手一抖,轻轻带走EF延迟加载的本质原因
除了牵扯到延迟加载
还涉及到一个小问题
现在我们将代码改为只查询单个数据
在Console.WriteLine设置断点,运行
将user对象添加监视
注意看监视栏
是不是很惊悚?!
user的类型怎么变成System.Data.Entity.DynamicProxies.T_Users_185D30475F9AA2490A...
什么乱七八糟的东西
不是T_Users类型的对象吗?
终于找到为什么使用返回来的实体对象序列化成json格式
返回前台老是报错的原因了
因为人家根本就不是T_Users类啊!
完全两个不同的类型!
其实这是一个EF的动态代理类
那么这个动态代理类是什么飞机?
因为这里又涉及到了后面的修改等操作
所以这个动态代理类放在后面修改方法的地方在详细说明
最后我们来看看修改的方法
微软官方推荐的做法是先查询,后修改
就是先把要修改的数据查出来,然后修改完成之后再保存到数据库
具体操作如下:
但是总是觉得怪怪的
因为我们一般做修改的时候直接实例化对象穿进去,然后在根据Id修改对应数据的值
没用过先查询后修改的法子呀
接下来看看按照我们平常思路的修改方法
但是现在又觉得奇怪了
为什么要先设置对象的状态为Unchanged
然后在设置要修改的属性的IsModified=true
这好像又多此一举了?
EF修改数据的时候有一个特点
它会根据要修改的属性生成update语句
而不是将每个属性都加入到update语句中
譬如版本一的修改
我们只对UserName属性做了修改
那么最后生成的update语句也只会有UserName
想想看当一个表中字段有n多个时
但是我们只需要修改一个字段的值
那么update语句是写上全部的属性好
还是只写要修改的字段好?
答案不是很明显嘛!
使用版本一方法进行修改时
EF只生成有赋值过的属性
而在版本二中
我们同样希望能够使用EF的这个特性进行sql语句的优化
但是如果直接这么做的话
想都不用想肯定是生成所有的属性
所以我们需要先把实体的State设置为Unchanged
然后为要修改的属性
一一将它们的IsModified设置为true
什么意思很明显了吧?
两个修改版本最后的效果是一样的
怎么选择看个人爱好了~
那么是不是觉得EF的这个优化挺不错的?
但是我们修改user对象的哪个属性EF上下文是怎么知道的?
难道它时时刻刻都在监控user对象的信息吗?
这显然是不太可能的
它完成这个工作是靠着一个动态代理类来实现的
没错
就是前面查询操作提到的那货
下面给出一张图来解释
额...画的很难看
意思也表达的不是很全面
如果看不懂的同志
老方法
google之~
好了,简单的增删改查
get it!
期待明天的行程
一路走过来
总是需要记录一些风景
待人老了花谢了
我们还有记忆可以回忆~
那么今天记录就到此结束~
做什么呢?
就做一个入门级的增删改查操作吧
废话不说,开搞~
首先
操作的数据库还是之前建立的例子
关系图:
已经是我们的老朋友啦
简单又好用~
还是建立一个控制台应用程序
根据数据库生成ADO.NET实体数据模型
准备工作做完之后
现在可以放开那个鼠标
放手敲代码吧~
使用EF最最开始要做什么事情?
准备一个EF上下文对象
因为EF的所有操作都要通过这个上下文对象来完成
之前我们说过
这个Model.Context.cs文件就是EF的上下文
后缀名是cs的是什么文件
是不是有一种似曾相识的赶脚?
不就是C#中的类嘛!
双击点开它瞧瞧
是不是一个很标准的名字叫做Entities的类?
其中包含了两个类型为DbSet集合的属性,分别是T_Products和T_Users
是不是又很熟悉
没错
这就是上下文对象中保存的
对应数据库表的属性
我们可以直接通过这个Entities.属性的方式方便快速的操作数据库
爽吧?
具体怎么爽
马上你就知道~
实例化一个Entities上下文对象
那么现在我们来想一想
使用ADO.NET进行新增操作的时候需要做什么?
无非就是new一个实体类对象,为该对象的各个属性赋值
在根据这么属性的值来生成sql语句发送到数据库
这种思路也通用于EF
但是更加简便
不信?
上证据:
//首先new一个T_Users对象,并为其UserName属性赋值 T_Users user = new T_Users() { UserName = "JChubby" };
然后使用之前实例化的EF上下文对象,找到T_Users属性,在点一下,是不是有一个很显眼的Add方法?
再看看这个Add方法的参数
是不是很明显的一个T_Users实体对象!
没错,不要怀疑了,就是它了
将之前new的实体对象传给Add方法
然后调用一下dbEntities的SaveChanges方法
注意,EF非查询操作只有在调用了这个方法之后才会向数据库发送请求!
这是一个很重要的方法,以后我们会详细的针对它展开讨论
现在暂时放过它吧~
整个新增方法如下:
private static void Create() { T_Users user = new T_Users() { UserName = "JChubby" }; dbEntities.T_Users.Add(user); dbEntities.SaveChanges(); Console.WriteLine("添加成功!"); }
没了
没了?!
怎么就没了?
这才几行代码
放心吧~
不骗你
不行走一个试试~
在Main方法中调用Create方法,运行
你还不确定的话回头去看数据库
没有这个数据你来找我~
那么此时你就会觉得EF很简单
因为有新增的方法肯定会有删除啊,修改啊之类的方法,到时候直接调不就行了~
先别着急下定论~
一是一横,二是两个横,三是三个横,难道四就是四个横吗?
很明显不是
先来看看Delete删除方法
EF中实现删除有两种方式
版本一:
//实例化一个T_Users对象,并指定Id的值 T_Users user = new T_Users() { Id = 1 }; //将user附加到上下文对象中,并获得EF容器的管理对象 var entry = dbEntities.Entry(user); //设置该对象的状态为删除 entry.State = EntityState.Deleted; //保存修改 dbEntities.SaveChanges(); Console.WriteLine("删除成功!");这个应该可以理解的吧,虽然思路有点奇葩
版本二:
private static void Delete() { //实例化一个T_Users对象,并指定Id的值 T_Users user = new T_Users() { Id = 1 }; //将user附加到上下文对象中 dbEntities.T_Users.Attach(user); //删除user对象 dbEntities.T_Users.Remove(user); //保存修改 dbEntities.SaveChanges(); Console.WriteLine("删除成功!"); }
这不是有一个Remove吗,一看就知道是删除用的呀
但是这个Attach是什么鬼东西?
直接删除不就好了
干嘛还要附加,然后在删除
这不是闲着蛋疼吗?
Attach是真的附加方法
但是Remove就不是真的删除方法了
没证据我会乱说?
我们在Attach方法之后加一句代码
var entry = dbEntities.Entry(user);作用是方便看清楚entry变量的State属性,也就是user实体的状态信息
在Remove和SaveChanges方法分别设上断点,运行进行调试
程序执行完Attach之后在Remove停下
注意看下面的监视区域
user实体的状态信息为Unchanged
从字面上的意思来理解就是没有改变的
那么接着运行,执行Remove方法,停在SaveChanges
看到那个大红的Deleted了吗?
这就证明了
Remove方法并不是将实体从数据库中删除
只是将实体的State属性设置成Deleted
在上下文对象调用SaveChanges方法的时候
它一看到这个实体的State是Deleted
那么它就会向数据库发送删除的请求
其实第二个版本本质上还是继续这第一个版本的操作
只是简便了一些
至于查询方法
那就更爽了
怎么爽?
听说过Lambda吗?
没有?
那你就out了!
真的,赶紧去冲一下电,不骗你
因为在EF中使用Lambda真的是太爽了(当然也可以用Linq,这也很爽!)
怎么爽?
看看下面的代码你就知道了
private static void Query() { //取出数据库T_Users表中所有的数据,并转成集合 List<T_Users> users = dbEntities.T_Users.Where(u => true).ToList(); //循环遍历输出所有的user实体的UserName属性 users.ForEach(u => Console.WriteLine(u.UserName)); }好了,批量查询搞定
就两行代码~
这里需要注意的是
Where方法的参数是一个Lambda表达式,并且返回的是IQueryable的泛型接口
所以如果只是查询一条数据,记得用FirstOrDefault方法,只返回一条数据
但是在这里又牵扯到了前面我们所说过的EF的延迟加载功能
详细介绍实在太长~
想了解的同志们小手一抖,轻轻带走EF延迟加载的本质原因
除了牵扯到延迟加载
还涉及到一个小问题
现在我们将代码改为只查询单个数据
var user = dbEntities.T_Users.Where(u => u.Id == 3).FirstOrDefault(); Console.WriteLine(user.UserName);
在Console.WriteLine设置断点,运行
将user对象添加监视
注意看监视栏
是不是很惊悚?!
user的类型怎么变成System.Data.Entity.DynamicProxies.T_Users_185D30475F9AA2490A...
什么乱七八糟的东西
不是T_Users类型的对象吗?
终于找到为什么使用返回来的实体对象序列化成json格式
返回前台老是报错的原因了
因为人家根本就不是T_Users类啊!
完全两个不同的类型!
其实这是一个EF的动态代理类
那么这个动态代理类是什么飞机?
因为这里又涉及到了后面的修改等操作
所以这个动态代理类放在后面修改方法的地方在详细说明
最后我们来看看修改的方法
微软官方推荐的做法是先查询,后修改
就是先把要修改的数据查出来,然后修改完成之后再保存到数据库
具体操作如下:
private static void Modify() { //将Id为2的T_Users数据取出 var user = dbEntities.T_Users.Where(u => u.Id == 2).FirstOrDefault(); Console.WriteLine("修改之前:" + user.UserName); //修改UserName属性 user.UserName = "222222222222222"; //保存修改 dbEntities.SaveChanges(); Console.WriteLine("修改之后:" + user.UserName); }这么做虽然也是挺方便的吧
但是总是觉得怪怪的
因为我们一般做修改的时候直接实例化对象穿进去,然后在根据Id修改对应数据的值
没用过先查询后修改的法子呀
接下来看看按照我们平常思路的修改方法
//实例化user对象 T_Users user = new T_Users() {Id = 1, UserName = "2222"}; //将user加入EF容器中,并获得EF容器的管理对象 var entry = dbEntities.Entry(user); //设置对象的状态为Unchanged entry.State = EntityState.Unchanged; //设置要修改的属性的IsModified=true,表示这个属性是要修改的 entry.Property("UserName").IsModified = true; //保存 dbEntities.SaveChanges();
但是现在又觉得奇怪了
为什么要先设置对象的状态为Unchanged
然后在设置要修改的属性的IsModified=true
这好像又多此一举了?
EF修改数据的时候有一个特点
它会根据要修改的属性生成update语句
而不是将每个属性都加入到update语句中
譬如版本一的修改
我们只对UserName属性做了修改
那么最后生成的update语句也只会有UserName
想想看当一个表中字段有n多个时
但是我们只需要修改一个字段的值
那么update语句是写上全部的属性好
还是只写要修改的字段好?
答案不是很明显嘛!
使用版本一方法进行修改时
EF只生成有赋值过的属性
而在版本二中
我们同样希望能够使用EF的这个特性进行sql语句的优化
但是如果直接这么做的话
var entry = dbEntities.Entry(user); entry.State = EntityState.Modified;
想都不用想肯定是生成所有的属性
所以我们需要先把实体的State设置为Unchanged
然后为要修改的属性
一一将它们的IsModified设置为true
什么意思很明显了吧?
两个修改版本最后的效果是一样的
怎么选择看个人爱好了~
那么是不是觉得EF的这个优化挺不错的?
但是我们修改user对象的哪个属性EF上下文是怎么知道的?
难道它时时刻刻都在监控user对象的信息吗?
这显然是不太可能的
它完成这个工作是靠着一个动态代理类来实现的
没错
就是前面查询操作提到的那货
下面给出一张图来解释
额...画的很难看
意思也表达的不是很全面
如果看不懂的同志
老方法
google之~
好了,简单的增删改查
get it!
期待明天的行程
一路走过来
总是需要记录一些风景
待人老了花谢了
我们还有记忆可以回忆~
那么今天记录就到此结束~