asp.net core实践笔记:WebAPI(EF+Pomelo+MySQL)Code-First Repository模式

前言

活到老,学到老。

需要用到asp.net core写个小工具,n+n年前曾经用过asp写过一个小网站,但,asp.net core跟asp只是名字相似罢了。

前段时间学习了一下,在此写些文字分享一下,权当是笔记吧,希望能做到温故而知新。感觉知识点还是蛮多的,好在之前有学习过C#和WPF,对学习asp.net core还是非常有帮助的。最终目的是做一个前后端分离的小工具站点并用Docker部署,不知晓最后能不能完成目标,也无法估计需要多长时间,且学且做了。

网上一搜,相关的信息太多了,但说到要找到最适合自己的教程资料却也殊为不易,因为乱花迷人眼啊,这就需要看运气和个人的学习习惯了。分享下鄙人主要的学习资料来源:

  • https://dotnettutorials.net/lesson/introduction-to-asp-net-core/ 网站
    在这里插入图片描述
    个人觉得此网站上的asp.net core教程写得比较通俗易懂,非常适合快速入门。注意有些页面(章节)会有重复的内容。

  • 《Pro ASP.NET Core 3 – Eighth Edition》
    在这里插入图片描述
    叙述详细,文字比较多,需要耐心和时间才能看下去。目前只看了前面三部分,第四部分(高级功能)有需要时再参考。基本上按书的例程做了一遍(除了单元测试的部分),感觉上:有些懂了、有些似懂非懂、有些还没有懂。

/*********************************** 分界线 ***********************************/

开发环境

这是一个后端WebAPI的建立过程笔记,环境如下:

VisualStudio 2019
{
.NET Core 3.1

Microsoft.EntityFrameworkCore(5.0.13)
Microsoft.EntityFrameworkCore.Tools(5.0.13)
Pomelo.EntityFrameworkCore.MySql(5.0.4)
}

MySQL管理器:HeidiSQL,Ver:11.3
MySQL:不折腾,直接采用小皮面板(PHPStudy),Ver:5.7.26;
在这里插入图片描述

建立WebAPI项目

项目创建

从零开始,创建一个名为:MyFirstWebAPI的Project,比较简单,参考下列截图:
在这里插入图片描述
在这里插入图片描述
确保Target Framework为 .NET Core 3.1,Authentication Type为None,不启用HTTPS和Docker。
在这里插入图片描述

安装依赖包

通过NuGet Package Manager安装以下依赖包(注意版本!):
Microsoft.EntityFrameworkCore(5.0.13)
Microsoft.EntityFrameworkCore.Tools(5.0.13)
Pomelo.EntityFrameworkCore.MySql(5.0.4)

同样比较简单,参考下列截图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码构建步骤

基本上,建立一个EntityFramework+Pomelo+MySQL的WebAPI,大概有11个步骤(位置)需要添加/修改相应代码,详细参考下图(图中的第一步已在上面【安装依赖包】小节完成):
在这里插入图片描述

1. 添加依赖包(参考:【安装依赖包】小节)

2. 建立POCO实体类(Soldier.cs)

using System.ComponentModel.DataAnnotations;

namespace MyFirstWebAPI.Models
{
	/// <summary>
	/// 定义Soldier实体类
	/// </summary>
	public class Soldier
	{
		/// <summary>
		///  标识主键
		/// </summary>
		[Key]
		public int Id { get; set; }

		public string FirstName { get; set; }

		public string LastName { get; set; }

		public string Number { get; set; }
	}
}

3. 建立DataContext类(DataContext.cs)

using Microsoft.EntityFrameworkCore;

namespace MyFirstWebAPI.Models
{
	public class DataContext : DbContext
	{
		public DataContext(DbContextOptions<DataContext> opts)
		: base(opts) { }

		/// <summary>
		/// 取得Gang7Lian数据表
		/// </summary>
		public DbSet<Soldier> Gang7Lian { get; set; }
	}
}

4. 建立Repository接口(ISoldierRepository.cs 注意:是接口!)

using System.Collections.Generic;
using System.Threading.Tasks;

namespace MyFirstWebAPI.Models
{
	/// <summary>
	/// 定义ISoldierRepository接口
	/// </summary>
	public interface ISoldierRepository
	{
		// 定义GetAll方法
		Task<List<Soldier>> GetAll();
	}
}

5. 建立Repository类以实现ISoldierRepository接口(SoldierRepository.cs)

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace MyFirstWebAPI.Models
{
	/// <summary>
	/// SoldierRepository数据仓库
	/// (实现ISoldierRepository接口)
	/// </summary>
	public class SoldierRepository : ISoldierRepository
	{
		private DataContext context;

		/// <summary>
		/// 注入DataContext服务
		/// </summary>
		/// <param name="ctx"></param>
		public SoldierRepository(DataContext ctx)
		{
			context = ctx;
		}

		/// <summary>
		/// GetAll方法实现
		/// </summary>
		/// <returns>List<Soldier></returns>
		public async Task<List<Soldier>> GetAll()
		{
			// throw new NotImplementedException();

			List<Soldier> soldiers = await context.Gang7Lian.ToListAsync<Soldier>(); 

			return soldiers;
		}
	}
}

6. 建立SeedingData类以填充测试数据(SeedingData.cs)

using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;

