使用EF执行更新操作
方法1,写sql语句
//实例化实体
FHZMEntities fHZMEntities = new FHZMEntities();
//参数化列表
List<SqlParameter> sqlParameterList = new List<SqlParameter>() {
new SqlParameter("@UserName",userInfo.UserName),
new SqlParameter("@Number",userInfo.Number),
new SqlParameter("@UClass",userInfo.UClass),
new SqlParameter("@Ustate",userInfo.Ustate),
new SqlParameter("@Id",userInfo.Id),
};
//给实体添加sql语句并操作数据库,第二个参数是一数组,如果只是一个参数化对象就可以不是数组,但是如果是参数化对象集合就要转为数组,不然报错
int count = fHZMEntities.Database.ExecuteSqlCommand("update UserInfo set UserName=@UserName,Number=@Number,UClass=@UClass,Ustate=@Ustate where Id=@Id", sqlParameterList.ToArray());
//返回受影响的行数
return Json(count);
方法2:先查询再更新
这种修改方式比较通用,优点,实体只改数据库里需要修改的字段,不会全部修改,
缺点,它会先查询,会浪费一点效率,而且如果一个表单里有大量字段需要修改,这样把要修改的字段全部写出来赋值比较麻烦
查询需要用到id来查,直接修改则不需要,只需要传id即可,框架会自动从数据库里根据你传的id来操作需要修改的信息
//实例化实体
FHZMEntities fHZMEntities = new FHZMEntities();
//执行实体操作,从数据库查询相应的数据,
UserInfo NewUserInfo = fHZMEntities.UserInfo.Where(a => a.Id == userInfo.Id).FirstOrDefault();
//更改需要更新的信息
NewUserInfo.UserName = userInfo.UserName;
NewUserInfo.Number = userInfo.Number;
NewUserInfo.UClass = userInfo.UClass;
NewUserInfo.Ustate = userInfo.Ustate;
//对数据库执行操作
int count = fHZMEntities.SaveChanges();
//设置可以接收get请求return Json(1, JsonRequestBehavior.AllowGet);
//返回给ajax受影响的行数
return Json(count);
方法3:不查询直接修改
优点:如果有大量要修改的字段,它也只需要将对象添加到实体的更新请求,不需要一个一个字段修改,
改全部字段非常方便,因为不用把字段依次写出来,直接把对象给实体即可。
缺点:每一次都会将实体对象里的所有字段全部修改,因为它是将实体对象直接给模型,不能指定修改属性
查询需要用到id来查,直接修改则不需要,只需要传id即可,框架会自动从数据库里根据你传的id来操作需要修改的信息
//实例化实体
FHZMEntities fHZMEntities = new FHZMEntities();
//给实体添加一个修改的状态(他会认为实体对象所有字段都会修改,就是它是改全部)
fHZMEntities.Entry(userInfo).State = System.Data.EntityState.Modified;
//调用SaveChanges执行添加的状态
int count = fHZMEntities.SaveChanges();
//返回受影响的行数
return Json(count);
这种修改方式有种不好的地方,它是覆盖式修改,就是将要修改的对象的纪录里的每个字段全部修改,如果你有传值还好说,没有传值你不想改的字段就会默认被修改为空,如图:
如图:你不想修改用户的Hobby和gender字段的值,所以前台就没有传这两个值过来,所以传过来的对象里这两个字段的值是为空的
数据库中也可以看到没修改前的样子:
但是修改之后就可以看到,由于你没有传这两个值过来,所以数据库里这两个值被修改为空了。
这就是这个方法的弊端,虽然可以不用依次赋值,但是却不能修改指定字段,这个方法适用于修改表单的全部数据时使用,也就是要修改一个对象里的所有字段的时候很方便,不用像上一个方法一样依次赋值。
方法4:不需要查询,而且可以修改指定字段
这种写法和方法1类似,只不过方法1需要对要修改的字段赋值,而这种写法它结合第二种的写法,都是把值全部带到数据库,只不过
是对要修改的的字段改为true,这样就可以对它不赋值了,不像方法一那样麻烦,而且还不用查询一次,可以利用这个原理自己写一个方法,这样就可以不用一个一个把要修改的字段写出来了。
//实例化实体
FHZMEntities fHZMEntities = new FHZMEntities();
//把对象添加到上下文中
fHZMEntities.UserInfo.Attach(userInfo);
//获取指定的实体
var entry = fHZMEntities.Entry(userInfo);
//设置请求数据库后可以修改的字段
entry.Property("UserName").IsModified =true;
entry.Property("Number").IsModified = true;
entry.Property("UClass").IsModified = true;
entry.Property("Ustate").IsModified = true;
//数据库里除了上面几个字段都不能修改
////fHZMEntities.Entry(userInfo).Property("usernae").IsModified = true;,上面两个步骤可以这样简写
//调用执行实体的请求操作数据库的方法
int count=fHZMEntities.SaveChanges();
//返回受影响的行数
return Json(count);
上面的指定要修改的字段的书写可以这样写
最后,使用这种方法对方法3的缺点进行同样的测试:测试结果如下图,发现即使不传gender和Hobby这两个字段时,它也不会去修改数据库里的这两个字段。
方法5:自己通过反射+泛型实现通用的修改方法
这玩意儿以后学了反射再来写,这个方法可以放方法4更加简便
练习
单击表格里的更新,打开一个弹窗,在这个弹窗里可以修改用户的信息,点击确定之后提交到指定的控制器,在控制器里完成EF操作。
第一步:建一个隐藏元素,用于弹窗的内容:
弹窗的类型和正文修改好
第三步:只需要给表格里的修改的单击事件前把弹窗里的内容里的值赋值好,这样弹窗一打开里面就有用户的信息了
上面这张图里的做法并不优雅,因为是要对弹窗里的内容赋值,但是弹窗又是以页面的形式打开的,而且弹窗里的内容其实就是页面里的一个隐藏域,所以在单击事件执行时候赋值也是科学的,只是不优雅,其实,layer自己有一个加载事件,这个事件执行完了才打开弹窗,所以也可以在layer的加载事件里给这个页面的隐藏域赋值,如下图:
第四步,在弹窗里的yes的函数里写你访问控制器的方式,使用ajax和使用location.href形式都行,都是为了传值给控制器。
控制器只需要接受传过来的用户信息对象,然后让EF执行操作数据库修改即可。
如下图:控制器的参数:
所以你传的值也得按照这个对象里的属性名来传,不然控制器里的参数这个对象可能某些字段的值为空,传值格式如下图:
控制器处理完后使用ajax的形式不希望刷新页面来实现前台数据的更新的话,因为前台的数据都是网页加载时控制器里EF访问数据库时拿到的数据给前台的,如果改了值要使前台表格里的值实时更新的话,重新刷新页面即可,但是刷新页面用户体验不好,所以也可以通过ajax的回调函数里使用js修改当前行的数据即可。
练习2
这个练习和上面那个练习一样,只不过这次将layer的内容改为别的视图也就是将别的一个单独的网页作为它的内容。
第一步
下图就是被用作layer的内容的网页
第二步
设置layer的类型和路径即可让弹窗的内容为你指定的网页
第三步
至于你打开弹窗时里面的值怎么赋过去,方式有下面几种,:
第一种:
可以直接传个你点击的那个用户的id给控制器,控制器使用EF的实体查当前用户的数据,如下:
传用户的id给控制器:
第二步:控制器通过传过来的id通过实体查出这个用户的信息,并传给前台
第三步:前台里使用控制器传来的用户数据即可
这种方法不是很好,因为它还要重新从数据库里查询用户信息。接下来第二种方法:
第二种
直接把用户信息传过来
第一步:在被当做iframe层的页面(子页面)里用js写一个方法,便于外面这个页面(父页面)打开弹窗时调用,这个方法作用相当于接收传过来的参数并对这个页面里的文本框赋值,如图::
第二步:外面层在弹窗的加载事件里调用并传参数给这个方法
页面层调用iframe里的方法就是:
window.frames[frames层的id].方法名
默认frames每次用到frames层的时候浏览器会默认给frames元素取id和name,这两个都是一样的,默认都是”layui-layer-iframe”开头,至于最后的iframe是几就看你打开的是第几次了,
frame的name和id测试:
第一次打开:
第二次打开:
可以看到每次的id的最后的数字都会变,但是我们可以直接用layer的加载事件里的第二个参数来搞定这个问题,如果你iframe的名字写错就调不了你iframe里的方法了。
如下图为解决方法:
这里顺便提一下子页面iframe层调用外面页面(父页面)的方法的格式:
parent.方法名();
第四步
在修改完毕后点击确定,调用子页面的方法,使子页面(iframe层)完成更新,访问控制器,控制器使用EF实体操作数据库
方式1
提交给数据库后不想刷新前台页面实现更新,而使用ajax异步刷新:
在yes的事件里调用子页面更新前台数据的方法,把要修改的id和当前用户所在的行传给子页面
子页面的方法:这个方法就是在子页面里修改父页面的表格里的数据,并没有通过刷新的方式更新前台的表格里的数据。
方式2
刷新式更新前台数据:
这种方式简单,在前台刷新,也就是子页面层的保存的方法里的ajax的回调函数里返回受影响的行数,前台的回调函数里判断刷新即可
子页面的保存方法:
这里有个坑,一定要将ajax改为同步,不然前台接收到的返回值永远为0,因为是异步的话,它不好去暂停等ajax执行完毕后对count赋值,也就是还没有等ajax执行完,程序就已经执行完了return了,这样前台永远都不会刷新。
也可以不使用ajax,可以直接使用lacation.href,把值传过去,在控制器里判断是否刷新等,但是这样写更麻烦,因为location现在我不知道怎么接收返回值,所以不管EF是否执行成功这个弹窗里的内容都会变为那个控制器的视图,如果控制器没对应的视图还会报错,所以目前推荐使用ajax访问控制器,控制器返回受影响的行数这样写
父页面的回调函数刷新即可:
这里还有一个坑,为什么要把刷新放在前台呢,直接放在子页面(iframe)里刷新不香吗,
其实我们可以测试,如果直接放在子页面里,它会刷新子页面访问父页面,弹窗里就显示父页面了,因为弹窗还没有关,如下测试:
将子页面里的ajax回调函数里写判断刷新:
但是当我们在弹窗里点确定之后:它把内容显示在弹窗里了。
这种方法的总结:调用frames页面的方法,并把要修改的Id传过去,要接收返回值,根据返回值来判断是否刷新页面,不推荐使用这种方法,麻烦,而且逻辑放在leyer的子页面里就好了,让它处理,这个页面只需要负责打开弹窗,并传值过去即可
方式3(推荐使用)
在调用iframe的方法时传一个方法过去,让iframe的ajax的回调函数里执行即可,
子页面里接收这个方法,回调函数里执行
这样写还不用考虑异步同步的问题,因为根本不需要返回值回去,ajax执行完了会自动执行刷新页面的方法
练习3,批量更新
练习3和前面的逻辑一样,只不过点击的时候可以批量在文本框里修改多个用户的信息
步骤一
直接将选中的复选框的行改为编辑状态
这里第六个td可以复制上面筛选里的下拉列表框,减少代码
步骤二,保存
思路,前台接收所有修改的用户信息,将每个用户信息都单独存在一个对象里,然后再把这个用户对象放入一个存所有用户对象的数组里,然后序列化为json数组字符串传给控制器。
步骤三,控制器接收并反序列化
步骤四,循环添加状态,一次提交,使EF只操作一次数据库,避免循环操作数据库浪费效率,