Asp.Net Core MVC + Code First + Mysql 项目创建以及相关配置

伴随着.net core 技术的成熟,使用.net 开发的项目可以轻松实现跨平台。今天记录一下 Asp.Net Core MVC + Code First + MySql 的用法,开发工具依然是 Visual Studio(此项目用的是Visual Studio 2019).

1. 创建新项目,选择Asp.Net Core MVC 模板

 

2. 填写项目名称和解决方案名称,如下图所示

 

 

 解决方案名称与项目名称最好有所区别,因为下面还要在该解决方案下添加别的项目(实体类项目以及数据仓促项目等),填写完成后点击下一步,如下图所示

 

 

 

3. 创建项目

  点击创建,Visual Studio开始创建项目。如下图所示

 

 

 

4. 添加实体类项目(CoreMvc.Entity)

  添加类库项目是为了存放数据实体类,用来生成数据库中对应的数据表。 右击解决方案选择 添加->新项目,如下图所示

 

 

 在弹出的添加界面中选择类库项目,如下图所示

 

 

 点击下一步,输入项目名称(CoreMvc.Entity),如下图所示

 

 

 点击下一步,选择合适的目标框架(和CoreMvc.Web项目的框架保持一致),如下图所示

 

 

 点击创建,实体类项目创建成功!如下图所示

 

 

 

 5. 添加数据仓储项目(CoreMvc.Repository)

  按照步骤4 添加另外一个类库项目,并命名 CoreMvc.Repository(创建此项目的目的是为了存放数据库上下文文件,以及存放项目迁移的纪录),创建好后如下图所示

 

 

 6.  CoreMvc.Entity项目下添加实体类

  在 CoreMvc.Enity项目下 添加两个实体类(DepartMent 和 User)以及一个基础类(EntityBase)代码如下

using System;
using System.ComponentModel.DataAnnotations;

namespace CoreMvc.Entity
{
    /// <summary>
    /// 数据表基类(所有的表都应该继承该表)
    /// </summary>
    public class EntityBase
    {
        /// <summary>
        /// 编号(每张表都应该有ID,并且自动生成,此处使用Guid作为主键)
        /// </summary>
        [Key]
        public Guid Id { get; set; } = Guid.NewGuid();

        /// <summary>
        /// 名称
        /// </summary>
        [Required(ErrorMessage = "名称是必填项")]
        public string Name { get; set; }

        /// <summary>
        /// 创建时间(默认当前时间)
        /// </summary>
        public DateTime CreateTime { get; set; } = DateTime.Now;

        /// <summary>
        /// 修改时间(默认当前时间)
        /// </summary>
        public DateTime UpdateTime { get; set; } = DateTime.Now;
    }
}
namespace CoreMvc.Entity
{
    /// <summary>
    /// 部门表
    /// </summary>
    public class Department:EntityBase
    {
        /// <summary>
        /// 部门描述信息
        /// </summary>
        public string Desc { get; set; }
        /// <summary>
        /// 部门领导
        /// </summary>
        public string DepartmentLeader { get; set; }
        /// <summary>
        /// 部门领导电话
        /// </summary>
        public string LeaderPhone { get; set; }
    }
}
using System;
using System.ComponentModel.DataAnnotations.Schema;

namespace CoreMvc.Entity
{
    /// <summary>
    /// 用户表
    /// </summary>
    public class User:EntityBase
    {
        /// <summary>
        /// 年龄
        /// </summary>
        public int Age { get; set; }
        /// <summary>
        /// 性别
        /// </summary>
        public int Sex { get; set; }
        /// <summary>
        /// 住址
        /// </summary>
        public string Address { get; set; }
        /// <summary>
        /// 部门编号(此字段是外键,对应Department表中的Id,对应关系由下面的字段产生)
        /// </summary>
        public Guid DepartmentId { get; set; }
        /// <summary>
        /// 所属部门(在生成的数据库中此字段不显示,只显示DepartmentId,其中ForeignKey指向的是上面定义好的DepartmentId字段)
        /// </summary>
        [ForeignKey("DepartmentId")]
        public Department Department { get; set; }
    }
}

7. 在 CoreMvc.Repository项目里 添加NuGet包引用

  右击CoreMvc.Repository项目,选择 “管理NuGet程序包”如下图所示

 

 

 在打开的界面中点击“浏览”选项卡,搜索  Microsoft.EntityFrameworkCore,如下图所示

 

 

  

由于创建项目时目标框架选择的是 .Net 5.0,而最新包所依赖的框架是.Net 6.0 因此要降低该包的版本,否则会安装失败,更改包的版本后进行安装,如下图所示

 

 

 点击安装,则会安装成功。以同样的步骤 安装 Pomelo.EntityFrameworkCore.MySql 和 Microsoft.EntityFrameworkCore.Relational两个NuGet包

