zoukankan      html  css  js  c++  java
  • 七色花基本权限系统(4) 初步使用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等。如下图:

    image

    请一定注意实体类的命名空间,命名空间的最后一级必须是“数据库标识”。

    这里多讲一句,不要在实体类中增加“数据库映射”相关的特性。因为数据实体和数据核心应该是解耦的,作为数据实体,它不关心使用自己的数据驱动是EF还是dapper,那么就不该在它的身上体现任何“站队伍”的痕迹。

    数据核心

    数据核心应该能够支持多种数据驱动。这里先演示EF的使用。

    新建类库项目,名称S.Framework.DataCore,然后从nuget上先安装EntityFramework。

    创建结构如下图:

    image

    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数据。

    image

    image

    __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实体配置的使用和利用T4模板自动生成实体配置。

    截止本章节,项目源码下载:点击下载(存在百度云盘中)

  • 相关阅读:
    国际域名转出ICANN投诉
    C#中使用SslStream类来创建SSL服务器/客户端
    将.com域名转到godaddy的操作教程
    Google Test Automation Conference 2013 Schedule
    3月收藏
    4月收藏
    2月收藏
    5月收集
    stl中queues的基本用法
    codeblocks花屏终极解决方法
  • 原文地址:https://www.cnblogs.com/matrixkey/p/5557995.html
Copyright © 2011-2022 走看看