2.3 Entity Framework增删改查

2.3.1 附加数据库

从云盘中找到Northwind数据库文件,附加到SQL Server 2012,或者从网上下载Northwind数据库,下载地址为http://files.cnblogs.com/files/jiekzou/northwnd.zip

附加方式:打开SQL Server数据,右击“数据库”,选择“附加”命令,再选择数据库文件。

提示 如果使用的是低于SQL Server 2012版本的数据库,就会附加失败。你可以在网上下载低版本的Northwind数据库。

关于Northwind表字段的说明如下:

-------------------------Categories:种类表
--相应字段:CategoryID:类型ID; CategoryName:类型名;Description:类型说明;Picture:产品样本
-------------------------CustomerCustomerDemo:客户类型表1
--相应字段:CustomerID:客户ID; CustomerTypeID:客户类型ID
-------------------------CustomerDemographics:客户类型表2
--相应字段:CustomerTypeID:客户类型ID; CustomerDesc:客户描述
-------------------------Customers:客户表
--相应字段:CustomerID:客户ID; CompanyName:所在公司名称
--ContactName:客户姓名;ContactTitle:客户头衔;Address:联系地址
--City:所在城市;Region:所在地区;PostalCode:邮编;Country:国家
--Phone:电话;Fax:传真
-------------------------Employees:员工表
--相应字段:EmployeeID:员工代号;LastName + FirstName:员工姓名
--Title:头衔;TitleOfCourtesy:尊称;BirthDate:出生日期;HireDate:雇用日期
--Address:家庭地址;City:所在城市;Region:所在地区;PostalCode:邮编
--Country:国家用;HomePhone:宅电;Extension:分机;Photo:手机
--notes:照片;ReportsTo:上级;PhotoPath:照片
-------------------------EmployeeTerritories:员工部门表
--相应字段:EmployeeID:员工编号;TerritoryID:部门代号
-------------------------Order Details:订单明细表
--相应字段:OrderID:订单编号;ProductID:产品编号;UnitPrice:单价
--Quantity:订购数量;Discount:折扣
--------------------------Orders:订单表
--相应字段:OrderID:订单编号;CustomerID:客户编号;EmployeeID:员工编号
--OrderDate:订购日期;RequiredDate:预计到达日期;ShippedDate:发货日期
--ShipVia:运货商;Freight:运费;ShipName:货主姓名;ShipAddress:货主地址
--ShipCity:货主所在城市;ShipRegion:货主所在地区;ShipPostalCode:货主邮编
--ShipCountry:货主所在国家
---------------------------Products:产品表
--相应字段:ProductID:产品ID; ProductName:产品名称;SupplierID:供应商ID
--CategoryID:类型ID; QuantityPerUnit:数量;UnitPrice:单价
--UnitsInStock:库存数量;UnitsOnOrder:订购量;ReorderLevel:再次订购量
--Discontinued:中止
----------------------------Region:地区表
--RegionID:地区ID; RegionDescription:地区描述
----------------------------Shippers:运货商
--相应字段:ShipperID:运货商ID; CompanyName:公司名称;Phone:联系电话
----------------------------Suppliers:供应商表
--相应字段:ShipperID:供应商ID; CompanyName:供应商姓名;Phone;联系电话
----------------------------Territories:地域表
--相应字段: TerritoryID:地域ID; TerritoryDescription:地域描述;RegionID:地区ID

2.3.2 新建项目

右击EFDemo解决方案,选择“添加新项目→Windows→控制台应用程序”,修改项目名称为EFCRUD,然后按照2.2节的方式添加一个ADO.NET实体数据模型Northwind.edmx,再选择所有的表。

2.3.3 新增

在Program类中添加命名空间引用:

using System.Data.Entity.Infrastructure;
#region新增
      static int Add()
      {
          using (NorthwindEntities db = new NorthwindEntities())
          {
              Customers _Customers = new Customers
              {
                CustomerID = "zouqj",
                Address = "南山区新能源创新产业园",
                City = "深圳",
                Phone = "15243641131",
                CompanyName = "深圳跨境翼电商商务有限公司",
                ContactName = "邹琼俊"
              };
              //方法一
              //db.Customers.Add(_Customers);
              //方法二
              DbEntityEntry<Customers> entry = db.Entry<Customers>(_Customers);
              entry.State = System.Data.EntityState.Added;
              return db.SaveChanges();
          }
        }
#endregion

2.3.4 简单查询和延时加载

为了查看延时加载,要使用SqlProfiler跟踪SQL语句,加断点、加SqlProfiler跟踪事件。

EF生成SQL语句发送给数据库执行,我们可以使用SQL Server的跟踪监测工具查看。(可监测SQL语句、CPU占用、执行时间等。)

“延时加载”有两种形式:

(1)EF本身查询方法返回的都是IQueryable接口,此时并未查询数据库;只有当调用接口方法获取数据时,才会查询数据库。