如果出现下图所示,则表示所依赖的NuGet包已经安装成功

 

 

 此处简单说明一下三个包的作用:

  1. Microsoft.EntityFrameworkCore:Entity Framework Core is a lightweight and extensible version of the popular Entity Framework data access technology,提供了数据上下文和DbSet属性,我们在程序里面就是通过数据上下文和DbSet属性来对数据库里面的数据进行操作。

  2. Microsoft.EntityFrameworkCore.Relational:Shared Entity Framework Core components for relational database provider:关系数据库提供者共享EF Core组件。比如我们要将实体映射到数据库中,都是通过这个包完成的。

  3. Pomelo.EntityFrameworkCore.MySql:对于EF Core的Mysql数据库提供者。假如使用的是Mysql数据库,那么就需要安装这个包。对于其他数据库,EF Core提供的有其相对应的提供程序安装包。 

 8. CoreMvc.Repository项目添加CoreMvc.Entity引用

  由于CoreMvc.Repository项目需要用到CoreMvc.Entity项目的实体类,因此在该项目下需要添加 CoreMvc.Entity项目的引用,如图

 

 在弹出的界面上选择CoreMvc.Entity,如下图所示

 

 点击确定按钮即可。  

 9. CoreMvc.Repository项目添加数据库上下文

  在CoreMvc.Repository项目里 添加MysqlContext实体类,并继承DbContext,代码如下

  

using CoreMvc.Entity;
using Microsoft.EntityFrameworkCore;

namespace CoreMvc.Repository
{
    /// <summary>
    /// 数据上下文
    /// </summary>
    public class MysqlContext : DbContext
    {
        public MysqlContext(DbContextOptions<MysqlContext> options)
            : base(options)
        {

        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
        }

        /// <summary>
        /// 部门表(最终生成到数据库的表名可能会转为小写:departments)
        /// </summary>
        public DbSet<Department> Departments { get; set; }
        /// <summary>
        /// 用户号表(最终生成到数据库的表名可能会转为小写:users)
        /// </summary>
        public DbSet<User> Users { get; set; }
        
    }
}

 

10. CoreMvc.Web 项目下 安装Nuget包

  在 CoreMvc.Web项目下需要安装 Microsoft.EntityFrameworkCore.Tools、Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation两个Nuget包,安装步骤参考步骤7,注意对应包的版本的兼容性,安装成功后如下图所示

 

 

 

 

 简单说明一下各个包的用途:

  1.  Microsoft.EntityFrameworkCore.Tools:Visual Studio中NuGet Package Manager控制台的实体框架核心工具。后续需要需要使用 NuGet Package Manager (程序包管理器控制台)窗口进行数据迁移

  2. Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation:调试时,更改cshtml代码, 刷新浏览器可以更新页面(否则更改了cshtml的代码时,需要重新启动项目,浏览器才可以更新页面)

   需要在Startup.cs 的ConfigureServices方法中添加  services.AddMvc().AddRazorRuntimeCompilation(); 

11.  CoreMvc.Web 项目下配置数据库连接字符串

  打开appsettings.json文件,添加如下代码

  

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MyConnectionStr": "Server=localhost;DataBase=CoreMvcTestDB;uid=root;pwd=root123;pooling=true;port=3306;Charset=utf8;sslMode=None;"
  }
}

其中 “MyConnectionStr” 是连接字符串的名称(可以随意命名) 

  "Server=localhost" 指连接本地数据库(本地需要安装Mysql数据库,安装方法自行百度解决) 

  “DataBase=CoreMvcTestDB” 指要生成的数据库名称(此处做测试使用,实际开发中应该进行有意义的命名)

  "uid=root" 指本地数据库用户名(安装mysql数据库的时候填写的名字)

  “pwd=root123” 指本地mysql数据库密码(安装mysql数据库的时候设置的密码)

  “pooling=true” 指启用连接池(连接池是一些与数据库有关连接的存放地方,当你需要建立某个连接时,如果它已存在与连接池就不需要新建连接了,速度更快,所以建议还是开启)

  "port=3306;"  指数据库端口(mysql数据库默认端口是 3306)

  “Charset=utf8” 指采用utf8编码 (为了防止存取数据库时中文变乱码或“?”)

  "sslMode=None" 使数据库支持SSL连接

12. CoreMvc.Web项目添加项目引用

  由于CoreMvc.Web项目需要用到其它两个项目,所以需要在该项目下添加项目引用。因为CoreMvc.Repository项目本身就引用了CoreMvc.Entity,所以CoreMvc.Web项目里只需要添加CoreMvc.Repository引用即可, 具体添加方法参考步骤8,添加完成后结果如下图

 

 

 

