Loading... [](undefined) # C# CodeFirst > 简介: <br/>1.Code First 是一种使用 C# 编码来创建数据库的方法。它是 Entity Framework 中的一种数据访问方式,旨在通过编写 C# 代码来描述数据模型,然后自动生成数据库模式和表结构,从而简化了数据库开发的过程。 > <br/> 2.传统开发中,通常采用DbFirst的方式开发,先有数据库和表,再将对应的表转为实体。随着技术的发展,开始有了CodeFist的方式开发,先创建实体类,再通过实体类反向的创建数据库和表结构,微软的EF框架就是典型,本系统使用的ORM是SqlSugar,同样也支持CodeFisrt > 注意:<br/>1.CodeFirst可以快速开发,使用起来也要分阶段使用,比如早期随便搞,中后期需要禁用一些功能保证数据安全<br/>2.数据库账号需要有比较高的权限,<br/>3.Sqlite不支持删除列和修改列只能添加列 <br/> ## 使用C# Code First 进行数据库开发时,我们需要进行以下几个步骤: 以控制台项目为例 环境信息: ![](https://img.shields.io/badge/.Net-6.0-green) ![](https://img.shields.io/badge/MySQL-8.x-blue) ### 0.前期准备 #### 0.创建项目 ![](https://hwcloud.sdqps.top/ShareImages/PicGo/20230614/638223642052929354.png) #### 1.创建目录 除 ```Program.cs``` 外,均按照当前文件层级创建 ![](https://hwcloud.sdqps.top/ShareImages/PicGo/20230614/638223645583333107.png) ### 1.编写实体类 在 ```Entity``` 文件夹下, 实体类对于创建数据库表至关重要,所以要严格检查是否存在遗漏或其他容易出错的地方, 字段的特性时在创建库表时必不可少的条件,所以要创建符合业务的有效信息 ```csharp #region AuthorInfo // Author:sjx // Date:2023-06-14 16:04 #endregion namespace CodeFirstBySqlSugar.Entity; /// <summary> /// 用户表 /// </summary> [SugarTable("User")] [Description("用户表")] public class User { /// <summary> /// 用户ID,唯一标识用户的编号 /// </summary> [SugarColumn(ColumnDescription = "用户ID,唯一标识用户的编号",IsPrimaryKey = true, IsIdentity = true)] public int UserId { get; set; } /// <summary> /// 用户名,用户的登录名或昵称 /// </summary> [Required, MaxLength(20)] [SugarColumn(ColumnDescription = "用户名,用户的登录名或昵称")] public string UserName { get; set; } /// <summary> /// 密码,用户的登录密码 /// </summary> [Required, MaxLength(50)] [SugarColumn(ColumnDescription = "密码,用户的登录密码")] public string PassWord { get; set; } /// <summary> /// 姓名,用户的真实姓名 /// </summary> [SugarColumn(ColumnDescription = "姓名,用户的真实姓名")] public string FullName { get; set; } /// <summary> /// 性别,用户的性别,通常是男或女 /// </summary> [SugarColumn(ColumnDescription = "性别,用户的性别,通常是男或女")] public string Gender { get; set; } /// <summary> /// 生日,用户的出生日期 /// </summary> [SugarColumn(ColumnDescription = "生日,用户的出生日期")] public DateTime Birthday { get; set; } /// <summary> /// 手机号码,用户的手机号码 /// </summary> [MaxLength(11)] [SugarColumn(ColumnDescription = "手机号码,用户的手机号码")] public string MobileNumber { get; set; } /// <summary> /// 电子邮件,用户的电子邮件地址 /// </summary> [SugarColumn(ColumnDescription = "电子邮件,用户的电子邮件地址")] public string EmailAddress { get; set; } /// <summary> /// 地址,用户的联系地址 /// </summary> [SugarColumn(ColumnDescription = "地址,用户的联系地址")] public string Address { get; set; } /// <summary> /// 注册时间,用户注册账号的时间 /// </summary> [SugarColumn(ColumnDescription = "注册时间,用户注册账号的时间")] public DateTime RegistrationTime { get; set; } = DateTime.Now; } ``` ### 2.创建 DbContext 类 创建 ```SqlSugarHelper.cs``` 类,该类只有一个数据库连接单例,用于帮助迁移数据库表和种子数据,可以根据项目的实际情况自行修改,或使用已存在的数据库连接 ```csharp #region AuthorInfo // Author:sjx // Date:2023-06-14 16:23 #endregion namespace CodeFirstBySqlSugar; public class SqlSugarHelper { /// <summary> /// 数据库单例 /// </summary> /// <returns></returns> public static SqlSugarScope Db() { return new SqlSugarScope(new ConnectionConfig() { ConnectionString = "Data Source=localhost;Database=code_first_mysql;User ID=code_first_mysql;Password=code_first_mysql;pooling=true;port=3306;SslMode=none;CharSet=utf8;Convert Zero Datetime=True;Allow Zero Datetime=True;AllowLoadLocalInfile=true;", DbType = SqlSugar.DbType.MySql, InitKeyType = InitKeyType.Attribute, MoreSettings = new ConnMoreSettings() { IsAutoRemoveDataCache = true }, IsAutoCloseConnection = true }); } } ``` ### 3.配置实体类种子类 #### 0.创建实现种子数据接口 在 ```SeedData``` 文件夹下,创建 ```ISeedData.cs``` ```csharp #region AuthorInfo // Author:sjx // Date:2023-06-14 18:09 #endregion namespace CodeFirstBySqlSugar.SeedData; /// <summary> /// 实体种子数据接口 /// </summary> public class ISeedData { } ``` #### 1.创建泛型接口 在 ```SeedData``` 文件夹下,创建 ```ISqlSugarEntitySeedData.cs``` ```csharp #region AuthorInfo // Author:sjx // Date:2023-06-14 16:16 #endregion namespace CodeFirstBySqlSugar.SeedData; /// <summary> /// 泛型接口 ISqlSugarEntitySeedData /// </summary> /// <typeparam name="TEntity"></typeparam> public interface ISqlSugarEntitySeedData<out TEntity> where TEntity: class, new() { /// <summary> /// 种子数据 /// </summary> /// <returns></returns> IEnumerable<TEntity> HasData(); } ``` #### 2.创建种子数据 在 ```SeedData``` 文件夹下,创建 ```UserSeedData.cs``` 继承 创建泛型接口 ```ISeedData``` 和 ```ISqlSugarEntitySeedData``` 有几个实体类就创建多少个种子数据类 ```csharp #region AuthorInfo // Author:sjx // Date:2023-06-14 16:15 #endregion namespace CodeFirstBySqlSugar.SeedData; /// <summary> /// 种子数据 /// </summary> public class UserSeedData:ISeedData,ISqlSugarEntitySeedData<User> { /// <summary> /// 种子数据 /// </summary> /// <returns></returns> public IEnumerable<User> HasData() { return new List<User>() { new() { UserId = 1, UserName = "admin", PassWord = "123456", FullName = "管理员", Gender = "男", Birthday = DateTime.Parse("1990-01-01"), MobileNumber = "13800138000", EmailAddress = "admin@admin.com", Address = "北京市海淀区", }, new() { UserId = 2, UserName = "user", PassWord = "123456", FullName = "用户", Gender = "男", Birthday = DateTime.Parse("1990-01-01"), MobileNumber = "13800138001", EmailAddress = "user@admin.com", Address = "北京市海淀区", } }; } } ``` ### 4.迁移数据库 在 ```Program.cs``` 创建迁移数据库方法 ```InitDatabase()``` ,仔细阅读 ```注释``` 和 ```代码解释``` ```csharp // 初始化表结构 void InitDatabase() { // 简单写法 // SqlSugarHelper.Db().CodeFirst.InitTables(typeof(User)); // 高级写法 // 获取所有实现了 SugarTableAttribute 特性的类 var assembles = new[] { "CodeFirstBySqlSugar.dll" }; // 获取所有实现了 SugarTableAttribute 特性的类 var types = Assembly .LoadFrom(assembles[0]) .GetTypes() .Where(m => m.GetCustomAttribute<SugarTable>() != null) .ToArray(); // 遍历所有实现了 SugarTableAttribute 特性的类 foreach (var type in types) { // 初始化表结构 SqlSugarHelper.Db().CodeFirst.InitTables(type); } // 控制台输出数据库表迁移成功 Console.WriteLine($"{DateTime.Now.ToString(CultureInfo.InvariantCulture)} 数据库表迁移成功"); } ``` 代码解释:</br> 这段代码是一个初始化数据库表结构的函数 `InitDatabase()`,用于在应用程序启动时自动迁移数据库表结构。函数使用了 SqlSugar 框架提供的 CodeFirst 功能,可以自动根据实体类生成数据库表结构。 函数分为两个部分: </br> 第一部分使用了简单写法: 直接调用 `SqlSugarHelper.Db().CodeFirst.InitTables(typeof(User))` 方法初始化 `User` 实体对应的数据库表结构。 </br> 第二部分使用了高级写法: 首先通过 `Assembly.LoadFrom` 方法加载程序集 `CodeFirstBySqlSugar.dll`,然后使用 `GetTypes` 方法获取程序集中所有的类型,再使用 LINQ 表达式筛选出所有实现了 `SugarTableAttribute` 特性的类型,存储在 `types` 数组中。 接着,函数遍历所有实现了 `SugarTableAttribute` 特性的类型,使用 `SqlSugarHelper.Db().CodeFirst.InitTables(type)` 方法初始化对应的数据库表结构。 最后,函数控制台输出数据库表迁移成功的提示信息。 ### 5.实现种子数据 在 ```Program.cs``` 创建迁移数据库方法 ```InsertData()``` ,仔细阅读 ```注释``` 和 ```代码解释``` ```csharp // 创建种子数据 void InsertData() { // 简单写法 // SqlSugarHelper.Db().Insertable(new UserSeedData().HasData().ToList()).ExecuteCommand(); // 高级写法 // 获取所有实现了 ISeedData 接口的类 var assembles = new[] { "CodeFirstBySqlSugar.dll" }; // 获取所有实现了 ISeedData 接口的类 var baseType = typeof(ISeedData); // 获取所有实现了 ISeedData 接口的类 var seedDataTypes = Assembly .LoadFrom(assembles[0]) .GetTypes() .Where(m => baseType.IsAssignableFrom(m) && m is { IsClass: true, IsAbstract: false }) .ToArray(); // 遍历所有实现了 ISeedData 接口的类 foreach (var seedDataType in seedDataTypes) { // 获取实现了 ISeedData 接口的类的实例 var instance = Activator.CreateInstance(seedDataType) as ISeedData; // 获取实现了 ISeedData 接口的类的 HasData 方法 var hasDataMethod = seedDataType.GetMethod("HasData"); // 如果实现了 ISeedData 接口的类没有 HasData 方法,则跳过 if (hasDataMethod?.Invoke(instance, null) is not IEnumerable seedData) continue; // 获取实现了 ISeedData 接口的类的泛型参数 var data = seedData.Cast<object>(); // 获取实现了 ISeedData 接口的类的泛型参数的泛型参数 var entityType = seedDataType.GetInterfaces().First().GetGenericArguments().First(); // 获取实现了 ISeedData 接口的类的泛型参数的泛型参数的表名 var tableName = SqlSugarHelper.Db().EntityMaintenance.GetEntityInfo(entityType).DbTableName; // 将实现了 ISeedData 接口的类的泛型参数的泛型参数转换为 DataTable var seedDataTable = data.ToList().ToDataTable(); // 设置 DataTable 的表名 seedDataTable.TableName = tableName; // 将 DataTable 转换为 Storageable var storage = SqlSugarHelper.Db().Storageable(seedDataTable).ToStorage(); // 执行插入操作 var insertCount = storage.AsInsertable.ExecuteCommand(); // 控制台输出插入数据的数量 Console.WriteLine($"{DateTime.Now.ToString(CultureInfo.InvariantCulture)} {tableName} 表插入 {insertCount} 条数据"); } } ``` 代码解释: 这段代码是一个创建种子数据的函数 `InsertData()`,用于在应用程序启动时自动向数据库中添加种子数据。函数使用了 SqlSugar 框架提供的 Insertable 和 Storageable 功能,可以将种子数据转换为 DataTable,并将 DataTable 转换为 Storageable,最后执行插入操作。 函数分为两个部分 第一部分使用了简单写法: 直接调用 `SqlSugarHelper.Db().Insertable(new UserSeedData().HasData().ToList()).ExecuteCommand()` 方法向数据库中插入 `UserSeedData` 类中定义的种子数据。 第二部分使用了高级写法: 首先通过 `Assembly.LoadFrom` 方法加载程序集 `CodeFirstBySqlSugar.dll`,然后使用 `GetTypes` 方法获取程序集中所有的类型,再使用 LINQ 表达式筛选出所有实现了 `ISeedData` 接口的类型,存储在 `seedDataTypes` 数组中。 接着,函数遍历所有实现了 `ISeedData` 接口的类型,使用 `Activator.CreateInstance` 方法创建类型的实例,并获取类型的 `HasData` 方法,如果类型没有实现 `HasData` 方法,则跳过。如果类型实现了 `HasData` 方法,则调用该方法获取种子数据,并使用 `Cast<object>` 方法将种子数据转换为 `IEnumerable<object>` 类型的序列。 然后,获取类型的泛型参数,即实体类的类型,并使用 `SqlSugarHelper.Db().EntityMaintenance.GetEntityInfo(entityType).DbTableName` 方法获取实体类对应的表名。接着,将种子数据转换为 DataTable,并设置 DataTable 的表名为实体类对应的表名。 最后,将 DataTable 转换为 Storageable,并执行插入操作。函数控制台输出插入数据的数量。 ### 6.帮助类和全局引用类 #### 0.帮助类 ```ObjectExtension.cs``` ```csharp #region AuthorInfo // Author:sjx // Date:2023-06-14 17:46 #endregion namespace CodeFirstBySqlSugar; /// <summary> /// 对象扩展 /// </summary> public static class ObjectExtension { /// <summary> /// list转datatable /// </summary> /// <param name="list"></param> /// <typeparam name="T"></typeparam> /// <returns></returns> public static DataTable ToDataTable<T>(this List<T> list) { DataTable result = new(); if (list.Count <= 0) return result; var properties = list[0]?.GetType().GetProperties(); if (properties == null) return result; foreach (var pi in properties) { var colType = pi.PropertyType; if (colType.IsGenericType && colType.GetGenericTypeDefinition() == typeof(Nullable<>)) { colType = colType.GetGenericArguments()[0]; } if (IsIgnoreColumn(pi)) continue; result.Columns.Add(pi.Name, colType); } foreach (var t in list) { ArrayList tempList = new(); foreach (var pi in properties) { if (IsIgnoreColumn(pi)) continue; var obj = pi.GetValue(t, null); tempList.Add(obj); } var array = tempList.ToArray(); result.LoadDataRow(array, true); } return result; } /// <summary> /// 是否忽略列 /// </summary> /// <param name="pi"></param> /// <returns></returns> private static bool IsIgnoreColumn(MemberInfo pi) { var sc = pi.GetCustomAttributes<SugarColumn>(false).FirstOrDefault(u => u.IsIgnore == true); return sc != null; } } ``` #### 1.全局引用类 ```GlobalUsings.cs``` (可以不使用全局引用类,可忽略) ```csharp #region AuthorInfo // Author:sjx // Date:2023-06-14 16:05 #endregion global using System.Collections; global using System.ComponentModel; global using System.ComponentModel.DataAnnotations; global using System.Globalization; global using System.Reflection; global using CodeFirstBySqlSugar; global using CodeFirstBySqlSugar.Entity; global using CodeFirstBySqlSugar.SeedData; global using SqlSugar; global using System.Data; ``` ### 7.演示效果 ![](https://hwcloud.sdqps.top/ShareImages/PicGo/20230614/638223665390731163.gif) # 源码:[C# CodeFirst 发行版 - Gitee.com](https://gitee.com/ktith/signal-r-simple-private-chat/releases) ![source](https://hwcloud.sdqps.top/ShareImages/PicGo/20230615/638224175820024747.png) 最后修改:2023 年 06 月 15 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 5 如果觉得我的文章对你有用,请随意赞赏
1 条评论
为什么没有错误处理?你对用户和自己的代码这么放心??