static void QueryDelay1()
{
      using (NorthwindEntities db = new NorthwindEntities())
      {
              DbQuery<Customers>  dbQuery  =  db.Customers.Where(u  =>  u.ContactName
== "邹琼俊").OrderBy(u => u.ContactName).Take(1) as DbQuery<Customers>;
              //获得延迟查询对象后,调用对象的获取方法,此时,就会根据之前的条件生成SQL语句,
              //查询数据库了!
              Customers _Customers = dbQuery.FirstOrDefault();
              // 或者SingleOrDefault ()
              Console.WriteLine(_Customers.ContactName);
      }
}

在代码Customers_Customers处添加断点,然后打开SQL Server的跟踪监测工具SQL ServerProfiler,新建跟踪,然后运行,如图2-16所示。

图2-16

由于我们这里只需要跟踪SQL查询语句,因此我们可以在事件里面进行过滤,只选中TSQL事件类,在跟踪属性窗体中,切换到“事件选择”选项卡,进行如下操作,如图2-17所示。

图2-17

用到的事件说明如表2-1所示。

表2-1 事件选择器说明

当我们运行到断点的时候,监听窗体中没有监听到SQL执行语句,当执行下一步到dbQuery.FirstOrDefault()的时候,可以看到执行了如下SQL语句,如图2-18所示。

图2-18

(2)当前可能通过多个标准查询运算符(SQO)方法来组合查询条件,那么每个方法都只是添加一个查询条件而已,无法确定本次查询条件是否已经添加结束,所以没有办法在执行每个SQO方法的时候确定SQL语句是什么,只能返回一个包含了所有添加条件的DBQuery对象,就相当于一直在拼接SQL语句但是不执行,只有当使用这个DBQuery对象的时候才根据所有条件生成SQL语句,最终查询数据库。

static void QueryDelay2()
{
      using (NorthwindEntities db = new NorthwindEntities())
      {
              IQueryable<Orders>  _Orders  =  db.Orders.Where(a  =>  a.CustomerID  == "zouqj"); //真实返回的DbQuery对象,以接口方式返回
              //此时只查询了订单表
              Orders order = _Orders.FirstOrDefault();
              //当访问订单对象里的外键实体时,EF会查询订单对应的用户表,查到之后,再将数据装入
              //这个外键实体
              Console.WriteLine(order.Customers.ContactName);
              IQueryable<Orders> orderList = db.Orders;
              foreach (Orders o in orderList)
              {
                Console.WriteLine(o.OrderID          +          ":ContactName="          +          o.Customers.ContactName);
              }
      }
}

延迟加载的缺点是每次调用外键实体时都会去查询数据库,不过EF有小优化,即相同的外键实体只查一次。

禁用延迟的方法有ToList()、FirstOrDefault()、Include()等。

通常在多层架构中,数据访问层都会返回IQueryable,业务逻辑层根据需要转为List,这样可以有更多的选择。

2.3.5 根据条件排序和查询

首先我们需要引入命名空间System.Linq.Expressions。

#region根据条件排序和查询
      /// <summary>
      /// 根据条件排序和查询
      /// </summary>
      /// <typeparam name="TKey">排序字段类型</typeparam>
      /// <param name="whereLambda">查询条件lambda表达式</param>
      /// <param name="orderLambda">排序条件lambda表达式</param>
      /// <returns></returns>
      public List<Customers> GetListBy<TKey>(Expression<Func<Customers, bool>>
      whereLambda, Expression<Func<Customers, TKey>> orderLambda)
      {
          using (NorthwindEntities db = new NorthwindEntities())
          {
            return  db.Customers.Where(whereLambda).OrderBy(orderLambda).ToList();
          }
      }
#endregion

2.3.6 分页查询

#region分页查询
      /// <summary>
      /// 分页查询
      /// </summary>
      /// <param name="pageIndex">页码</param>
      /// <param name="pageSize">页容量</param>
      /// <param name="whereLambda">条件lambda表达式</param>
      /// <param name="orderBy">排序lambda表达式</param>
      /// <returns></returns>
      public List<Customers> GetPagedList<TKey>(int pageIndex, int pageSize,
      Expression<Func<Customers, bool>> whereLambda,
      Expression<Func<Customers, TKey>> orderBy)
      {
          using (NorthwindEntities db = new NorthwindEntities())
          {
              // 分页时一定注意: Skip之前一定要OrderBy
              return db.Customers.Where(whereLambda).OrderBy(orderBy).Skip
              ((pageIndex -1) * pageSize).Take(pageSize).ToList();
          }
      }
#endregion

2.3.7 修改

关于数据修改,微软官方推荐的修改方式是先查询再修改。

#region官方推荐的修改方式(先查询,再修改)
      /// <summary>
      /// 官方推荐的修改方式(先查询,再修改)
      /// </summary>
      static void Edit()
      {
          using (NorthwindEntities db = new NorthwindEntities())
          {
            //1.查询出一个要修改的对象 -- 注意:此时返回的是一个Customers类的代理类对象
            //(包装类对象)
            Customers _Customers = db.Customers.Where(u => u.CustomerID ==
            "zouqj").FirstOrDefault();
            Console.WriteLine("修改前:" + _Customers.ContactName);
            //2.修改内容 -- 注意:此时其实操作的是代理类对象的属性,这些属性会将值设置给内部
            //的Customers对象对应的属性,同时标记此属性为已修改状态
            _Customers.ContactName = "邹玉杰";
            //3.重新保存到数据库 -- 注意:此时EF上下文会检查容器内部所有的对象,先找到标记
              //为修改的对象,然后找到标记为修改的对象属性,生成对应的update语句执行
            db.SaveChanges();
            Console.WriteLine("修改成功:");
            Console.WriteLine(_Customers.ContactName);
          }
      }
