MVC+EF6教程一:使用EF初始化数据库 文章提纲 概述 核心概念介绍 建立一个基本框架的详细步骤

第1课 创建MVC项目,使用EF初始化数据库

  • 概述
  • 核心概念介绍
  • 从空白开始,建立一个基本框架详细步骤

概述

  1. 本系列文章及文章中的例子主要基于微软官方文档
  2. 使用工具 : VS2019 + SQL Server 2019
  3. 开始主要讲解MVC + EF搭配使用

核心概念介绍

MVC,Model – View – Controller 的简写

Model 封装业务逻辑相关的数据及对数据的处理方法

View 向用户提供交互界面

Controller 负责控制Model和View

MVC+EF6教程一:使用EF初始化数据库
文章提纲
概述
核心概念介绍
建立一个基本框架的详细步骤

Entity Framework(EF)是一个对象关系映射器,它让.Net开发人员可以使用领域对象来操作关系数据,并帮助开发人员省略大部分访问数据的代码。
在介绍如何使用EF之前,先了解一下EF的几种工作流,它们如下图所示:

  MVC+EF6教程一:使用EF初始化数据库
文章提纲
概述
核心概念介绍
建立一个基本框架的详细步骤

  ● 模型优先(创建一个新的数据库):
    ○ 在设计器中创建模型。
    ○ 通过模型创建数据库。
    ○ 通过模型自动生成实体类。

  ● 数据库优先(使用已存在的数据库):
    ○ 在设计器中反向工程模型。
    ○ 通过模型自动生成实体类。

  ● 代码优先(新数据库):
    ○ 在代码中定义实体类和映射(如类和表的映射、属性和表列的映射)。
    ○ 通过模型创建数据库。
    ○ 使用Migrations去更新数据库(结构)。

  ● 代码优先(使用已存在数据库):
    ○ 在代码中定义实体类和映射(如类和表的映射、属性和表列的映射)。
    ○ 或者使用反向工程生成实体类和映射。

建立一个基本框架的详细步骤

1.新建项目

MVC+EF6教程一:使用EF初始化数据库
文章提纲
概述
核心概念介绍
建立一个基本框架的详细步骤

 打开Global.asax, 我们发现程序启动的时候注册了路由规则:

 public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
//  程序启动的时候注册了路由规则 RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } }

打开RouteConfig.cs文件,注意到里面有个静态方法,这就是映射路由的控制,这个方法定义了路由规则,其中:url: "{controller}/{action}/{id}"定义了URL的格式:

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }

 2.创建Controller

 右键Controllers文件夹,这里先建一个AccountController,添加后会发现多了下图方框处的类和文件夹

MVC+EF6教程一:使用EF初始化数据库
文章提纲
概述
核心概念介绍
建立一个基本框架的详细步骤

 打开新建的AccountController.cs看下,自动生成了一个方法:

 public ActionResult Index()
  {
    return View();
  }

3.创建View

     添加View有两种方法,一种是直接在Views文件夹下添加;另外一种是通过Controller中的Action来添加,打开AccountController, 右键Index方法,添加视图;这样就添加了一个和特定的Controller和Action(这里指AccountController和Index)相对应的View(ViewsàAccountàIndex.cshtml)。

    按照上面的方法,我们创建用户注册/登录的两个控制器,对应生成对应的View:

        /// <summary>
        /// 登陆页面
        /// </summary>
        /// <returns></returns>
        public ActionResult Login()
        {
            return View();
        }
        /// <summary>
        /// "HttpPost":这个Action只会接受http post请求
        /// </summary>
        /// <param name="fc"></param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult Login(FormCollection fc)
        {
            return View();
        }
        /// <summary>
        /// 注册页面
        /// </summary>
        /// <returns></returns>
        public ActionResult Register()
        {
            return View();
        }

新添加的Action中增加了一个[HttpPost] ,表示这个Action只会接受http post请求。ASP.NET MVC提供了Action Method Selector, HttpPost就是其中之一。HttpPost属性典型的应用场景:

涉及到需要接受客户端窗口数据的时候,创建一个用于接收HTTP Get请求的Action, 用于显示界面, 提供给用户填写数据;

另一个同名Action则应用[HttpPost]属性,用于接收用户发来的数据,完成对应的功能。

 4.创建相关类 (Data Model)

 Models文件夹里面存放对应于数据库表的实体,为了更加贴近真实情况,我们针对用户建立三个相关的类(SysUser, SysRole, SysUserRole),用户表和角色表是多对多的关系:

MVC+EF6教程一:使用EF初始化数据库
文章提纲
概述
核心概念介绍
建立一个基本框架的详细步骤

