zoukankan      html  css  js  c++  java
  • MVC+EF6教程一:使用EF初始化数据库

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

    文章提纲

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

    概述

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

    核心概念介绍

    MVC,Model – View – Controller 的简写

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

    View 向用户提供交互界面

    Controller 负责控制Model和View

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

      

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

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

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

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

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

    1.新建项目

     打开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,添加后会发现多了下图方框处的类和文件夹

     打开新建的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),用户表和角色表是多对多的关系:

    用户表实体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的架构图如下:

     从上图可以看出,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这个数据库已经新建,示例数据已经插入

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

    6.前台Login页面如下:

    <body>
        <div id="loginState">
            @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" id="email" name="email" placeholder="Email">
                    </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" id="pwd" name="pwd" placeholder="Password">
                    </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

  • 相关阅读:
    Ubuntu 虚拟机空间不足增加空间笔记
    am335x sd卡启动开启识别emmc kernel 上的改动
    Delphi实现树型结构具体实例
    学习 TTreeView [15]
    delphi中Treeview的使用介绍
    按下F2编辑dxDBTreeView的节点
    dbtreeview
    我的dbtreeview–treeview直接连接数据表_delphi教程
    SqlDbx连接oracle(可用)
    SqlDbx连接oracle(无需安装Oracle客户端)
  • 原文地址:https://www.cnblogs.com/qianj/p/12493510.html
Copyright © 2011-2022 走看看