13. 配置相关服务 

  CoreMvc.Web项目下打开Startup.cs文件,添加相关服务配置,最终代码如下

using CoreMvc.Repository;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace CoreMvc.Web
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<MysqlContext>(p => p.UseMySql(Configuration.GetConnectionString("MyConnectionStr"), MySqlServerVersion.LatestSupportedServerVersion));//添加数据库链接
            services.AddMvc().AddRazorRuntimeCompilation(); //调试时,更改cshtml代码, 刷新浏览器可以更新页面内容(需要 引入 Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation 包)
            services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_3_0).AddJsonOptions(opt =>
            {
                opt.JsonSerializerOptions.PropertyNamingPolicy = null;//匹配大小写(否则实体类中的字段传到View中时全部变成了小写,比如:Department中的DepartmentLeader字段,传到View中时变成了 departmentleader)
            });
            services.AddControllersWithViews();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

 14. 数据迁移

 1. 打开“程序包管理器控制台”

 通过 工具-》Nuget包管理器-》程序包管理器控制台 打开“程序包管理器控制台”窗口

 

 

 

 2. 设置启动项目和默认项目

 

 

 设置启动项目:因为“包管理器控制台”依赖于包“Microsoft.EntityFrameworkCore.Tools”,此项目中“Microsoft.EntityFrameworkCore.Tools”安装到了CoreMvc.Web项目下(实际开发中也应该安装到此项目下,这样每次迁移数据时就不需要再更换启动项目),所以启动项目应该设置为CoreMvc.Web

设置默认项目:“包管理器控制台”中的默认项目指数据上下文(DbContext)所在的项目,因为CoreMvc.Repository项目下的MysqlContext继承了DbContext,所以默认项目应该设置为 CoreMvc.Repository。

设置完后,迁移数据时,“包管理器控制台”通过“默认项目”找到“MysqlContext”,然后通过 CoreMvc.Repository 项目下的包“Microsoft.EntityFrameworkCore.Relational”将实体类映射到数据库中。

3. 数据迁移命令

  A. 添加迁移命令

  在“包管理器控制台中”输入命令 (命令格式:“Add-Migration 迁移名称”)

Add-Migration Init

 其中“Add-Migration”是固定格式,表示添加迁移,Init是迁移的名称,可以根据实际需求填写,此处Init的意思是初始化数据,运行完此命令后,在CoreMvc.Repository项目下会有迁移记录文件,如下图所示

 

 

 B. 更新数据库命令

   输入“添加迁移”命令后,只是多了迁移记录文件,数据还没有更新到数据库中(首次迁移的时候数据库还没有生成,此时可以通过‘Remove-Migration’命令撤回刚才的迁移记录),如果要将数据更新到数据库中,需要输入以下命令

Update-Database

  输入完此命令后,如果出现下图所示内容,表示数据迁移成功

 

 

 此时,通过数据库管理工具(Navicat)查看本地数据库,则发现“CoreMvcTestDB”数据库已经生成,如下图所示

 

 

 15. 添加控制器

  为了实现数据的增删改查,我们可以在CoreMvc.Web的Controllers文件夹下添加一个空控制器BaseController,用来存放增删改查的基本代码,其它控制器可以继承这个控制器,这样就可以实现代码的重用了。代码如下

using CoreMvc.Entity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;