用户表实体SysUser

    /// <summary>
    /// 用户信息
    /// </summary>
    public class SysUser
    {
        /// <summary>
        /// 用户ID
        /// </summary>
        public int ID { get; set; }
        /// <summary>
        /// 用户名
        /// </summary>
        public string UserName { get; set; }
        /// <summary>
        /// 密码
        /// </summary>
        public string Password { get; set; }
        /// <summary>
        /// Email
        /// </summary>
        public string Email { get; set; }
        public virtual ICollection<SysUserRole> SysUserRoles { get; set; }
    }

  角色表实体SysRole

    /// <summary>
    /// 用户角色
    /// </summary>
    public class SysRole
    {
        /// <summary>
        /// 主键ID
        /// </summary>
        public int ID { get; set; }
        /// <summary>
        /// 角色名称
        /// </summary>
        public string RoleName { get; set; }
        /// <summary>
        /// 角色描述
        /// </summary>
        public string RoleDesc { get; set; }
        public virtual ICollection<SysUserRole> SysUserRoles { get; set; }
    }

  用户角色关系表实体SysUserRole

    /// <summary>
    /// 用户与角色关系
    /// </summary>
    public class SysUserRole
    {
        /// <summary>
        /// 主键ID
        /// </summary>
        public int ID { get; set; }
        /// <summary>
        /// 用户ID
        /// </summary>
        public int SysUserID { get; set; }
        /// <summary>
        /// 角色ID
        /// </summary>
        public int SysRoleID { get; set; }
        public virtual SysUser SysUser { get; set; }
        public virtual SysRole SysRole { get; set; }
    }

对于上面几个类的约定和说明:

  1. EF生成数据库时,ID 属性将会成为主键。(约定:EF默认会将ID或classnameID生成主键, MSDN建议保持风格的一致性, 都用ID或classnameID, 我们这里都用ID)
  2. EF 生成数据库时 , <navigation property name><primary key property name>这种形式的会成为外键. ( 约定 )

    例如外键 SysUserID = SysUser(navigation property)+ID(SysUser的主键)

  3. 定义为virtual的几个属性是 navigation 属性(virtual非必须, 只是惯例用法, 后面文章将会讲解用virtual的好处).

    navigation 属性保存着其他的关联entity(entities)

    示例中, SysUser和SysUserRole是一对多的关系, SysRole和SysUserRole也是一对多的关系.

    如果是 "多", 属性类型就必须是list( 这里用的是Icollection )

5.创建Database Context

 前置条件:安装EF

打开 工具》库程序包管理器》程序包管理器控制台,输入 install-package entityframework。EF的架构图如下:

MVC+EF6教程一:使用EF初始化数据库
文章提纲
概述
核心概念介绍
建立一个基本框架的详细步骤

 从上图可以看出,EF框架在底层是通过调用ADO.NET来实现数据库操作的。

在DAL文件夹下创建类 AccountContext.cs , 让他继承自System.Data.Entity.DbContext, 我们用这个类完成EF的功能。

主要做下面三件事:

  1. 为每个entity set创建一个DbSet

    在EF中,通常情况下一个entity set对应数据库中的一张表,一个entity对应表中的一行。

  2. 指定一个连接字符串

    构造函数中的 base("AccountContext") 。

    默认情况下和类名一样,即AccountContext,我们显式的给他指定出来。

  3. 指定单数形式的表名

    默认情况下会生成复数形式的表,如SysUsers

    表名用单复数形式看各自的习惯,没有明确的规定。有的公司表名全用单数,有的公司根据表的意思,有单数也有复数。

    /// <summary>
    /// 在EF中,通常情况下一个entity set对应数据库中的一张表,一个entity对应表中的一行
    /// </summary>
    public class AccountContext : DbContext
    {
        public AccountContext() : base("AccountContext")
        {
            //指定一个连接字符串,默认情况下和类名一样,即AccountContext,我们显式的给他指定出来
        }
        public DbSet<SysUser> SysUsers { get; set; }
        public DbSet<SysRole> SysRoles { get; set; }
        public DbSet<SysUserRole> SysUserRoles { get; set; }
        /// <summary>
        /// 默认情况下会生成复数形式的表,如SysUsers,我们这里指定单数形式的表名,如SysUser
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }

  配合上面第2点,先把web.config中连接字符串给指定了:

  <connectionStrings>
    <!--AttachDBFilename=|DataDirectory|MVCDemo.mdf设定了数据库文件的存放位置:在项目根目录的App_Data文件夹下-->
    <add name="AccountContext" connectionString="Data Source=.;database=MvcDemo;uid=sa;pwd=123456;AttachDBFilename=|DataDirectory|MvcDemo.mdf;" 
providerName="System.Data.SqlClient"/> </connectionStrings>

5.创建Initializer,使用EF初始化数据库,插入示例数据

EF可以以多种方式建立数据库。

我们采用如下方式:

第一次运行程序时新建数据库,插入测试数据; model改变(和database不一致)时删除重建数据库,插入测试数据。