namespace MyFirstWebAPI.Models
{
	/// <summary>
	/// 定义SeedingData类
	/// 向数据库自动填充初始数据
	/// </summary>
	public static class SeedingData
	{
		/// <summary>
		/// Gang7Lian数据表初始数据填充
		/// </summary>
		/// <param name="app">IApplicationBuilder对象</param>
		public static async void EnsureThe7thCompanyPopulated(IApplicationBuilder app)
		{
			// 取得DataContext服务
			DataContext dataContext = app.ApplicationServices
			.CreateScope().ServiceProvider.GetRequiredService<DataContext>();

			// 如果存在Pending的Migration操作则自动执行
			// 在开发期间,我们有时会Reset数据库
			// 这三行代码会自动Rebuild数据库和表,很是方便
			if (dataContext.Database.GetPendingMigrations().Any())
			{
				dataContext.Database.Migrate();
			}

			// 如果表格为空(没有数据),则自动填入初始数据
			if (!dataContext.Gang7Lian.Any())
			{
				dataContext.Gang7Lian.AddRange(
					new Soldier
					{
						FirstName = "生",
						LastName = "梅",
						Number = "135-183108"
					},
					new Soldier
					{
					FirstName = "从戎",
						LastName = "余",
						Number = "221-183108"
					},					
					new Soldier
					 {
						 FirstName = "河",
						 LastName = "平",
						 Number = "280-183108"
					 },
					new Soldier
					{
						FirstName = "长贵",
						LastName = "何",
						Number = "335-183108"
					},
					new Soldier
					{
						FirstName = "持正",
						LastName = "李",
						Number = "572-183108"
					}, 
					new Soldier
					{
						FirstName = "志毅",
						LastName = "刘",
						Number = "657-183108"
					},
					new Soldier
					{
						FirstName = "小山",
						LastName = "张",
						Number = "176-183108"
					},
					new Soldier
					{
						FirstName = "龙",
						LastName = "巴",
						Number = "533-183108"
					},
					new Soldier
					{
						FirstName = "强",
						LastName = "宣",
						Number = "565-183108"
					},
					new Soldier
					{
						FirstName = "千里",
						LastName = "伍",
						Number = "162-183108"
					},
					new Soldier
					{
						FirstName = "百里",
						LastName = "伍",
						Number = "161-183108"
					},
					new Soldier
					{
						FirstName = "睢生",
						LastName = "雷",
						Number = "017-183108-81192"
					},
					new Soldier
					{
						FirstName = "万里",
						LastName = "伍",
						Number = "677-183108"
					},
					new Soldier
					{
						FirstName = "子为",
						LastName = "谈",
						Number = "160-183108"
					}
					);

				await dataContext.SaveChangesAsync();
			}
		}
	}
}

7. 建立Gang7Lian控制器类(Gang7LianController.cs 使用API Controller – Empty模板)

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using MyFirstWebAPI.Models;

namespace MyFirstWebAPI.Controllers
{
	[Route("api/[controller]")]
	[ApiController]
	public class Gang7LianController : ControllerBase
	{
		private ISoldierRepository _soldierRepository;

		/// <summary>
		/// 注入SoldierRepository服务
		/// </summary>
		/// <param name="soldierRepository"></param>
		public Gang7LianController(ISoldierRepository soldierRepository)
		{
			_soldierRepository = soldierRepository;
		}

		[HttpGet]
		public async Task<List<Soldier>> Get()
		{
			List<Soldier> gang7Lian = await _soldierRepository.GetAll();

			return gang7Lian;
		}
	}
}

8. 修改appsettings.json以添加MySQL连接字符串

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "server=localhost; port=3306; database=CPV; user=root; password=root; Persist Security Info=False; Connect Timeout=300"
  }
}

9. 修改Startup.cs以完成服务配置及调用SeedingData填充初始数据

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MyFirstWebAPI.Models;

namespace MyFirstWebAPI
{
	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)
		{
			// 获取MySQL连接字符串
			string mySqlConnectionStr = Configuration.GetConnectionString("DefaultConnection");
			// 使用AddDbContextPool添加MySQL服务
			services.AddDbContextPool<DataContext>(options => options.UseMySql(mySqlConnectionStr, ServerVersion.AutoDetect(mySqlConnectionStr)));

			// 使用AddTransient添加SoldierRepository服务
			services.AddTransient<ISoldierRepository, SoldierRepository>();

			services.AddControllers();
		}

		// 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();
			}

			app.UseRouting();

			app.UseAuthorization();

			app.UseEndpoints(endpoints =>
			{
				endpoints.MapControllers();
			});

			// 调用SeedingData确保数据初始化
			SeedingData.EnsureThe7thCompanyPopulated(app);
		}
	}
}

10. 在Package Manager Console中运行建立命令:Add-Migration DBInit以创建初始EF Migration

PM> Add-Migration DBInit

11. 在Package Manager Console中运行建立命令:Update-Database执行更新

PM> Update-Database

运行程序

运行程序并访问地址:http://localhost:28193/api/Gang7Lian(注意路径为: /api/[控制器名称])
在这里插入图片描述
在HeidiSQL管理器中可看到自动建立的数据库和表
在这里插入图片描述

后记

注意:文及代码中的说明和注解均为根据个人理解描述,如有疑问,请自行Google/Baidu查证,以免被误导!

MySQL Provider其实有两个:Pomelo.EntityFrameworkCore.MySql(开源)和MySql.Data.EntityFrameworkCore(官方),可参考:
https://stackoverflow.com/questions/48703318/mysql-data-entityframeworkcore-vs-pomelo-entityframeworkcore-mysql

AddDbContext和AddDbContextPool的区别,可参考:
https://stackoverflow.com/questions/48443567/adddbcontext-or-adddbcontextpool

如需要更高的性能,可考虑使用Dapper,可参考:
https://www.nuget.org/packages/Dapper
https://github.com/anzolin/AspNetCoreDapperMySql

可考虑使用JWT(JSON Web Token)认证以增强API的安全性,可参考:
https://jasonwatmore.com/post/2019/10/11/aspnet-core-3-jwt-authentication-tutorial-with-example-api


版权声明:本文为Sunny_Clickman原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。