#endregion
#region自己优化的修改方式(创建对象,直接修改)
      /// <summary>
      /// 自己优化的修改方式(创建对象,直接修改)
      /// </summary>
      static void Edit2()
      {
          //1.查询出一个要修改的对象
          Customers _Customers = new Customers()
          {
            CustomerID = "zouqj",
            Address = "南山区新能源创新产业园",
            City = "深圳",
            Phone = "15243641131",
            CompanyName = "深圳跨境翼电商商务有限公司",
            ContactName = "邹玉杰"
          };
          using (NorthwindEntities db = new NorthwindEntities())
          {
            //2.将对象加入EF容器,并获取当前实体对象的状态管理对象
            DbEntityEntry<Customers> entry = db.Entry<Customers>(_Customers);
            //3.设置该对象为被修改过
            entry.State = System.Data.EntityState.Unchanged;
            //4.设置该对象的ContactName属性为修改状态,同时entry.State被修改为
            //Modified状态
            entry.Property("ContactName").IsModified = true;
            //var u = db.Customers.Attach(_Customers);
            //u.ContactName = "郭富城";
            //5.重新保存到数据库 -- EF上下文会根据实体对象的状态entry.State =Modified
            //值生成对应的update sql语句
            db.SaveChanges();
            Console.WriteLine("修改成功:");
            Console.WriteLine(_Customers.ContactName);
        }
      }
#endregion

2.3.8 删除

#region删除 -void Delete()
      /// <summary>
      ///  删除
      /// </summary>
      static void Delete()
      {
          using (NorthwindEntities db = new NorthwindEntities())
          {
              //1.创建要删除的对象
              Customers u = new Customers() { CustomerID = "zouqj" };
              //2.附加到EF中
              db.Customers.Attach(u);
              //3.标记为删除--注意:此方法就是标记当前对象为删除状态
              db.Customers.Remove(u);
              /*
                也可以使用Entry来附加和修改
                DbEntityEntry<Customers> entry = db.Entry<Customers>(u);
                entry.State = System.Data.EntityState.Deleted;
              */
              //4.执行删除SQL
              db.SaveChanges();
              Console.WriteLine("删除成功!");
          }
        }
#endregion

2.3.9 批处理

在批处理中,我们能深深体会到上下文对象中SaveChanges方法的好处。

#region  批处理 -- 上下文SaveChanges方法的好处!
      /// <summary>
      /// 批处理 -- 上下文SaveChanges方法的好处!
      /// </summary>
      static void SaveBatched()
      {
          //1.新增数据
          Customers _Customers = new Customers
          {
              CustomerID = "zouyujie",
              Address = "洛阳西街",
              City = "洛阳",
              Phone = "1314520",
              CompanyName = "微软",
              ContactName = "邹玉杰"
          };
          using (NorthwindEntities db = new NorthwindEntities())
          {
              db.Customers.Add(_Customers);
              //2.新增第二个数据
              Customers _Customers2 = new Customers
              {
                CustomerID = "zhaokuanying",
                Address = "洛阳西街",
                City = "洛阳",
                Phone = "1314520",
                CompanyName = "微软",
                ContactName = "赵匡胤"
              };
              db.Customers.Add(_Customers2);
              //3.修改数据
              Customers usr = new Customers() { CustomerID = "zhaomu", ContactName
              = "赵牧" };
              DbEntityEntry<Customers> entry = db.Entry<Customers>(usr);
              entry.State = System.Data.EntityState.Unchanged;
              entry.Property("ContactName").IsModified = true;
              //4.删除数据
              Customers u = new Customers() { CustomerID = "zouyujie" };
              //5.附加到EF中
              db.Customers.Attach(u);
              //6.标记为删除--注意:此方法就是标记当前对象为删除状态
              db.Customers.Remove(u);
              db.SaveChanges();
              Console.WriteLine("批处理 完成~~~~~~~~~~~~! ");
          }
        }
#endregion
#region  批处理 -- 一次新增 50条数据 -void BatcheAdd()
        /// <summary>
        ///  批处理 -- 一次新增 50条数据
        /// </summary>
        static void BatcheAdd()
        {
          using (NorthwindEntities db = new NorthwindEntities())
          {
              for (int i = 0; i < 50; i++)
              {
                  Customers _Customers = new Customers
                  {
                    CustomerID = "zou" + i,
                    Address = "洛阳西街",
                    City = "洛阳",
                    Phone = "1314520",
                    CompanyName = "微软",
                    ContactName = "邹玉杰" + i
                  };
                  db.Customers.Add(_Customers);
              }
              db.SaveChanges();
          }
        }
#endregion