Files
2025-09-29 13:48:36 +08:00

200 lines
9.0 KiB
C#

// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
//
// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
// 1.请不要删除和修改根目录下的LICENSE文件。
// 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
// 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
// 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。
// 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
// 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。
using System.Collections;
namespace SimpleAdmin.SqlSugar;
/// <summary>
/// CodeFirst功能类
/// </summary>
[SuppressSniffer]
public static class CodeFirstUtils
{
/// <summary>
/// CodeFirst生成数据库表结构和种子数据
/// </summary>
/// <param name="options">codefirst选项</param>
/// <param name="assemblyName">程序集名称</param>
public static void CodeFirst(BaseOptions options, string assemblyName)
{
var appName = assemblyName.Split(",")[0];
if (options.InitTable)//如果需要初始化表结构
{
Console.WriteLine($"开始初始化{appName}数据库表结构");
InitTable(assemblyName);
}
if (options.InitSeedData)
{
Console.WriteLine($"开始初始化{appName}数据库种子数据");
InitSeedData(assemblyName);
}
}
/// <summary>
/// 初始化数据库表结构
/// </summary>
/// <param name="assemblyName">程序集名称</param>
private static void InitTable(string assemblyName)
{
// 获取所有实体表-初始化表结构
var entityTypes = App.EffectiveTypes.Where(u =>
!u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.Assembly.FullName == assemblyName);
if (!entityTypes.Any()) return;//没有就退出
foreach (var entityType in entityTypes)
{
var tenantAtt = entityType.GetCustomAttribute<TenantAttribute>();//获取SqlSugar多租户特性
var ignoreInit = entityType.GetCustomAttribute<IgnoreInitTableAttribute>();//获取忽略初始化特性
if (ignoreInit != null) continue;//如果有忽略初始化特性
if (tenantAtt == null) continue;//如果没有租户特性就下一个
var db = DbContext.DB.GetConnectionScope(tenantAtt.configId.ToString());//获取数据库对象
var splitTable = entityType.GetCustomAttribute<SplitTableAttribute>();//获取自动分表特性
if (splitTable == null)//如果特性是空
db.CodeFirst.InitTables(entityType);//普通创建
else
db.CodeFirst.SplitTables().InitTables(entityType);//自动分表创建
}
}
/// <summary>
/// 初始化种子数据
/// </summary>
/// <param name="assemblyName">程序集名称</param>
private static void InitSeedData(string assemblyName)
{
// 获取所有种子配置-初始化数据
var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && u is { IsAbstract: false, IsClass: true }
&& u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>))) && u.Assembly.FullName == assemblyName);
if (!seedDataTypes.Any()) return;
foreach (var seedType in seedDataTypes)//遍历种子类
{
//使用与指定参数匹配程度最高的构造函数来创建指定类型的实例。
var instance = Activator.CreateInstance(seedType);
//获取SeedData方法
var hasDataMethod = seedType.GetMethod("SeedData");
//判断是否有种子数据
var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>();
if (seedData == null) continue;//没有种子数据就下一个
var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();//获取实体类型
var tenantAtt = entityType.GetCustomAttribute<TenantAttribute>();//获取SqlSugar租户特性
if (tenantAtt == null) continue;//如果没有租户特性就下一个
var db = DbContext.DB.GetConnectionScope(tenantAtt.configId.ToString());//获取数据库对象
var config = DbContext.DB_CONFIGS.FirstOrDefault(u => u.ConfigId == tenantAtt.configId.ToString());//获取数据库配置
// var seedDataTable = seedData.ToList().ToDataTable();//获取种子数据:已弃用
var entityInfo = db.EntityMaintenance.GetEntityInfo(entityType);
// seedDataTable.TableName = db.EntityMaintenance.GetEntityInfo(entityType).DbTableName;//获取表名
var ignoreAdd = hasDataMethod.GetCustomAttribute<IgnoreSeedDataAddAttribute>();//读取忽略插入特性
var ignoreUpdate = hasDataMethod.GetCustomAttribute<IgnoreSeedDataUpdateAttribute>();//读取忽略更新特性
if (entityInfo.Columns.Any(u => u.IsPrimarykey))//判断种子数据是否有主键
{
// 按主键进行批量增加和更新
var storage = db.StorageableByObject(seedData.ToList()).ToStorage();
if (ignoreAdd == null) storage.AsInsertable.ExecuteCommand();//执行插入
if (ignoreUpdate == null) storage.AsUpdateable.ExecuteCommand();//只有没有忽略更新的特性才执行更新
}
else// 没有主键或者不是预定义的主键(有重复的可能)
{
//全量插入
// 无主键则只进行插入
if (!db.Queryable(entityInfo.DbTableName, entityInfo.DbTableName).Any() && ignoreAdd == null)
db.InsertableByObject(seedData.ToList()).ExecuteCommand();
}
}
}
/// <summary>
/// 判断类型是否实现某个泛型
/// </summary>
/// <param name="type">类型</param>
/// <param name="generic">泛型类型</param>
/// <returns>bool</returns>
public static bool HasImplementedRawGeneric(this Type type, Type generic)
{
// 检查接口类型
var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType);
if (isTheRawGenericType) return true;
// 检查类型
while (type != null && type != typeof(object))
{
isTheRawGenericType = IsTheRawGenericType(type);
if (isTheRawGenericType) return true;
type = type.BaseType ?? default;
}
return false;
// 判断逻辑
bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type);
}
/// <summary>
/// List转DataTable
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <returns></returns>
public static DataTable ToDataTable<T>(this List<T> list)
{
var result = new DataTable();
if (list.Count > 0)
{
// result.TableName = list[0].GetType().Name; // 表名赋值
var propertys = list[0].GetType().GetProperties();
foreach (var pi in propertys)
{
var colType = pi.PropertyType;
if (colType.IsGenericType && colType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
colType = colType.GetGenericArguments()[0];
}
if (IsIgnoreColumn(pi))
continue;
if (IsJsonColumn(pi))//如果是json特性就是sting类型
colType = typeof(string);
result.Columns.Add(pi.Name, colType);
}
for (var i = 0; i < list.Count; i++)
{
var tempList = new ArrayList();
foreach (var pi in propertys)
{
if (IsIgnoreColumn(pi))
continue;
var obj = pi.GetValue(list[i], null);
if (IsJsonColumn(pi))//如果是json特性就是转化为json格式
obj = obj?.ToJson();//如果json字符串是空就传null
tempList.Add(obj);
}
var array = tempList.ToArray();
result.LoadDataRow(array, true);
}
}
return result;
}
/// <summary>
/// 排除SqlSugar忽略的列
/// </summary>
/// <param name="pi"></param>
/// <returns></returns>
private static bool IsIgnoreColumn(PropertyInfo pi)
{
var sc = pi.GetCustomAttributes<SugarColumn>(false).FirstOrDefault(u => u.IsIgnore);
return sc != null;
}
private static bool IsJsonColumn(PropertyInfo pi)
{
var sc = pi.GetCustomAttributes<SugarColumn>(false).FirstOrDefault(u => u.IsJson);
return sc != null;
}
}