七色花基本权限系统 - 初步使用EntityFramework实现用户登录
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了七色花基本权限系统 - 初步使用EntityFramework实现用户登录相关的知识,希望对你有一定的参考价值。
这篇日志将演示:
1、利用EF的Code First模式,自动创建数据库
2、实现简单的用户登录(不考虑安全仅仅是密码验证)
为什么选择EntityFramework
第一,开发常规中小型系统,能够提高开发效率。
针对小型系统,ORM提高开发效率那是立竿见影。而且linq+lambda的用户体验非常棒,让代码可读性大大增强。即使把linq写得很烂,小系统也无所谓。
针对中型系统,在对ORM有一定了解并且对linq to entity也掌握一定技巧的基础上,使用ORM还是可以提高开发效率。
第二,对开发人员的sql技巧没有要求。
针对不同的数据库,需要写的sql语句是有差别的。写linq不用管这个,不会写sql都没事。
我不喜欢写sql,写得也很烂。不过EF也支持开发人员自己写sql。
为什么选择EF的Code First
第一,在开发阶段时,可以做到真正的忽略数据库(没错我就是那么讨厌数据库硬编程的方式)。
第二,实体类属性数据类型的控制,可以很精确 (比如你可以定义实体类的某个属性的数据类型为枚举)
第三,项目稳定后,即使想“不通过实体类去更新数据库表”,也可以。关闭EF对数据库的检测,数据库中手动修改结构,同时手动调整实体类。
第四,EF7中,将只会保留Code First模式,说明官方也认为Code First才是ORM的正确使用方式
数据实体
首先要注意分离数据实体层和数据上下文层。
新建类库项目,名称S.Framework.Entity,用来存放映射到数据库的实体类。
同时为考虑“多个数据库”的可能,以文件夹进行隔离,在根目录下创建文件夹Master(一个区别标识,可以按需定义)。这个设计将大大影响后面的架构设计,请务必注意理解。
根据需求抽象出实体模型。
演示简单的用户登录功能,一个用户表即可。
1 namespace S.Framework.Entity.Master 2 { 3 /// <summary> 4 /// 用户 5 /// </summary> 6 public class SysUser 7 { 8 /// <summary> 9 /// 主键 10 /// </summary> 11 public string ID { get; set; } 12 13 /// <summary> 14 /// 用户名 15 /// </summary> 16 public string UserName { get; set; } 17 18 /// <summary> 19 /// 登录密码 20 /// </summary> 21 public string Password { get; set; } 22 23 /// <summary> 24 /// 是否禁用 25 /// </summary> 26 public bool IsDisabled { get; set; } 27 28 /// <summary> 29 /// 是否删除 30 /// </summary> 31 public bool IsDeleted { get; set; } 32 33 /// <summary> 34 /// 获取或设置一个 <see cref="string"/> 值,该值表示实体对象的数据创建者。 35 /// </summary> 36 public virtual string CreateUser { get; set; } 37 38 /// <summary> 39 /// 获取或设置一个 <see cref="DateTime"/> 值,该值表示实体对象的数据创建时间。 40 /// </summary> 41 public virtual DateTime CreateDate { get; set; } 42 43 /// <summary> 44 /// 获取或设置一个 <see cref="string"/> 值,该值表示实体对象的数据最后修改者。 45 /// </summary> 46 public virtual string LastModifyUser { get; set; } 47 48 /// <summary> 49 /// 获取或设置一个 <see cref="DateTime"/> 值,该值表示实体对象的数据最后修改时间。 50 /// </summary> 51 public virtual DateTime? LastModifyDate { get; set; } 52 } 53 }
请一定注意实体类的命名空间。
为区分各实体类的功能,可以套一个文件夹进行分类,比如System、Basic、Hr等。如下图:
请一定注意实体类的命名空间,命名空间的最后一级必须是“数据库标识”。
这里多讲一句,不要在实体类中增加“数据库映射”相关的特性。因为数据实体和数据核心应该是解耦的,作为数据实体,它不关心使用自己的数据驱动是EF还是dapper,那么就不该在它的身上体现任何“站队伍”的痕迹。
数据核心
数据核心应该能够支持多种数据驱动。这里先演示EF的使用。
新建类库项目,名称S.Framework.DataCore,然后从nuget上先安装EntityFramework。
创建结构如下图:
EntityContexts用来存储EF的数据库上下文,因为在实体层定义了Master作为数据库标识,因此在这里建立相对应的数据库上下文。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Data.Entity; 7 using System.Data.Entity.ModelConfiguration.Conventions; 8 9 namespace S.Framework.DataCore.EntityFramework.EntityContexts 10 { 11 /// <summary> 12 /// 数据库上下文 13 /// </summary> 14 public class MasterEntityContext : DbContext 15 { 16 /// <summary> 17 /// 构造函数 18 /// </summary> 19 /// <param name="nameOrConnectionString">数据库名称或连接字符串。</param> 20 public MasterEntityContext(string nameOrConnectionString) 21 : base(nameOrConnectionString) 22 { } 23 24 /// <summary> 25 /// 模型配置重写 26 /// </summary> 27 /// <param name="modelBuilder">数据实体生成器</param> 28 protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder) 29 { 30 // 禁用一对多级联删除 31 modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); 32 // 禁用多对多级联删除 33 modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>(); 34 // 禁用表名自动复数规则 35 modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); 36 } 37 } 38 } 39
数据库上下文创建之后,还需要关联数据实体类,这样EF才能知道“数据库表应该是哪些实体”。在S.Framework.DataCore中引用S.Framework.Entity,然后在MasterEntityContext的中增加属性,用来表示对实体的使用。
1 /// <summary> 2 /// 用户 3 /// </summary> 4 public DbSet<SysUser> Users { get; set; }
通过EF自动创建数据库
到这步,数据实体、数据上下文都有了,理论上来说,就可以在WebUI层中使用了。为演示EF效果,就先这么使用吧,后面再慢慢地完善。
首先,UI层需引用Entity和DataCore。然后在UI层的web.config文件中配置数据库信息。
注意一下,EF-CodeFirst模式能够自动创建数据库,所以在配置数据库连接字符串的时候,设置一个不存在的数据库名是完全没问题的。
1 <connectionStrings> 2 <add name="matrixkey" connectionString="server=MATRIXKEY\\MATRIXKEYSQL2012;database=S-SevenMaster-1;uid=sa;password=1;" providerName="System.Data.SqlClient" /> 3 </connectionStrings>
还需要在UI层也引用EntityFramework(这个耦合会在后面移除掉,此处为做演示先引用),然后修改HomeController来尝试操作数据库。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 7 using S.Framework.Entity.Master; 8 using S.Framework.DataCore.EntityFramework.EntityContexts; 9 10 namespace S.Framework.WebClient.Controllers 11 { 12 public class HomeController : Controller 13 { 14 public ActionResult Index() 15 { 16 var db = new MasterEntityContext("matrixkey"); 17 18 //初始化用户实体类,只需要定义不该为空属性即可 19 //其实String类型的属性,通过EF映射到数据库中后,字段都是允许为空的,这个需要通过“实体配置类”来进行控制,下一章节会讲。 20 var entity = new SysUser { ID = Guid.NewGuid().ToString(), UserName = "admin", Password = "123456", CreateDate = DateTime.Now, CreateUser = "admin" }; 21 //将用户对象附加给数据库上下文(这仅仅是内存级别的操作) 22 db.Users.Add(entity); 23 //数据库上下文保存更改(提交变更到数据库执行) 24 db.SaveChanges(); 25 26 return View(); 27 } 28 } 29 }
编译生成,运行。如果成功打开/Home/Index页面,则表示执行成功。可以打开数据库,检查是否创建了名为“S-SevenMaster-1”的数据库,同时检查该数据库下是否创建了SysUser表,并且里面有一条admin数据。
__MigrationHistory表是EF自动生成的用来记录“数据库结构变更操作”的历史表,不用管它。有兴趣的读者可以研究一下,也很有意思。
用户登录
先注释HomeController中那段往数据库里添加admin用户的代码,不然每次打开/Home/Index页面都会新增admin用户信息。
先有模型后有天,要登录先定义登录页面的表单数据模型吧。可以在Models文件夹建立LoginModel类。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 6 namespace S.Framework.WebClient.Models 7 { 8 /// <summary> 9 /// 登录数据模型 10 /// </summary> 11 public class LoginModel 12 { 13 /// <summary> 14 /// 登录用户名 15 /// </summary> 16 public string UserName { get; set; } 17 18 /// <summary> 19 /// 登录密码 20 /// </summary> 21 public string Password { get; set; } 22 23 /// <summary> 24 /// 提示信息 25 /// </summary> 26 public string Message { get; set; } 27 } 28 }
然后创建AccountController,并定义登录页面的Action如下:
1 public ViewResult Login() 2 { 3 var model = new S.Framework.WebClient.Models.LoginModel(); 4 5 return View(model); 6 }
右键Login这个Action创建视图,选择不需要布局页。登录界面不是重点,此处就不贴页面代码一笔带过了。
在控制器中还需定义“登录操作”的Action,逻辑暂时比较简单,直接上代码:
1 [ValidateAntiForgeryToken] 2 [HttpPost] 3 public ActionResult Login(S.Framework.WebClient.Models.LoginModel model) 4 { 5 var db = new MasterEntityContext("matrixkey"); 6 var entity = db.Users.Where(w => w.UserName == model.UserName).FirstOrDefault(); 7 if (entity == null) 8 { 9 model.Message = "用户名不存在"; 10 } 11 else 12 { 13 if (entity.Password != model.Password) 14 { 15 model.Message = "密码输入不正确"; 16 } 17 else 18 { 19 return RedirectToAction("Index", "Home", new { }); 20 } 21 } 22 23 return View(model); 24 }
这里注意2个特性的作用。ValidateAntiForgeryToken是用来阻止伪造的登录请求的,需要视图中有相应信息配合使用。HttpPost是用于区别“登录页面的Login和登录操作的Login”,需要视图中表单的提交方式也是Post。顺带一句,控制器中的Action是不能重载的,但可以利用表示HTTP方法的特性加以区分。
身份验证的功能,下面单独拎出来讲,先跑通密码验证的逻辑。
数据模型、登录页面、登录操作,都已经完成,运行一下效果,用户admin,密码123456,成功跳转到首页。
说明一下,安全身份验证的功能将放在后面再讲。
因为要演示封装架构的过程,而现在连数据仓储层和业务层都没有创建,不方便单独写身份验证部分的代码。
下一章节,将演示EF数据仓储的实现。
以上是关于七色花基本权限系统 - 初步使用EntityFramework实现用户登录的主要内容,如果未能解决你的问题,请参考以下文章