目前在开发阶段,不用管数据丢失的问题,直接drop and re-create比较方便。

等系列文章结束后会讲解生产环境中如何不丢失数据修改schema

下面我们就新建类AccountInitializer.cs来完成这个工作。

 public class AccountInitializer : DropCreateDatabaseIfModelChanges<AccountContext>
    {
        protected override void Seed(AccountContext context)
        {
            /**
             第一次运行程序时新建数据库,插入测试数据; model改变(和database不一致)时删除重建数据库,插入测试数据。
             目前在开发阶段,不用管数据丢失的问题,直接drop and re - create比较方便。
            */
            var sysUsers = new List<SysUser>
          {
              new SysUser{UserName="张三",Password="1",Email="666666@qq.com"},
              new SysUser{UserName="李四",Password="2",Email="888888@qq.com"}
          };
            sysUsers.ForEach(s => context.SysUsers.Add(s));
            context.SaveChanges();


            var sysRoles = new List<SysRole>
          {
              new SysRole{RoleName="管理员",RoleDesc="具有所有权限"},
              new SysRole{RoleName="一般用户",RoleDesc="只有部分权限"}
          };
            sysRoles.ForEach(s => context.SysRoles.Add(s));
            context.SaveChanges();
        }
    }

Seed方法用我们之前定义的database context(即AccountContext) 作为参数,通过这个context将entities添加到database中去。(就是我们前面说的桥梁作用)

从上面代码可以看出, Seed方法对每一个entity的类型(我们用了SysUser和SysRole, SysUserRole我们暂不添加):

创建一个colletion 》 添加到适当的 DbSet property 》保存到数据库。

修改web.config, 找到entityFramework配置节,添加context节点,配置刚刚写好的initializer类,context 配置节中, type 的值对应 (context class的完整描述,程序集),databaseInitializer 配置节中 , type 的值对应 (initializer class 的完整描述,程序集), 如果你不想EF使用某个context, 可以将下面方框处设置为true。

  <entityFramework>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
    <contexts>
      <!--type 的值对应 (context class的完整描述,程序集),如果你不想EF使用某个context, 可以将disableDatabaseInitialization设置为true-->
      <context type="MvcWebDemo.DAL.AccountContext,MvcWebDemo" >
        <!--type 的值对应 (initializer class 的完整描述,程序集)-->
        <databaseInitializer type="MvcWebDemo.DAL.AccountInitializer,MvcWebDemo"></databaseInitializer>
      </context>
    </contexts>
  </entityFramework>

现在EF一切就绪,运行程序,当第一次连接数据库时,EF比较model(AccountContext和entity classes) 和database. 如果两边不一致,程序将会drop and re-create数据库。

检查一下数据库部分是否符合我们的预期:

打开数据库,发现MVCDemo这个数据库已经新建,示例数据已经插入

MVC+EF6教程一:使用EF初始化数据库
文章提纲
概述
核心概念介绍
建立一个基本框架的详细步骤MVC+EF6教程一:使用EF初始化数据库
文章提纲
概述
核心概念介绍
建立一个基本框架的详细步骤

打开项目的App_Data 文件夹,发现数据库文件已经存在

MVC+EF6教程一:使用EF初始化数据库
文章提纲
概述
核心概念介绍
建立一个基本框架的详细步骤

6.前台Login页面如下:

<body>
    <div >
        @ViewBag.LoginState
    </div>
    @*<form action="~/Account/Login" method="post" class="form-horizontal">*@
    @using (Html.BeginForm("Login", "Account", FormMethod.Post))
    {
        <form class="form-horizontal">
            <div class="form-group">
                <label for="email" class="col-sm-2 control-label">Email</label>
                <div class="col-sm-10">
                    <input type="email" class="form-control" >
                </div>
            </div>
            <div class="form-group">
                <label for="pwd" class="col-sm-2 control-label">Password</label>
                <div class="col-sm-10">
                    <input type="password" class="form-control" >
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-10">
                    <div class="checkbox">
                        <label>
                            <input type="checkbox"> Remember me
                        </label>
                    </div>
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-10">
                    <button type="submit" class="btn btn-default">Sign in</button>
                </div>
            </div>
        </form>
    }
</body>  

form的action和method的位置可以写成固定的,这样的话部署发生变化时有可能地址会不可用(如放在IIS根目录下和虚拟目录下是不同的)

<form action="~/Account/Login" method="post" class="form-horizontal">

我们对Login.cshtml中的form做一点改良,使用HtmlHelper动态计算路由地址就是其中的一种方法。

添加下面一句代码,将form中内容放到 {} 中去即可

@using (Html.BeginForm("login", "Account", FormMethod.Post)) { }

运行,到浏览器中查看源代码,可以看到生成的源代码和原来一样。

权责申明

作者:编程小纸条 出处: https://www.cnblogs.com/miro/category/620362.html