namespace CoreMvc.Web.Controllers
{
    public class BaseController<D,T> : Controller
        where D: DbContext
        where T: EntityBase
    {
        /// <summary>
        /// 链接对象
        /// </summary>
        private D _context;
        public BaseController(D context)
        {
            _context = context;
        }

        [HttpGet]
        public virtual JsonResult GetAll(int limit, int offset, string search, string sort, string order, string includes)
        {
            var dbSet = _context.Set<T>().OrderBy(d => d.CreateTime).AsQueryable();
            #region 查询
            if (!string.IsNullOrEmpty(search))
            {
                dbSet = dbSet.Where(d => d.Name.Contains(search));
            }
            #endregion
            #region 排序
            if (!string.IsNullOrEmpty(sort))
            {
                dbSet = dbSet.OrderBy(string.Format("{0} {1},CreateTime asc", sort, order));  // 根据某个字段排序,需要引入包:System.Linq.Dynamic.Core
            }
            #endregion
            #region Includes 是否包含关联表的信息
            if (!string.IsNullOrEmpty(includes))
            {
                includes.Split(',').ToList().ForEach(i =>
                {
                    dbSet = dbSet.Include(i);
                });
            }
            #endregion
            int total = dbSet.Count();
            var rows = dbSet.Skip(offset).Take(limit).AsNoTracking().ToList();//AsNoTracking加上之后,对读取的数据不进行跟踪,UpdateAsync不会报错,不然会报
            return Json(new { total = total, rows = rows });
        }

        public virtual IActionResult Get(Guid Id, string includes)
        {
            var dbSet = _context.Set<T>().AsQueryable();
            //自动Includes
            if (includes != null)
            {
                var sp = includes.Split(',').ToList();
                sp.ForEach(v =>
                {
                    dbSet = dbSet.Include(v).AsQueryable();
                });
            }

            return Ok(dbSet.FirstOrDefault<T>(m => m.Id == Id));
        }

        /// <summary>
        /// 删除数据
        /// </summary>
        /// <param name="datas"></param>
        /// <returns></returns>
        [HttpPost]
        public virtual IActionResult Delete(List<T> datas)
        {
            try
            {
                _context.RemoveRange(datas);
                _context.SaveChanges();
                return Ok(new { status = "success" });
            }
            catch (Exception)
            {
                return Ok(new { status = "error" });
            }
        }

        /// <summary>
        /// 添加或者修改
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        [HttpPost]
        public virtual IActionResult Submit([FromForm] T t)
        {
            if (!ModelState.IsValid) //判断验证是否成功
            {
                string error = "";
                foreach (var key in ModelState.Keys)
                {
                    var modelstate = ModelState[key];
                    if (modelstate.Errors.Any())
                    {
                        error = modelstate.Errors.FirstOrDefault().ErrorMessage;
                    }
                }
                return Ok(new { status = "error" });
            }
            else
            {
                try
                {

                    var entity = _context.Set<T>().Find(t.Id); //根据Id查询数据库中是否存在该记录
                    if (entity == null) //不存在则添加
                    {
                        _context.Add(t);
                    }
                    else
                    { //存在则修改
                        foreach (var mi in t.GetType().GetProperties()) //遍历view中传来的所有字段
                        {
                            var des = entity.GetType().GetProperty(mi.Name);
                            if (des != null)//如果数据库中所查询的记录包含此字段,则进行赋值修改
                            {
                                des.SetValue(entity, mi.GetValue(t));
                            }
                        }
                        _context.Entry<T>(entity).State = EntityState.Modified;//将状态设置为修改状态
                        //_context.Update(t);
                    }
                    _context.SaveChanges(); //保存新增或者修改
                    return Ok(new { status = "success" });
                }
                catch (Exception error)
                {
                    return Ok(new { status = "error" });
                }
            }

        }
    }
}

然后我们再创建一个DepartmentController,让他继承BaseController,代码如下

using CoreMvc.Repository;
using Microsoft.AspNetCore.Mvc;

namespace CoreMvc.Web.Controllers
{
    public class DepartmentController : BaseController<MysqlContext,Entity.Department>
    {
        private MysqlContext _context;
        public DepartmentController(MysqlContext context):base(context)
        {
            _context = context;
        }
        public IActionResult Index()
        {
            return View();
        }
    }
}

数据基本维护的代码完成后,右击Index添加视图,如下图所示,

 

 点击添加按钮,视图添加成功,此时就可以添加测试数据了。

16. 添加测试数据

  该程序数据的显示以及增删改查界面使用的是Bootstrap以及BootstrapTable插件。个人比较喜欢bootstrap3,因为它可以更好地兼容bootstraptable插件,但是由于bootstrap版本的不断升级(目前官方最新版本是5.1.3),所以此处我们采用最新的bootstrap版本。

  为了方便使用Table插件,自己封装了一个table.js。除了这些外还需要toastr.css、toastr.min.js、popup.js等插件,现在不用担心这些,文章最后提供了源码下载(最新版本的bootstrap程序源码和bootstrap3版本的程序源码),大家可以下载参考。

  最终的界面如下图所示

  

 

      

 

 至此,最简单的Asp.Net.Core Mvc + Code First + Mysql 项目已经完成。由于本篇章的主要要点是讲解项目的架构,所以有关Bootstrap以及BootStraptable等相关的知识会在另外的篇章中讲解。现附加源码下载地址,以供参考。

 CoreMvc项目源码链接

CoreMvc项目源码链接(bootstrap3版本)

备注: 源码使用注意事项

1. 要求本地安装了 visual studio 2019,框架版本是Net5.0

2. 要求本地安装了mysql数据库

3. 下载源码到本地并解压(或者通过git直接下载),用Vs2019打开项目

4. 设置CoreMvc.Web为启动项目

5. 修改CoreMvc.Web项目下的appsettings.json文件中数据库用户名和数据库密码(安装mysql时的用户名和密码)

6. 在visual studio中打开程序包管理器控制台,设置默认项目为 CoreMvc.Repository

7. “程序包管理器控制台”运行 Add-Migration Init命令

8. "程序包管理器控制台"运行 Update-Database命令

9. 运行项目

posted @ 2022-01-11 15:48  狼者为王  阅读(1141)  评论(0编辑  收藏  举报