对象关系映射
EF数据上下文
EF - EntityFrameWork
中文名:实体框架(数据持久化框架)
1.使用EF查询(Linq to EF)
1.1使用标准查询运算符来查询
OumindBlogEntities db = new OumindBlogEntities(); IQueryable<BlogArticle> list = db.BlogArticles.Where(a => a.ACate==2);
1.2使用Linq to EF
//1.EF默认使用延迟加载 //1.1 IQueryable<T> 支持延迟记载,linq编译成SQO,运行时会生成一颗 表达式树,也就是说,IQueryable中并没有保存查询的数据,而是保存了要查询的数据条件的 表达式树 IQueryable<BlogArticle> list = from a in db.BlogArticles where a.ACate == 2 select a; //1.2那么当使用 IQueryable的时候(ToList,foreach遍历),才会通过 表达式树里的条件生成 sql 语句,并到数据库查询 list.ToList().ForEach(a => Console.WriteLine(a.AId + "," + a.ATitle));
2.关于 Expression
//1.创建一个表达式对象 Expression<Func<string, bool>> pre = (str => str.Length > 0); //2.编译表达式树,生成 指定的 委托对象 Func<string,bool> fu = pre.Compile(); //3.执行委托 fu("123");
3.关于 形参设置默认值
public List<T> GetPagedList<TKey>(int pageIndex, int pageSize, out int rowCount, out int pageCount, Expression<Func<T, bool>> where, Expression<Func<T, TKey>> orderby, bool isAsc = true)
4.在程序集U中(比如UI层),如果使用 程序集B里的类b(比如BLL层):
4.1如果在类b中,直接创建 程序集D(比如DAL层)里的类的对象,没有问题。
4.2如果在定义类b的时候,使用到了程序集D的类(比如说继承,泛型参数),那么 程序集U 要求添加 程序集D的引用。
5.基于EF的新框架 简单三层
UI -> BLL - > IDAL -> DAL -> MODEL
1.表现层 直接使用 业务层对象
2.业务层 的每个具体的操作类,都继承于 业务父类,在业务父类中 定义了一个 数据层接口 IDAL、并且 业务父类 中还定义了一个 抽象方法;业务子类重写抽象方法,并实例化 父类里的 IDAL接口对象。
3.数据层 每个具体的操作类 继承于 数据父类,拥有父类通用的增删改查方法。
代码
新增
#region 1.0 新增 -void Add() /// <summary> /// 1.0 新增 /// </summary> static void Add() { //1.1创建实体对象 User uObj = new User() { uName = "刘德华", uLoginName = "aaa", uPwd = "asdfasdfasdfsadf", uIsDel = false, uAddtime = DateTime.Now }; //1.2通过EF新增到数据库 //1.2.1将对象 加入到 数据上下文 的 User集合中 //db.Users.Add(uObj); DbEntityEntry<User> entry = db.Entry<User>(uObj); entry.State = System.Data.EntityState.Added; //1.2.2调用数据上下文 的 保存方法,将 对象 存数数据库 db.SaveChanges(); Console.WriteLine("保存成功~~!"); } #endregion
查询
#region 2.0 简单查询 -void Query() /// <summary> /// 2.0 简单查询 /// </summary> static void Query() { //2.0 IEnumerable(集合) 和 IQueryable(EF里的DbSet<T>) 里的 SQO 本质不一样!: //2.1 集合 的 标准查询运算符 方法 ,是来自于 System.Linq.Enumerable 里 给 IEnumerable 接口添加的扩展方法 //List<string> listStr = new List<string>(); //listStr.Where //EF上下文里的 DBSet<T> 里的 标准查询运算符 方法,来自于 System.Linq.Queryable 里给 IQueryable接口 添加的扩展方法 //2.2 变相的 即时查询 List<User> list = db.Users.Where(u => u.uName == "刘德华").ToList(); list.ForEach(u => Console.WriteLine(u.ToString())); } #endregion #region 2.1 简单查询 延迟加载 -void QueryDelay_01() //【延时加载】 看成两种: //2.1 EF本身查询方法 返回的都是 IQueryable接口,此时并未查询数据库;只有当调用接口方法 获取 数据时,才会查询数据库 //2.1.1 【延时加载】,本质原因之一:当前可能通过多个SQO方法 来组合 查询条件,那么每个方法 都只是添加一个查询条件而已,无法确定本次查询条件 是否 已经添加结束 // 所以,没有办法在每个SQO方法的时候确定SQL语句是什么,只能返回一个 包含了所有添加的条件的 DBQuery 对象, // 当使用这个 DBQuery对象 的时候,才根据所有条件 生成 sql语句,查询数据库 static void QueryDelay_01() { DbQuery<User> dbQuery = db.Users.Where(u => u.uLoginName == "刘德华").OrderBy(u => u.uName).Take(2) as System.Data.Entity.Infrastructure.DbQuery<User>; //获得 延迟查询对象后,调用对象的 获取第一个数据方法,此时,【就会根据之前的条件】,生成sql语句,查询数据库了~~! User usr01 = dbQuery.FirstOrDefault();// ToList()...... Console.WriteLine(usr01.uLoginName); } //2.1.2【延迟加载】- 针对于 外键实体 的延迟(按需加载)! // 本质原因之二:对于外键属性而言,EF会在用到这个外键属性的时候才去查询 对应的 表。 static void QueryDelay_02() { IQueryable<UsersAddress> addrs = db.UsersAddresses.Where(a => a.udUId == 1);//真实返回的 DbQuery 对象,以接口方式返回 //a.此时只查询了 地址表 UsersAddress addr01 = addrs.FirstOrDefault(); //b.当访问 地址对象 里的 外键实体时,EF会查询 地址对应 的用户表;查询到之后,将数据 装入 这个外键实体 Console.WriteLine(addr01.User.uName); //c.【延迟加载】按需加载 的缺点:每次调用外键实体时,都会去查询数据库(EF有小优化:相同的外键实体只查一次) IQueryable<UsersAddress> addrs2 = db.UsersAddresses; foreach (UsersAddress add in addrs2) { Console.WriteLine(add.udAddress + ":userName=" + add.User.uName); } } #endregion #region 2.2 连接查询(生成 inner join) -void QueryInnerJoin() /// <summary> /// 2.2 连接查询(生成 inner join) /// </summary> static void QueryInnerJoin() { //通过Include方法,设置 EF 生成 sql 语句时,使用 inner join 把 地址表对应的 User属性 也查出来 // select * from UsersAddresses a inner join Users u on a.udId =u.id IQueryable<UsersAddress> addrs = db.UsersAddresses.Include("User").Where(a => a.udId == 1); foreach (UsersAddress add in addrs) { Console.WriteLine(add.udAddress + ":userName=" + add.User.uName); } //练习:查询消息表的同时,显示 消息发送人 和 接收名字 IQueryable<Msg> msgs = db.Msgs.Include("User").Include("User1"); foreach (Msg m in msgs) { Console.WriteLine(m.mId + ",发送人:" + m.User.uName + ",接收人:" + m.User1.uName + ",消息内容:" + m.mMsg); } } #endregion
#region 2.3 连接查询(自动生成 inner join) -void QueryInnerJoinAuto()
/// <summary>
/// 2.3 连接查询(自动生成 inner join) -void QueryInnerJoinAuto()
/// </summary>
static void QueryInnerJoinAuto()
{
//如果 使用 EF查询方法 并 结合 Select方法,在Select中 使用到了 实体的外键属性,那么 EF 会自动 生成 inner join 语句 查询数据库
var addrs = db.UsersAddresses.Where(a => a.udUId == 1).Select(a => new { aId = a.udId, aAddress = a.udAddress, aUsrName = a.User.uName }).ToList();
}
#endregion
修改
#region 3.0 官方推荐的 修改方式(先查询,再修改) /// <summary> /// 3.0 官方推荐的 修改方式(先查询,再修改) /// </summary> static void Edit() { //1.查询出一个 要修改的对象 -- 注意:此时返回的 是 一个 User类的 代理类对象(包装类对象) User usr = db.Users.Where(u => u.uId == 1).FirstOrDefault(); Console.WriteLine("修改前:" + usr.ToString()); //2.修改内容 -- 注意:此时其实操作的 是 代理类对象 的属性,这些属性,会将 值 设置给 内部的 User对象对应的属性,同时 标记此属性为已修改状态 usr.uName = "刘德华"; usr.uLoginName = "liudehua"; //3.重新保存到数据库 -- 注意:此时 ef上下文,会检查容器内部 所有的对象,找到 标记为修改的 对象,然后 找到 标记为修改的 对象属性,生成对应的 update语句 执行! db.SaveChanges(); Console.WriteLine("修改成功:"); Console.WriteLine(usr.ToString()); } #endregion #region 3.1 自己优化的 修改方式(创建对象,直接修改) /// <summary> /// 3.1 自己优化的 修改方式(创建对象,直接修改) /// </summary> static void Edit2() { //1.查询出一个 要修改的对象 User usr = new User() { uId = 8,uName="小白~~~"}; //2.将 对象 加入 EF容器,并获取 当前实体对象 的 状态管理对象 DbEntityEntry<User> entry = db.Entry<User>(usr); //3.设置 该对象 为被修改过 entry.State = System.Data.EntityState.Unchanged; //4.设置 该对象 的 uName属性 为 修改状态,同时 entry.State 被修改为 Modified 状态 entry.Property("uName").IsModified = true; //var u = db.Users.Attach(usr); //u.uName = "小白~~"; //3.重新保存到数据库 -- ef 上下文 会 根据 实体对象的 状态 ,根据 entry.State =Modified 的值 生成 对应的 update sql 语句 db.SaveChanges(); Console.WriteLine("修改成功:"); Console.WriteLine(usr.ToString()); } #endregion
删除
#region 4.0 删除 -void Delete() /// <summary> /// 4.0 删除 /// </summary> static void Delete() { //4.1创建要删除的 对象 User u = new User() { uId = 10 }; //4.2附加到 EF中 db.Users.Attach(u); //4.3标记为删除 注意:此方法 就是 起到了 标记 当前对象 为 删除状态 ! db.Users.Remove(u); /* 也可以使用 Entry 来附加和 修改 DbEntityEntry<User> entry = db.Entry<User>(u); entry.State = System.Data.EntityState.Deleted; */ //4.4执行删除sql db.SaveChanges(); Console.WriteLine("删除成功~~~"); } #endregion
批处理
#region 5.0 批处理 -- 上下文 SaveChanges 方法 的 好处!!!! /// <summary> /// 批处理 -- 上下文 SaveChanges 方法 的 好处!!!! /// </summary> static void SaveBatched() { //5.1新增数据 User uObj = new User() { uName = "刘德华", uLoginName = "aaa", uPwd = "asdfasdfasdfsadf", uIsDel = false, uAddtime = DateTime.Now }; db.Users.Add(uObj); //5.2新增第二个数据 User uObj2 = new User() { uName = "刘德华2", uLoginName = "aaa2", uPwd = "asdfasdfasdfsadf2", uIsDel = false, uAddtime = DateTime.Now }; db.Users.Add(uObj2); //5.3修改数据 User usr = new User() { uId = 8, uName = "又黑了~~~" }; DbEntityEntry<User> entry = db.Entry<User>(usr); entry.State = System.Data.EntityState.Unchanged; entry.Property("uName").IsModified = true; //5.4删除数据 User u = new User() { uId = 11 }; //4.2附加到 EF中 db.Users.Attach(u); //4.3标记为删除 注意:此方法 就是 起到了 标记 当前对象 为 删除状态 ! db.Users.Remove(u); db.SaveChanges(); Console.WriteLine("批处理 完成~~~~~~~~~~~~!"); } #endregion #region 5.1 批处理 -- 一次新增 50条数据 -void BatcheAdd() /// <summary> /// 5.1 批处理 -- 一次新增 50条数据 /// </summary> static void BatcheAdd() { for (int i = 0; i < 50; i++) { User uObj = new User() { uName = "刘德华" + i, uLoginName = "aaa" + i, uPwd = "asdfasdfasdfsadf" + i, uIsDel = false, uAddtime = DateTime.Now }; db.Users.Add(uObj); } db.SaveChanges(); } #endregion
反射批量修改