[](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 进行数据库开发时,我们需要进行以下几个步骤:
以控制台项目为例
环境信息:
0.前期准备
0.创建项目
1.创建目录
除 Program.cs
外,均按照当前文件层级创建
1.编写实体类
在 Entity
文件夹下, 实体类对于创建数据库表至关重要,所以要严格检查是否存在遗漏或其他容易出错的地方,
字段的特性时在创建库表时必不可少的条件,所以要创建符合业务的有效信息
#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
类,该类只有一个数据库连接单例,用于帮助迁移数据库表和种子数据,可以根据项目的实际情况自行修改,或使用已存在的数据库连接
#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
#region AuthorInfo
// Author:sjx
// Date:2023-06-14 18:09
#endregion
namespace CodeFirstBySqlSugar.SeedData;
/// <summary>
/// 实体种子数据接口
/// </summary>
public class ISeedData { }
1.创建泛型接口
在 SeedData
文件夹下,创建 ISqlSugarEntitySeedData.cs
#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
有几个实体类就创建多少个种子数据类
#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()
,仔细阅读 注释
和 代码解释
// 初始化表结构
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)} 数据库表迁移成功");
}
代码解释:
这段代码是一个初始化数据库表结构的函数 InitDatabase()
,用于在应用程序启动时自动迁移数据库表结构。函数使用了 SqlSugar 框架提供的 CodeFirst 功能,可以自动根据实体类生成数据库表结构。
函数分为两个部分:
第一部分使用了简单写法:
直接调用 SqlSugarHelper.Db().CodeFirst.InitTables(typeof(User))
方法初始化 User
实体对应的数据库表结构。
第二部分使用了高级写法:
首先通过 Assembly.LoadFrom
方法加载程序集 CodeFirstBySqlSugar.dll
,然后使用 GetTypes
方法获取程序集中所有的类型,再使用 LINQ 表达式筛选出所有实现了 SugarTableAttribute
特性的类型,存储在 types
数组中。
接着,函数遍历所有实现了 SugarTableAttribute
特性的类型,使用 SqlSugarHelper.Db().CodeFirst.InitTables(type)
方法初始化对应的数据库表结构。
最后,函数控制台输出数据库表迁移成功的提示信息。
5.实现种子数据
在 Program.cs
创建迁移数据库方法 InsertData()
,仔细阅读 注释
和 代码解释
// 创建种子数据
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
#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
(可以不使用全局引用类,可忽略)
#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;
此处评论已关闭