zoukankan      html  css  js  c++  java
  • ASP.NET MVC学习---(三)EF简单增删改查

    那么现在我们已经大概从本质上了解了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
    但是更加简便
    不信?
    上证据:
    //首先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!
    期待明天的行程
    一路走过来
    总是需要记录一些风景
    待人老了花谢了
    我们还有记忆可以回忆~
    那么今天记录就到此结束~








  • 相关阅读:
    面试题27:二叉树的镜像
    面试题26:树的子结构
    面试题25:合并两个排序的链表
    面试题24:反转链表
    面试题23:链表中环的入口节点
    面试题22:链表中倒数第k个节点
    欧拉函数的使用
    C++ STL 全排列函数详解
    Ubuntu系统安装网易云音乐、搜狗输入法
    Ubuntu系统 安装谷歌 Chrome 浏览器
  • 原文地址:https://www.cnblogs.com/jchubby/p/4429721.html
Copyright © 2011-2022 走看看