原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(11)-验证码实现和底层修改
ASP.NET MVC+EF框架+EasyUI实现权限管系列
(开篇) (1):框架搭建 (2):数据库访问层的设计Demo (3):面向接口编程 (4 ):业务逻辑层的封装
(5):前台Jquery easyUI实现 (6):EF上下文实例管理 (7):DBSession的封装 (8):DBSession线程内唯一
前言:上篇博客我们简单的说了一下源代码管理工具(VSS)的使用,相信大家看完之后都能够会使用VSS源代码管理工具,在源代码管理工具中VSS算是最简单的,没有什么难度,就是重点理解签入,签出和回滚的含有以及如何操作。那么这篇博客我们开始讲述如何实现用户的登录以及对前面博友们的评论提出修改底层的东西进行了一次修改还有验证码的使用。那么下面我首先要说的是前面网友提出的一个小的修改。
1.Func<T, bool>和Expression<Func<T, bool>>的使用区别
(1)在前面我写ASP.NET MVC+EF框架+EasyUI实现权限管理系列(4)-业务逻辑曾的封装(http://www.cnblogs.com/hanyinglong/archive/2013/04/09/3011119.html)这篇博客的时候,有一位博友(@Qlin)提出了我在项目底层些查询的时候为什么不用Expression<Func<T,bool>>,因为当时我实现的代码是Func<T,bool> wherelambda来定义的,当时的代码如下:
1 //实现对数据库的查询 --简单查询 2 IQueryable<T> LoadEntities(Func<T, bool> whereLambda); 3 4 /// <summary> 5 /// 实现对数据的分页查询 6 /// </summary> 7 8 /// <typeparam name="S">按照某个类进行排序</typeparam> 9 10 /// <param name="pageIndex">当前第几页</param> 11 12 /// <param name="pageSize">一页显示多少条数据</param> 13 14 /// <param name="total">总条数</param> 15 16 /// <param name="whereLambda">取得排序的条件</param> 17 18 /// <param name="isAsc">如何排序,根据倒叙还是升序</param> 19 20 /// <param name="orderByLambda">根据那个字段进行排序</param> 21 22 /// <returns></returns> 23 24 IQueryable<T> LoadPageEntities<S>(int pageIndex, int pageSize, out int total, Func<T, bool> whereLambda, bool isAsc, Func<T, S> orderByLambda);
(2)那么根据那位博友提出来的意见,我查看资料,发现他们还真的有很大的区别,这里我就简单的说一下他们的区别,并且非常感谢博友们能提出这样的问题,希望广大博友们能真心的提出来项目中的问题,我将虚心接受。
(3)那么他们有什么区别呢?网上的资料是这样说的,Func<T,bool>本身就是一个委托(delegate),而Expression<Func<T,bool>>确实一个表达式,只有在编译之后才会变成委托,那么在EF中到底使用哪一个呢?又是为什么呢?其实如果我们写成Func<T,bool>类型的便来那个如果作为参数传递给where方法进行Linq查询时,Entity FrameWork将会产生全表查询,将整个数据库表忠的数据加载到内存中,然后再内存中根据where中的条件进一步查询,而Expression<Func<t,bool>>只是查询出来你where条件中的数据,不用去进行全表查询,所以我们修改后的代码是:
1 //实现对数据库的查询 --简单查询 2 3 IQueryable<T> LoadEntities(Expression<Func<T, bool>> whereLambda); 4 5 /// <summary> 6 7 /// 实现对数据的分页查询 8 9 /// </summary> 10 11 /// <typeparam name="S">按照某个类进行排序</typeparam> 12 13 /// <param name="pageIndex">当前第几页</param> 14 15 /// <param name="pageSize">一页显示多少条数据</param> 16 17 /// <param name="total">总条数</param> 18 19 /// <param name="whereLambda">取得排序的条件</param> 20 21 /// <param name="isAsc">如何排序,根据倒叙还是升序</param> 22 23 /// <param name="orderByLambda">根据那个字段进行排序</param> 24 25 /// <returns></returns> 26 27 IQueryable<T> LoadPageEntities<S>(int pageIndex, int pageSize, out int total, Expression<Func<T, bool>> whereLambda, 28 29 bool isAsc, Expression<Func<T, S>> orderByLambda);
(4)上面我们简单的介绍了一下Func<T, bool>和Expression<Func<T, bool>>的使用区别,我查到了就这些资料,如果那位博友还能够详细的说明的话,真心希望你在下面留言,我将非常的感谢你们。我参考的资料是:http://www.cnblogs.com/dudu/archive/2012/04/01/enitity_framework_func.html。
(5)综上所述的话,这时候我们就要对我们项目中数据库访问层,数据库访问接口层,业务逻辑层和业务逻辑接口层的查询方法都进行修改,这里就不多说了,和上面的基本差不多。
2.登录页面的设计
(1)首先我们到网上下载一个登录页面的Demo,然后经过修改之后放到我们MVC4的项目里面。
(2)然后我们在项目中添加一个LoginController控制器,然后再在Index上面添加一个空视图,最后我们将我们前面下载的Demo中的代码部署到Index.cshtml页面之上,最后生成的静态页面如图所示:
(3)这时候我们就把静态页面设计好了,这里静态页面的代码我就不往出来贴了,在后面我会把主要的代码写出来的并且共享整个项目的,那么下来我们看到了我们的验证码没有实现,只是一个假的图标在哪里,这时候我们开始着手验证码的设计。
3.如何设计验证码的实现
(1)通过上图我们看到现在我们已经实现了页面的展示,那么我们的验证码还没有,现在开始我们设计验证码,首先看我前台验证码这里的代码是:
1 <ul> 2 3 <li class="user_main_text">验证码: </li> 4 5 <li class="user_main_input"> 6 7 <input type="text" class="TxtPasswordCssClass" id="Code" name="Code"> 8 9 </li> 10 11 </ul> 12 13 <ul> 14 15 <li class="user_main_text">验证码: </li> 16 17 <li class="user_main_input"> 18 19 <img src="/Login/CheckCode?ID=1" id="imgCode" alt="单击可刷新" onclick="ClickRemoveChangeCode()" /> 20 21 <a href="javascript:void(0)" onclick="ClickRemoveChangeCode();return false;">看不清</a> 22 23 </li> 24 25 </ul>
(2)首先我们看到我们绑定验证码的这里是这样写的,<img src=”/Login/CheckCode?ID=1”>,那么前面的src绑定的地址什么意思呢?他的意思就是我们在Login控制器下面含有一个CheckCode方法来实现验证码的读取。
(3)我们要实现验证码,首先我们就要写一个生成验证码的类,没什么难度,网上一搜一大推,下面就是我封装的生成验证码的类,首先我们在LYZJ.UserLimitMVC.Common类库下面新建一个KenceryValidateCode.cs类来存放生成验证码的代码,在这里我们需要给类库引入命名空间System.Drawing。最终的代码如下:
1 namespace LYZJ.UserLimitMVC.Common 2 3 { 4 5 public class KenceryValidateCode 6 7 { 8 9 /// <summary> 10 11 /// 验证码的最大长度 12 13 /// </summary> 14 15 public int MaxLength 16 17 { 18 19 get { return 10; } 20 21 } 22 23 24 25 /// <summary> 26 27 /// 验证码的最小长度 28 29 /// </summary> 30 31 public int MinLength 32 33 { 34 35 get { return 1; } 36 37 } 38 39 40 41 /// <summary> 42 43 /// 生成验证码 44 45 /// </summary> 46 47 /// <param name="length">指定验证码的长度</param> 48 49 /// <returns></returns> 50 51 public string CreateValidateCode(int length) 52 53 { 54 55 int[] randMembers = new int[length]; 56 57 int[] validateNums = new int[length]; 58 59 string validateNumberStr = ""; 60 61 //生成起始序列值 62 63 int seekSeek = unchecked((int) DateTime.Now.Ticks); 64 65 Random seekRand = new Random(seekSeek); 66 67 int beginSeek = (int) seekRand.Next(0, Int32.MaxValue - length*10000); 68 69 int[] seeks = new int[length]; 70 71 for (int i = 0; i < length; i++) 72 73 { 74 75 beginSeek += 10000; 76 77 seeks[i] = beginSeek; 78 79 } 80 81 //生成随机数字 82 83 for (int i = 0; i < length; i++) 84 85 { 86 87 Random rand = new Random(seeks[i]); 88 89 int pownum = 1*(int) Math.Pow(10, length); 90 91 randMembers[i] = rand.Next(pownum, Int32.MaxValue); 92 93 } 94 95 //抽取随机数字 96 97 for (int i = 0; i < length; i++) 98 99 { 100 101 string numStr = randMembers[i].ToString(); 102 103 int numLength = numStr.Length; 104 105 Random rand = new Random(); 106 107 int numPosition = rand.Next(0, numLength - 1); 108 109 validateNums[i] = Int32.Parse(numStr.Substring(numPosition, 1)); 110 111 } 112 113 //生成验证码 114 115 for (int i = 0; i < length; i++) 116 117 { 118 119 validateNumberStr += validateNums[i].ToString(); 120 121 } 122 123 return validateNumberStr; 124 125 } 126 127 128 129 //C# MVC 升级版 130 131 /// <summary> 132 133 /// 创建验证码的图片 134 135 /// </summary> 136 137 /// <param name="containsPage">要输出到的page对象</param> 138 139 /// <param name="validateNum">验证码</param> 140 141 public byte[] CreateValidateGraphic(string validateCode) 142 143 { 144 145 Bitmap image = new Bitmap((int) Math.Ceiling(validateCode.Length*12.0), 22); 146 147 Graphics g = Graphics.FromImage(image); 148 149 try 150 151 { 152 153 //生成随机生成器 154 155 Random random = new Random(); 156 157 //清空图片背景色 158 159 g.Clear(Color.White); 160 161 //画图片的干扰线 162 163 for (int i = 0; i < 25; i++) 164 165 { 166 167 int x1 = random.Next(image.Width); 168 169 int x2 = random.Next(image.Width); 170 171 int y1 = random.Next(image.Height); 172 173 int y2 = random.Next(image.Height); 174 175 g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2); 176 177 } 178 179 Font font = new Font("Arial", 12, (FontStyle.Bold | FontStyle.Italic)); 180 181 LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height), 182 183 Color.Blue, Color.DarkRed, 1.2f, true); 184 185 g.DrawString(validateCode, font, brush, 3, 2); 186 187 //画图片的前景干扰点 188 189 for (int i = 0; i < 100; i++) 190 191 { 192 193 int x = random.Next(image.Width); 194 195 int y = random.Next(image.Height); 196 197 image.SetPixel(x, y, Color.FromArgb(random.Next())); 198 199 } 200 201 //画图片的边框线 202 203 g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1); 204 205 //保存图片数据 206 207 MemoryStream stream = new MemoryStream(); 208 209 image.Save(stream, ImageFormat.Jpeg); 210 211 //输出图片流 212 213 return stream.ToArray(); 214 215 } 216 217 finally 218 219 { 220 221 g.Dispose(); 222 223 image.Dispose(); 224 225 } 226 227 } 228 229 230 231 /// <summary> 232 233 /// 得到验证码图片的长度 234 235 /// </summary> 236 237 /// <param name="validateNumLength">验证码的长度</param> 238 239 /// <returns></returns> 240 241 public static int GetImageWidth(int validateNumLength) 242 243 { 244 245 return (int) (validateNumLength*12.0); 246 247 } 248 249 250 251 /// <summary> 252 253 /// 得到验证码的高度 254 255 /// </summary> 256 257 /// <returns></returns> 258 259 public static double GetImageHeight() 260 261 { 262 263 return 22.5; 264 265 } 266 267 } 268 269 }
(4)那么现在我们的验证码生成的类已经完成了,这时候我们根据<img src=”/Login/CheckCode?ID=1”>所知,我们要到Login控制器下面去创建CheckCode方法来实现能够从View层读取验证码显示出来,那么必然在我们项目当中women就要用到刚才定义的获取验证码的类,那么这时候women就要添加LYZJ.UserLimitMVC.Common的引用,这时候在Login控制器下面的读取验证码的方法代码如下:
1 /// <summary> 2 3 /// 验证码的实现 4 5 /// </summary> 6 7 /// <returns></returns> 8 9 public ActionResult CheckCode() 10 11 { 12 13 //首先实例化验证码的类 14 15 KenceryValidateCode validateCode = new KenceryValidateCode(); 16 17 //生成验证码指定的长度 18 19 string code = validateCode.CreateValidateCode(5); 20 21 //将验证码赋值给Session变量 22 23 Session["ValidateCode"] = code; 24 25 //创建验证码的图片 26 27 byte[] bytes = validateCode.CreateValidateGraphic(code); 28 29 //最后将验证码返回 30 31 return File(bytes, @"image/jpeg"); 32 33 }
(5)根据上面的代码,我们就实现了能够在前台显示验证码的功能,这里我就不详细的解释代码了,我在代码里面写了大量的注释,相信大家能够很容易的明白代码的意思,效果如图所示:
(6)那么这时候我们就有一个问题出现了,我们的验证码有时候可能看不清,当我们要单击”验证码本身”或者”看不清,换一张”的时候要能够动态的变化,下面我就简单介绍一下动态的变化验证码。
4.单击验证码的时候验证码随机获取
(1)当我们想要单击”验证码本身”或者”看不清,换一张”的字眼的时候我们就要实施的刷新一下验证码,那么我们看到我们在页面的HTML代码中就有一个javaScript的Clieck事件,<img src="/Login/CheckCode?ID=1" id="imgCode" alt="单击可刷新" onclick="ClickRemoveChangeCode()" /> ,最后我们只要使用JavaScript实现onClick事件的ClickRemoveChangeCode()方法,使用JavaScript实现此事件的方法如下:
1 @*引用Jquery文件的JS脚本*@ 2 3 <script src="~/Scripts/jquery-1.7.1.min.js"></script> 4 5 <script type="text/javascript"> 6 7 //单击重新改变验证码 8 9 function ClickRemoveChangeCode() { 10 11 //首先我们获取到验证码的路径 12 13 var code = $("#imgCode").attr("src"); 14 15 //然后重新给验证码的路径赋值 16 17 $("#imgCode").attr("src", code + "1"); 18 19 } 20 21 </script>
(2)这样我们的验证码就实现了,当然还有一些小样式的修改我就不说了,比如当鼠标移动上去的时候能够变成小手等。
(3)那么下篇博客我们将实现用户的登录,越往后面的话我会越说的简单,这些东西都不怎么难,如果大家不太清楚的话,可以留言或者加QQ群,我将很高兴为我们解决问题。
(4)最后在秀一下今天完成的验证码的功能,本来打算把登录一起写的,可是就验证码写了这么多,想想还是把登录放后面和写T4模版一起说吧。
Kencery返回本系列开篇