七色花基本权限系统 - 初步使用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 }
SysUser.cs

请一定注意实体类的命名空间。

为区分各实体类的功能,可以套一个文件夹进行分类,比如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 
MasterEntityContext.cs

数据库上下文创建之后,还需要关联数据实体类,这样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 }
通过EF操作数据库

编译生成,运行。如果成功打开/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 }
登录页面Action

右键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 }
登录操作Action

这里注意2个特性的作用。ValidateAntiForgeryToken是用来阻止伪造的登录请求的,需要视图中有相应信息配合使用。HttpPost是用于区别“登录页面的Login和登录操作的Login”,需要视图中表单的提交方式也是Post。顺带一句,控制器中的Action是不能重载的,但可以利用表示HTTP方法的特性加以区分。

身份验证的功能,下面单独拎出来讲,先跑通密码验证的逻辑。

数据模型、登录页面、登录操作,都已经完成,运行一下效果,用户admin,密码123456,成功跳转到首页。

 

说明一下,安全身份验证的功能将放在后面再讲。

因为要演示封装架构的过程,而现在连数据仓储层和业务层都没有创建,不方便单独写身份验证部分的代码。

 

下一章节,将演示EF数据仓储的实现。

以上是关于七色花基本权限系统 - 初步使用EntityFramework实现用户登录的主要内容,如果未能解决你的问题,请参考以下文章

七色花基本权限系统(10)- 数据接口的实现

七色花基本权限系统(13)- 业务层的设计和实现

七色花基本权限系统- 利用EasyUI进行首页布局

七色花基本权限系统- 实现实体层和核心层解耦

七色花基本权限系统- 让EntityFramework Code First自动合并/迁移/数据初始化

OA(权限管理初步模块)