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