CloudEntity使用手册_02 - applelikai/CloudEntity GitHub Wiki
第02章 CloudEntity使用入门
- 下面的所有步骤都不做详细说明,先照做,不明白的后面章节再说。
开发环境准备
这次以操作MySQL
为例子,使用实体框架CloudEntity
操作MySQL
数据库。
- 安装
MySQL
; - 安装
dotnet sdk
,我在这里使用的是6.0
版本的dotnet sdk
; - 安装
Visual Studio 2022
。
创建数据库,准备控制台工程
- 登录MySQL,并创建一个数据库,我在这里创建的数据库为
pingguo_blog
,连接字符串为Server=localhost;User Id=root;Password=000000;Database=pingguo_blog;
。 - 打开
Visual Studio 2022
,新建一个net6.0
的控制台工程(netcore
, 及其之后的版本都行),新建文件夹如下:
- 添加Nuget包如下:
创建实体类和Mapper类,配置实体映射
- 在
Entities
文件夹下添加实体类如下:
namespace PingGuo.Blog.Test.Entities { /// <summary> /// 角色类 /// </summary> public class Role { /// <summary> /// 角色id /// </summary> public string RoleId { get; set; } /// <summary> /// 角色名称 /// </summary> public string RoleName { get; set; } /// <summary> /// 录入时间 /// </summary> public DateTime? CreatedTime { get; set; } } }
namespace PingGuo.Blog.Test.Entities { /// <summary> /// 用户类 /// </summary> public class User { /// <summary> /// 用户id /// </summary> public string UserId { get; set; } /// <summary> /// 用户名 /// </summary> public string UserName { get; set; } /// <summary> /// 角色id /// </summary> public string RoleId { get; set; } /// <summary> /// 角色 /// </summary> public Role Role { get; set; } /// <summary> /// 录入时间 /// </summary> public DateTime? CreatedTime { get; set; } } }
- 在
Mappers
文件夹下添加Mapper类如下:
using CloudEntity.Mapping; using CloudEntity.Mapping.Common; using PingGuo.Blog.Test.Entities; namespace PingGuo.Blog.Test.Mappers { /// <summary> /// 角色的Mapper类 /// </summary> internal class RoleMapper : TableMapper<Role> { /// <summary> /// 获取映射的Table信息 /// </summary> /// <returns>映射的Table信息</returns> protected override ITableHeader GetHeader() { return base.GetHeader("Sys_Roles"); } /// <summary> /// 设置属性映射 /// </summary> /// <param name="setter">属性映射设置对象</param> protected override void SetColumnMappers(IColumnMapSetter<Role> setter) { setter.Map(r => r.RoleId, ColumnAction.PrimaryAndInsert).Length(36); setter.Map(r => r.RoleName, allowNull: false).Length(25); setter.Map(r => r.CreatedTime, ColumnAction.Default); } } }
using CloudEntity.Mapping; using CloudEntity.Mapping.Common; using PingGuo.Blog.Test.Entities; namespace PingGuo.Blog.Test.Mappers { /// <summary> /// 用户的Mapper类 /// </summary> internal class UserMapper : TableMapper<User> { /// <summary> /// 获取映射的Table信息 /// </summary> /// <returns>映射的Table信息</returns> protected override ITableHeader GetHeader() { // 指定表名 return base.GetHeader("Sys_Users"); } /// <summary> /// 设置属性映射 /// </summary> /// <param name="setter">属性映射设置对象</param> protected override void SetColumnMappers(IColumnMapSetter<User> setter) { setter.Map(u => u.UserId, ColumnAction.PrimaryAndInsert).Length(36); setter.Map(u => u.UserName, allowNull: false).Length(25); setter.Map(u => u.RoleId, ColumnAction.Insert).Length(36); setter.Map(u => u.CreatedTime, ColumnAction.Default); } } }
- 在
Mappers
文件夹下创建一个Mapper容器类:using CloudEntity.Mapping; using CloudEntity.Mapping.Common; namespace PingGuo.Blog.Test.Mappers { /// <summary> /// Mapper对象容器类 /// </summary> internal class InnerMapperContainer : MapperContainerBase { /// <summary> /// 根据实体类型创建对应的Mapper对象 /// </summary> /// <param name="entityType">实体类型</param> /// <returns>对应的Mapper对象</returns> protected override ITableMapper CreateTableMapper(Type entityType) { // Mapper类型全名 // 假设这里的实体类型为:PingGuo.Blog.Test.Entities.Role // 那么如下方法获到的Mapper对象类型则为:PingGuo.Blog.Test.Mappers.RoleMapper string targetNameSpace = entityType.Namespace.Replace("Entities", "Mappers"); string targetMapperTypeName = string.Format("{0}.{1}Mapper", targetNameSpace, entityType.Name); // 创建Mapper对象 return Activator.CreateInstance(Type.GetType(targetMapperTypeName)) as ITableMapper; } } }
到这里,创建实体类,配置映射工作基本完成
编写数据库驱动
这里以操作MySQL为例子,编写用于操作MySQL的数据库驱动。在文件夹MySqlClient
新建如下类:
- 操作
MySQL
数据库的DbHelper
类:MySqlHelper
,具体如下:using CloudEntity.Data; using MySql.Data.MySqlClient; using System.Data; namespace PingGuo.Blog.Test.MySqlClient { /// <summary> /// 操作MySql数据库的DbHelper类 /// </summary> internal class MySqlHelper : DbHelper { /// <summary> /// 获取数据库连接 /// </summary> /// <param name="connectionString">连接字符串</param> /// <returns>数据库连接</returns> /// <exception cref="NotImplementedException"></exception> protected override IDbConnection Connect(string connectionString) { return new MySqlConnection(connectionString); } /// <summary> /// 获取数据适配器 /// </summary> /// <returns>数据适配器</returns> protected override IDbDataAdapter CreateDataAdapter() { return new MySqlDataAdapter(); } /// <summary> /// 记录所有执行的sql命令 /// </summary> /// <param name="commandText">sql命令</param> protected override void RecordCommand(string commandText) { Console.WriteLine("================"); Console.WriteLine(commandText); Console.WriteLine("================"); } /// <summary> /// 初始化(pingguo_blog是通用的表名前缀,相当于Sql Server下的dbo) /// </summary> /// <param name="connectionString">连接字符串</param> public MySqlHelper(string connectionString) : base(connectionString, "pingguo_blog") { } /// <summary> /// 创建sql参数 /// </summary> /// <returns>sql参数</returns> public override IDbDataParameter CreateParameter() { return new MySqlParameter(); } } }
- 用于创建表的Table初始化器类:
using CloudEntity.CommandTrees; using CloudEntity.Core.Data.Entity; using CloudEntity.Data; using CloudEntity.Mapping; using System.Data; using System.Text; namespace PingGuo.Blog.Test.MySqlClient { /// <summary> /// MySql的表初始化类(Code First时使用) /// </summary> internal class MySqlTableInitializer : TableInitializer { /// <summary> /// 初始化 /// </summary> /// <param name="dbHelper">数据库操作对象</param> /// <param name="commandFactory">SQL命令工厂</param> public MySqlTableInitializer(IDbHelper dbHelper, ICommandFactory commandFactory) : base(dbHelper, commandFactory) { } /// <summary> /// 检查表是否存在 /// </summary> /// <param name="dbHelper">操作数据库的DbHelper对象</param> /// <param name="tableHeader">table信息</param> /// <returns>当前table是否存在</returns> public override bool IsExist(ITableHeader tableHeader) { // 获取数据库架构名(表名前缀) string schemaName = tableHeader.SchemaName ?? base.DbHelper.DefaultSchemaName; //获取sql参数数组 IList<IDbDataParameter> parameters = new List<IDbDataParameter> { base.DbHelper.CreateParameter("TableName", tableHeader.TableName) }; //初始化sql命令 StringBuilder commandText = new StringBuilder(); commandText.AppendLine("SELECT COUNT(*)"); commandText.AppendLine(" FROM information_schema.Tables t"); commandText.AppendLine(" WHERE t.Table_Name = @TableName"); //若有架构名则带上架构名 if (!string.IsNullOrEmpty(schemaName)) { commandText.AppendLine(" AND t.Table_Schema = @SchemaName"); parameters.Add(base.DbHelper.CreateParameter("SchemaName", schemaName)); } //执行获取结果 int result = Convert.ToInt32(base.DbHelper.GetScalar(commandText.ToString(), parameters: parameters.ToArray())); return result > 0; } } }
- 用于新增列的Column初始化器类:
using CloudEntity.CommandTrees; using CloudEntity.Core.Data.Entity; using CloudEntity.Data; using CloudEntity.Mapping; using System.Data; using System.Text; namespace PingGuo.Blog.Test.MySqlClient { /// <summary> /// MySql列初始化器(Code First时使用,检查列是否完整,列不完整添加列) /// </summary> internal class MySqlColumnInitializer : ColumnInitializer { /// <summary> /// 获取当前表下所有的列 /// </summary> /// <param name="tableHeader">表的基本信息对象</param> /// <returns>当前表下所有的列</returns> protected override IEnumerable<string> GetColumns(ITableHeader tableHeader) { // 获取数据库架构名(表名前缀) string schemaName = tableHeader.SchemaName ?? base.DbHelper.DefaultSchemaName; //初始化sql参数数组 IList<IDbDataParameter> parameters = new List<IDbDataParameter> { base.DbHelper.CreateParameter("TableName", tableHeader.TableName) }; //初始化sql StringBuilder sqlBuilder = new StringBuilder(); sqlBuilder.AppendLine("SELECT c.COLUMN_NAME"); sqlBuilder.AppendLine(" FROM information_schema.COLUMNS c"); sqlBuilder.AppendLine(" WHERE c.TABLE_NAME = @TableName"); //若有TABLE_SCHEMA查询条件则带上 if (!string.IsNullOrEmpty(schemaName)) { sqlBuilder.AppendLine(" AND c.TABLE_SCHEMA = @SchemaName"); parameters.Add(base.DbHelper.CreateParameter("SchemaName", schemaName)); } //执行查询获取所有列 return base.DbHelper.GetResults(reader => reader.GetString(0), sqlBuilder.ToString(), parameters: parameters.ToArray()); } /// <summary> /// 初始化 /// </summary> /// <param name="dbHelper">数据库操作对象</param> /// <param name="commandFactory">SQL命令工厂</param> public MySqlColumnInitializer(IDbHelper dbHelper, ICommandFactory commandFactory) : base(dbHelper, commandFactory) { } } }
- 用户初始化数据容器的数据库初始化类:
using CloudEntity.CommandTrees; using CloudEntity.CommandTrees.Commom.MySqlClient; using CloudEntity.Core.Data.Entity; using CloudEntity.Data; using CloudEntity.Mapping; using PingGuo.Blog.Test.Mappers; namespace PingGuo.Blog.Test.MySqlClient { /// <summary> /// 访问MySql数据库的初始化器 /// </summary> internal class MySqlInitializer : DbInitializer { /// <summary> /// 创建SQL命令工厂 /// </summary> /// <returns>SQL命令工厂</returns> public override ICommandFactory CreateCommandFactory() { return new MySqlCommandFactory(); } /// <summary> /// 创建Mapper容器 /// </summary> /// <returns>Mapper容器</returns> public override IMapperContainer CreateMapperContainer() { return new InnerMapperContainer(); } /// <summary> /// 创建操作数据库的DbHelper /// </summary> /// <param name="connectionString">连接字符串</param> /// <returns>操作数据库的DbHelper对象</returns> public override DbHelper CreateDbHelper(string connectionString) { return new MySqlHelper(connectionString); } /// <summary> /// 创建Table初始化器(Code First时需要使用) /// </summary> /// <param name="dbHelper">数据库操作对象</param> /// <param name="commandFactory">SQL命令工厂</param> /// <returns>Table初始化器</returns> public override TableInitializer CreateTableInitializer(IDbHelper dbHelper, ICommandFactory commandFactory) { return new MySqlTableInitializer(dbHelper, commandFactory); } /// <summary> /// 获取Column初始化器(Code First时需要使用) /// </summary> /// <param name="dbHelper">数据库操作对象</param> /// <param name="commandFactory">SQL命令工厂</param> /// <returns>Column初始化器</returns> public override ColumnInitializer CreateColumnInitializer(IDbHelper dbHelper, ICommandFactory commandFactory) { return new MySqlColumnInitializer(dbHelper, commandFactory); } } }
至此,实体框架CloudEntity
的MySQL
驱动编写完成。
有些细心的人可能已经注意到了,要想让它支持所有能够通过ADO.Net
访问的数据库,关键在于用于生成SQL的SQL命令工厂接口ICommandFactory
,除了本次使用的MySQL
,我在CloudEntity
只内置了SQL Server
Oracle
和 Postgresql
的SQL命令工厂类,具体如下:
命名空间 | 类名 | 描述 |
---|---|---|
CloudEntity.CommandTrees.Commom.MySqlClient |
MySqlCommandFactory |
用于生成针对MySQL 的SQL 命令的工厂类 |
CloudEntity.CommandTrees.Commom.SqlClient |
SqlCommandFactory |
用于生成针对SQL Server 的SQL 命令的工厂类 |
CloudEntity.CommandTrees.Commom.OracleClient |
OracleCommandFactory |
用于生成针对Oracle 的SQL 命令的工厂类 |
CloudEntity.CommandTrees.Commom.PostgreSqlClient |
PostgreSqlCommandFactory |
用于生成针对Postgresql 的SQL 命令的工厂类 |
因此,要想支持其他数据库,除了上面配置的数据库驱动类,还要自己写用于生成SQL的SQL命令工厂,有兴趣的可以自己看CloudEntity源码。
首次使用CloudEntity操作数据库
OK,下面开始正式操作数据库。
- 修改
Program
类,初始化数据容器,初始化表,具体如下:好了,按下Ctrl + F5,开始创建表,会打印创建表的sql,执行结果如下:using CloudEntity.Core.Data.Entity; using CloudEntity.Data.Entity; using PingGuo.Blog.Test.Entities; using PingGuo.Blog.Test.MySqlClient; public static class Program { /// <summary> /// 连接字符串 /// </summary> private static string _connectionString; /// <summary> /// 静态初始化 /// </summary> static Program() { // 获取连接字符串 _connectionString = "Server=localhost;User Id=root;Password=000000;Database=pingguo_blog;"; // 初始化数据容器 DbContainer.Init<MySqlInitializer>(_connectionString); } /// <summary> /// 初始化表(表不存在创建表,缺少列则添加列) /// </summary> private static void InitTables() { // 获取数据容器 IDbContainer container = DbContainer.Get(_connectionString); // 初始化角色表 container.InitTable<Role>(); // 初始化用户表 container.InitTable<User>(); } /// <summary> /// 程序启动时执行的方法 /// </summary> private static void Main() { // 初始化表 Program.InitTables(); } }
这里,先来分析下关键代码:================ SELECT COUNT(*) FROM information_schema.Tables t WHERE t.Table_Name = @TableName AND t.Table_Schema = @SchemaName ================ ================ CREATE TABLE pingguo_blog.`Sys_Roles` ( `RoleId` VARCHAR(36) PRIMARY KEY, `RoleName` VARCHAR(25) NOT NULL, `CreatedTime` DATETIME DEFAULT NOW() NULL ) ================ ================ SELECT COUNT(*) FROM information_schema.Tables t WHERE t.Table_Name = @TableName AND t.Table_Schema = @SchemaName ================ ================ CREATE TABLE pingguo_blog.`Sys_Users` ( `UserId` VARCHAR(36) PRIMARY KEY, `UserName` VARCHAR(25) NOT NULL, `RoleId` VARCHAR(36) NOT NULL, `CreatedTime` DATETIME DEFAULT NOW() NULL ) ================
- 初始化数据容器(只需在程序启动时执行一次即可):
// 获取连接字符串 _connectionString = "Server=localhost;User Id=root;Password=000000;Database=pingguo_blog;"; // 初始化数据容器 DbContainer.Init<MySqlInitializer>(_connectionString);
- 后面操作数据库时,先获取数据容器:
// 获取数据容器 IDbContainer container = DbContainer.Get(_connectionString);
- 然后才是,执行其他操作,如初始化表(没有表,创建表,缺少列,添加列):
// 初始化角色表 container.InitTable<Role>(); // 初始化用户表 container.InitTable<User>();
- 初始化数据容器(只需在程序启动时执行一次即可):
- 删除表
- 添加方法
DropTables
,如下:/// <summary> /// 删除所有表 /// </summary> private static void DropTables() { // 获取数据容器 IDbContainer container = DbContainer.Get(_connectionString); // 删除角色表 container.DropTable<Role>(); // 删除用户表 container.DropTable<User>(); }
- 修改
Main
方法,调用方法DropTables
,如下:/// <summary> /// 程序启动时执行的方法 /// </summary> private static void Main() { // 删除表 Program.DropTables(); }
- 好了,按下
Ctrl + F5
开始执行,会打印出删除表的SQL,具体如下:================ SELECT COUNT(*) FROM information_schema.Tables t WHERE t.Table_Name = @TableName ================ ================ DROP TABLE pingguo_blog.`Sys_Roles` ================ ================ SELECT COUNT(*) FROM information_schema.Tables t WHERE t.Table_Name = @TableName ================ ================ DROP TABLE pingguo_blog.`Sys_Users` ================
- 添加方法
- 以上步骤都试过后,重新创建表,下面添加测试数据
- 添加方法
InsertData
,如下:/// <summary> /// 添加测试数据 /// </summary> private static void InsertData() { // 创建一个角色 Role role = new Role() { RoleId = Guid.NewGuid().ToString(), RoleName = "管理员" }; // 创建多个User User[] users = new User[] { new User() { UserId = Guid.NewGuid().ToString(), UserName = "apple", RoleId = role.RoleId }, new User() { UserId = Guid.NewGuid().ToString(), UserName = "admin", RoleId = role.RoleId }, new User() { UserId = Guid.NewGuid().ToString(), UserName = "bob", RoleId = role.RoleId } }; // 获取数据容器 IDbContainer container = DbContainer.Get(_connectionString); // 添加角色 container.List<Role>().Add(role); // 添加所有的用户 foreach (User user in users) { container.List<User>().Add(user); } }
- 修改
Main
方法,调用方法InsertData
,如下:/// <summary> /// 程序启动时执行的方法 /// </summary> private static void Main() { // 添加测试数据 Program.InsertData(); }
- 按下
Ctrl + F5
开始执行,会打印出添加数据的SQL,具体如下:================ INSERT INTO pingguo_blog.`Sys_Roles` (`RoleId`, `RoleName`) VALUES (@RoleId, @RoleName) ================ ================ INSERT INTO pingguo_blog.`Sys_Users` (`UserId`, `UserName`, `RoleId`) VALUES (@UserId, @UserName, @RoleId) ================ ================ INSERT INTO pingguo_blog.`Sys_Users` (`UserId`, `UserName`, `RoleId`) VALUES (@UserId, @UserName, @RoleId) ================ ================ INSERT INTO pingguo_blog.`Sys_Users` (`UserId`, `UserName`, `RoleId`) VALUES (@UserId, @UserName, @RoleId) ================
- 添加方法
- 查询测试
- 添加打印用户信息方法
PrintUsers
:/// <summary> /// 打印用户列表 /// </summary> /// <param name="users">用户列表</param> private static void PrintUsers(IEnumerable<User> users) { foreach (User user in users) { Console.WriteLine("{0} {1}", user.Role.RoleName, user.UserName); } }
- 添加查询方法
Query
如下:/// <summary> /// 查询测试 /// </summary> private static void Query() { // 获取数据容器 IDbContainer container = DbContainer.Get(_connectionString); // 构建角色查询数据源 IDbQuery<Role> roles = container.CreateQuery<Role>() // 并设置只查询RoleId 和 RoleName .SetIncludeBy(r => new { r.RoleId, r.RoleName }) // 设置进一步检索角色名称不为空的角色数据 .SetIsNull(r => r.RoleName, false) // 设置按排序时间倒序排序 .SetSortBy(r => r.CreatedTime, true); // 构建用户查询数据源 IDbQuery<User> users = container.CreateQuery<User>() // 并设置只查询UserId 和 UserName(不设置则查询所有) .SetIncludeBy(u => new { u.UserId, u.UserName }) // 并按RoleId关联角色查询数据源 .SetJoin(roles, u => u.Role, (u, r) => u.RoleId == r.RoleId) // 设置进一步检索在某时间段内录入的用户数据 .SetBetween(u => u.CreatedTime, DateTime.Parse("2023/01/01"), DateTime.Now); // 打印用户列表 Program.PrintUsers(users); }
- 修改
Main
方法,调用方法Query
,如下:/// <summary> /// 程序启动时执行的方法 /// </summary> private static void Main() { // 查询测试 Program.Query(); }
- 按下
Ctrl + F5
开始执行,在打印用户信息之前,会先打印生成的查询SQL,具体如下:================ SELECT `user`.`UserId`, `user`.`UserName`, `role`.`RoleId`, `role`.`RoleName` FROM pingguo_blog.`Sys_Users` `user` INNER JOIN pingguo_blog.`Sys_Roles` `role` ON `user`.`RoleId` = `role`.`RoleId` WHERE `role`.`RoleName` IS NOT NULL AND `user`.`CreatedTime` BETWEEN @CreatedTime0 AND @CreatedTime1 ================ 管理员 admin 管理员 bob 管理员 apple
- 添加打印用户信息方法
- 好了,本章到这里就结束了,有兴趣的小伙伴可以把上一章简介里面的查询测试都试下。