2024-07-15 11:38:17 +08:00
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
2025-08-12 17:36:28 +08:00
using NewLife.IO ;
using NewLife.Reflection ;
2025-08-13 10:57:54 +08:00
using System.Text.Json ;
2024-07-15 11:38:17 +08:00
namespace Admin.NET.Core ;
/// <summary>
2025-05-18 17:27:22 +08:00
/// Sqlsugar 扩展方法
2024-07-15 11:38:17 +08:00
/// </summary>
public static class SqlSugarExtension
{
2025-05-18 17:27:22 +08:00
#region 切 换 数 据 库
/// <summary>
/// 切换数据库
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="db"></param>
/// <returns></returns>
public static ISqlSugarClient ForTenant < TEntity > ( this ISqlSugarClient db )
{
var attr = typeof ( TEntity ) . GetCustomAttribute < TenantAttribute > ( ) ;
var tenantId = attr ! = null ? GetConfigIdFromAttribute ( attr ) : SqlSugarConst . DefaultTenantId ;
2025-05-19 12:43:54 +08:00
return db . AsTenant ( ) . GetConnectionScope ( tenantId ? ? SqlSugarConst . DefaultTenantId ) ;
2025-05-18 17:27:22 +08:00
}
private static object GetConfigIdFromAttribute ( TenantAttribute attr )
{
const string targetKey = "configId" ;
var type = attr . GetType ( ) ;
// 方式1: 尝试通过属性获取
var prop = type . GetProperty ( targetKey , BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ) ;
if ( prop ! = null ) return prop . GetValue ( attr ) ;
// 方式2: 尝试通过私有字段获取
var fieldNames = new [ ]
{
$"<{targetKey}>k__BackingField" , // 自动属性字段
$"_{targetKey}" , // 常规私有字段 (如_configId)
$"m_{targetKey}" , // 匈牙利命名法
"configId" // 直接字段访问
} ;
foreach ( var name in fieldNames )
{
var field = type . GetField ( name , BindingFlags . NonPublic | BindingFlags . Instance ) ;
if ( field ! = null ) return field . GetValue ( attr ) ;
}
// 方式3: 通过Dynamic访问( 备用方案)
try
{
dynamic d = attr ;
return d . configId ;
}
catch
{
return null ;
}
}
#endregion 切 换 数 据 库
#region 动 态 查 询 扩 展 方 法
/// <summary>
/// Sqlsugar 动态查询扩展方法
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="queryable"></param>
/// <param name="filter"></param>
/// <returns></returns>
2024-07-15 11:38:17 +08:00
public static ISugarQueryable < T > SearchBy < T > ( this ISugarQueryable < T > queryable , BaseFilter filter )
{
2024-12-30 02:20:39 +08:00
return queryable . SearchByKeyword ( filter . Keyword ) . AdvancedSearch ( filter . Search ) . AdvancedFilter ( filter . Filter ) ;
2024-07-15 11:38:17 +08:00
}
public static ISugarQueryable < T > SearchByKeyword < T > ( this ISugarQueryable < T > queryable , string keyword )
{
return queryable . AdvancedSearch ( new Search { Keyword = keyword } ) ;
}
public static ISugarQueryable < T > AdvancedSearch < T > ( this ISugarQueryable < T > queryable , Search search )
{
if ( ! string . IsNullOrWhiteSpace ( search ? . Keyword ) )
{
var paramExpr = Expression . Parameter ( typeof ( T ) ) ;
Expression right = Expression . Constant ( false ) ;
2024-12-30 02:20:39 +08:00
if ( search . Fields ? . Count ! = 0 )
2024-07-15 11:38:17 +08:00
{
foreach ( string field in search . Fields )
{
MemberExpression propertyExpr = GetPropertyExpression ( field , paramExpr ) ;
var left = AddSearchPropertyByKeyword < T > ( propertyExpr , search . Keyword ) ;
right = Expression . Or ( left , right ) ;
}
}
else
{
2024-12-30 02:20:39 +08:00
var properties = typeof ( T ) . GetProperties ( ) . Where ( prop = > Nullable . GetUnderlyingType ( prop . PropertyType ) = = null
& & ! prop . PropertyType . IsEnum
& & Type . GetTypeCode ( prop . PropertyType ) ! = TypeCode . Object ) ;
2024-07-15 11:38:17 +08:00
foreach ( var property in properties )
{
var propertyExpr = Expression . Property ( paramExpr , property ) ;
var left = AddSearchPropertyByKeyword < T > ( propertyExpr , search . Keyword ) ;
right = Expression . Or ( left , right ) ;
}
}
var lambda = Expression . Lambda < Func < T , bool > > ( right , paramExpr ) ;
return queryable . Where ( lambda ) ;
}
return queryable ;
}
public static ISugarQueryable < T > AdvancedFilter < T > ( this ISugarQueryable < T > queryable , Filter filter )
{
if ( filter is not null )
{
var parameter = Expression . Parameter ( typeof ( T ) ) ;
Expression binaryExpresioFilter ;
2025-08-15 14:30:23 +08:00
if ( Enum . IsDefined ( filter . Logic ) )
2024-07-15 11:38:17 +08:00
{
if ( filter . Filters is null ) throw new ArgumentException ( "The Filters attribute is required when declaring a logic" ) ;
2025-05-28 11:53:51 +08:00
binaryExpresioFilter = CreateFilterExpression ( filter . Logic , filter . Filters , parameter ) ;
2024-07-15 11:38:17 +08:00
}
else
{
var filterValid = GetValidFilter ( filter ) ;
2025-05-28 11:53:51 +08:00
binaryExpresioFilter = CreateFilterExpression ( filterValid . Field ! , filterValid . Operator , filterValid . Value , parameter ) ;
2024-07-15 11:38:17 +08:00
}
var lambda = Expression . Lambda < Func < T , bool > > ( binaryExpresioFilter , parameter ) ;
return queryable . Where ( lambda ) ;
}
return queryable ;
}
2025-08-15 14:30:23 +08:00
private static BinaryExpression CombineFilter ( FilterLogicEnum filterLogic , Expression bExpresionBase , Expression bExpresion )
2024-07-15 11:38:17 +08:00
{
return filterLogic switch
{
FilterLogicEnum . And = > Expression . And ( bExpresionBase , bExpresion ) ,
FilterLogicEnum . Or = > Expression . Or ( bExpresionBase , bExpresion ) ,
FilterLogicEnum . Xor = > Expression . ExclusiveOr ( bExpresionBase , bExpresion ) ,
_ = > throw new ArgumentException ( "FilterLogic is not valid." , nameof ( filterLogic ) ) ,
} ;
}
private static Filter GetValidFilter ( Filter filter )
{
if ( string . IsNullOrEmpty ( filter . Field ) ) throw new ArgumentException ( "The field attribute is required when declaring a filter" ) ;
if ( filter . Operator . IsNullOrEmpty ( ) ) throw new ArgumentException ( "The Operator attribute is required when declaring a filter" ) ;
return filter ;
}
2024-12-30 02:20:39 +08:00
private static Expression CreateFilterExpression ( FilterLogicEnum filterLogic , IEnumerable < Filter > filters , ParameterExpression parameter )
2024-07-15 11:38:17 +08:00
{
Expression filterExpression = default ! ;
foreach ( var filter in filters )
{
2025-08-12 17:36:28 +08:00
Expression bExpresionFilter ;
2025-08-15 14:30:23 +08:00
if ( Enum . IsDefined ( filter . Logic ) )
2024-07-15 11:38:17 +08:00
{
if ( filter . Filters is null ) throw new ArgumentException ( "The Filters attribute is required when declaring a logic" ) ;
2025-05-28 11:53:51 +08:00
bExpresionFilter = CreateFilterExpression ( filter . Logic , filter . Filters , parameter ) ;
2024-07-15 11:38:17 +08:00
}
else
{
var filterValid = GetValidFilter ( filter ) ;
2025-05-28 11:53:51 +08:00
bExpresionFilter = CreateFilterExpression ( filterValid . Field ! , filterValid . Operator , filterValid . Value , parameter ) ;
2024-07-15 11:38:17 +08:00
}
filterExpression = filterExpression is null ? bExpresionFilter : CombineFilter ( filterLogic , filterExpression , bExpresionFilter ) ;
}
return filterExpression ;
}
2024-12-30 02:20:39 +08:00
private static Expression CreateFilterExpression ( string field , FilterOperatorEnum filterOperator , object? value , ParameterExpression parameter )
2024-07-15 11:38:17 +08:00
{
var propertyExpresion = GetPropertyExpression ( field , parameter ) ;
var valueExpresion = GeValuetExpression ( field , value , propertyExpresion . Type ) ;
return CreateFilterExpression ( propertyExpresion , valueExpresion , filterOperator ) ;
}
2024-12-30 02:20:39 +08:00
private static Expression CreateFilterExpression ( MemberExpression memberExpression , ConstantExpression constantExpression , FilterOperatorEnum filterOperator )
2024-07-15 11:38:17 +08:00
{
return filterOperator switch
{
FilterOperatorEnum . EQ = > Expression . Equal ( memberExpression , constantExpression ) ,
FilterOperatorEnum . NEQ = > Expression . NotEqual ( memberExpression , constantExpression ) ,
FilterOperatorEnum . LT = > Expression . LessThan ( memberExpression , constantExpression ) ,
FilterOperatorEnum . LTE = > Expression . LessThanOrEqual ( memberExpression , constantExpression ) ,
FilterOperatorEnum . GT = > Expression . GreaterThan ( memberExpression , constantExpression ) ,
FilterOperatorEnum . GTE = > Expression . GreaterThanOrEqual ( memberExpression , constantExpression ) ,
FilterOperatorEnum . Contains = > Expression . Call ( memberExpression , nameof ( FilterOperatorEnum . Contains ) , null , constantExpression ) ,
FilterOperatorEnum . StartsWith = > Expression . Call ( memberExpression , nameof ( FilterOperatorEnum . StartsWith ) , null , constantExpression ) ,
FilterOperatorEnum . EndsWith = > Expression . Call ( memberExpression , nameof ( FilterOperatorEnum . EndsWith ) , null , constantExpression ) ,
_ = > throw new ArgumentException ( "Filter Operator is not valid." ) ,
} ;
}
private static string GetStringFromJsonElement ( object value )
{
2024-12-30 02:20:39 +08:00
if ( value is JsonElement element ) return element . GetString ( ) ! ;
if ( value is string v ) return v ;
2024-07-15 11:38:17 +08:00
return value ? . ToString ( ) ;
}
2024-12-30 02:20:39 +08:00
private static ConstantExpression GeValuetExpression ( string field , object? value , Type propertyType )
2024-07-15 11:38:17 +08:00
{
if ( value = = null ) return Expression . Constant ( null , propertyType ) ;
if ( propertyType . IsEnum )
{
string? stringEnum = GetStringFromJsonElement ( value ) ;
if ( ! Enum . TryParse ( propertyType , stringEnum , true , out object? valueparsed ) ) throw new ArgumentException ( string . Format ( "Value {0} is not valid for {1}" , value , field ) ) ;
return Expression . Constant ( valueparsed , propertyType ) ;
}
2024-08-27 23:34:25 +08:00
if ( propertyType = = typeof ( long ) | | propertyType = = typeof ( long? ) )
2024-07-15 11:38:17 +08:00
{
string? stringLong = GetStringFromJsonElement ( value ) ;
if ( ! long . TryParse ( stringLong , out long valueparsed ) ) throw new ArgumentException ( string . Format ( "Value {0} is not valid for {1}" , value , field ) ) ;
return Expression . Constant ( valueparsed , propertyType ) ;
}
if ( propertyType = = typeof ( Guid ) )
{
string? stringGuid = GetStringFromJsonElement ( value ) ;
if ( ! Guid . TryParse ( stringGuid , out Guid valueparsed ) ) throw new ArgumentException ( string . Format ( "Value {0} is not valid for {1}" , value , field ) ) ;
return Expression . Constant ( valueparsed , propertyType ) ;
}
if ( propertyType = = typeof ( string ) )
{
string? text = GetStringFromJsonElement ( value ) ;
return Expression . Constant ( text , propertyType ) ;
}
if ( propertyType = = typeof ( DateTime ) | | propertyType = = typeof ( DateTime ? ) )
{
string? text = GetStringFromJsonElement ( value ) ;
return Expression . Constant ( ChangeType ( text , propertyType ) , propertyType ) ;
}
return Expression . Constant ( ChangeType ( ( ( JsonElement ) value ) . GetRawText ( ) , propertyType ) , propertyType ) ;
}
private static dynamic? ChangeType ( object value , Type conversion )
{
var t = conversion ;
if ( t . IsGenericType & & t . GetGenericTypeDefinition ( ) . Equals ( typeof ( Nullable < > ) ) )
{
if ( value = = null )
return null ;
t = Nullable . GetUnderlyingType ( t ) ;
}
return Convert . ChangeType ( value , t ! ) ;
}
2024-12-30 02:20:39 +08:00
private static MemberExpression GetPropertyExpression ( string propertyName , ParameterExpression parameter )
2024-07-15 11:38:17 +08:00
{
Expression propertyExpression = parameter ;
foreach ( string member in propertyName . Split ( '.' ) )
{
propertyExpression = Expression . PropertyOrField ( propertyExpression , member ) ;
}
return ( MemberExpression ) propertyExpression ;
}
2025-08-15 14:30:23 +08:00
private static MethodCallExpression AddSearchPropertyByKeyword < T > ( Expression propertyExpr , string keyword , FilterOperatorEnum operatorSearch = FilterOperatorEnum . Contains )
2024-07-15 11:38:17 +08:00
{
if ( propertyExpr is not MemberExpression memberExpr | | memberExpr . Member is not PropertyInfo property )
throw new ArgumentException ( "propertyExpr must be a property expression." , nameof ( propertyExpr ) ) ;
ConstantExpression constant = Expression . Constant ( keyword ) ;
MethodInfo method = operatorSearch switch
{
2024-12-30 02:20:39 +08:00
FilterOperatorEnum . Contains = > typeof ( string ) . GetMethod ( nameof ( FilterOperatorEnum . Contains ) , [ typeof ( string ) ] ) ,
FilterOperatorEnum . StartsWith = > typeof ( string ) . GetMethod ( nameof ( FilterOperatorEnum . StartsWith ) , [ typeof ( string ) ] ) ,
FilterOperatorEnum . EndsWith = > typeof ( string ) . GetMethod ( nameof ( FilterOperatorEnum . EndsWith ) , [ typeof ( string ) ] ) ,
2024-07-15 11:38:17 +08:00
_ = > throw new ArgumentException ( "Filter Operator is not valid." ) ,
} ;
2024-12-30 02:20:39 +08:00
Expression selectorExpr = property . PropertyType = = typeof ( string )
? propertyExpr
: Expression . Condition ( Expression . Equal ( Expression . Convert ( propertyExpr , typeof ( object ) ) , Expression . Constant ( null , typeof ( object ) ) ) , Expression . Constant ( null , typeof ( string ) ) , Expression . Call ( propertyExpr , "ToString" , null , null ) ) ;
2024-07-15 11:38:17 +08:00
return Expression . Call ( selectorExpr , method , constant ) ;
}
2025-05-18 17:27:22 +08:00
#endregion 动 态 查 询 扩 展 方 法
2025-06-11 01:18:59 +08:00
#region 初 始 化 库 表 结 构 和 种 子 数 据
2025-05-18 17:27:22 +08:00
/// <summary>
/// 初始化表实体
/// </summary>
/// <param name="dbProvider"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static void InitTable < T > ( this SqlSugarScopeProvider dbProvider ) where T : class , new ( )
{
InitTable ( dbProvider , typeof ( T ) ) ;
}
/// <summary>
/// 初始化表实体
/// </summary>
/// <param name="entityType"></param>
/// <param name="dbProvider"></param>
/// <returns></returns>
public static void InitTable ( this SqlSugarScopeProvider dbProvider , Type entityType )
{
// 初始化表实体,如果存在分表特性,需要额外处理
if ( entityType . GetCustomAttribute < SplitTableAttribute > ( ) = = null )
dbProvider . CodeFirst . InitTables ( entityType ) ;
else
dbProvider . CodeFirst . SplitTables ( ) . InitTables ( entityType ) ;
// 将不存在实体中的字段改为可空
var entityInfo = dbProvider . EntityMaintenance . GetEntityInfo ( entityType ) ;
var dbColumnInfos = dbProvider . DbMaintenance . GetColumnInfosByTableName ( entityInfo . DbTableName ) ? ? [ ] ;
2025-06-05 19:35:21 +08:00
foreach ( var dbColumnInfo in dbColumnInfos . Where ( dbColumnInfo = > ! dbColumnInfo . IsPrimarykey & & entityInfo . Columns . All ( u = > u . DbColumnName = = null | | u . DbColumnName . ToLower ( ) ! = dbColumnInfo . DbColumnName . ToLower ( ) ) ) )
2025-05-18 17:27:22 +08:00
{
dbColumnInfo . IsNullable = true ;
dbProvider . DbMaintenance . UpdateColumn ( entityInfo . DbTableName , dbColumnInfo ) ;
}
}
/// <summary>
/// 初始化表种子数据
/// </summary>
/// <param name="db"></param>
/// <param name="handleBefore"></param>
/// <returns></returns>
2025-05-19 12:43:54 +08:00
public static ( int , int , int ) ? InitTableSeedData < T > ( this SqlSugarScopeProvider db , Action < object > handleBefore = null )
2025-05-18 17:27:22 +08:00
{
return InitTableSeedData ( db , typeof ( T ) , handleBefore ) ;
}
/// <summary>
/// 初始化表种子数据
/// </summary>
2025-05-19 12:43:54 +08:00
/// <param name="dbProvider"></param>
2025-05-18 17:27:22 +08:00
/// <param name="seedType"></param>
/// <param name="handleBefore"></param>
/// <returns></returns>
2025-05-19 12:43:54 +08:00
public static ( int , int , int ) ? InitTableSeedData ( this SqlSugarScopeProvider dbProvider , Type seedType , Action < object > handleBefore = null )
2025-05-18 17:27:22 +08:00
{
2025-05-19 12:43:54 +08:00
var config = dbProvider . CurrentConnectionConfig ;
2025-05-18 17:27:22 +08:00
// 获取表实体类型
var entityType = seedType . GetInterfaces ( ) . First ( ) . GetGenericArguments ( ) . First ( ) ;
if ( config . ConfigId . ToString ( ) = = SqlSugarConst . MainConfigId ) // 默认库(有系统表特性、没有日志表和租户表特性)
{
if ( entityType . GetCustomAttribute < SysTableAttribute > ( ) = = null & &
( entityType . GetCustomAttribute < LogTableAttribute > ( ) ! = null | |
2025-08-12 17:36:28 +08:00
entityType . GetCustomAttribute < TenantAttribute > ( ) ! = null ) )
{
2025-08-07 11:15:57 +08:00
Console . WriteLine ( $" 忽略 {seedType.FullName,-58} ({dbProvider.CurrentConnectionConfig.ConfigId}) 原因: 非SysTable 与 (LogTable 或 Tenant)表" ) ;
2025-08-12 17:36:28 +08:00
return default ;
2025-05-28 10:06:23 +08:00
}
2025-05-18 17:27:22 +08:00
}
else if ( config . ConfigId . ToString ( ) = = SqlSugarConst . LogConfigId ) // 日志库
{
2025-08-12 17:36:28 +08:00
if ( entityType . GetCustomAttribute < LogTableAttribute > ( ) = = null )
{
Console . WriteLine ( $" 忽略 {seedType.FullName,-58} ({dbProvider.CurrentConnectionConfig.ConfigId}) 原因: 非LogTable表" ) ;
return default ;
2025-05-28 10:06:23 +08:00
}
2025-05-18 17:27:22 +08:00
}
else
{
var att = entityType . GetCustomAttribute < TenantAttribute > ( ) ; // 自定义的库
2025-08-12 17:36:28 +08:00
if ( att = = null | | att . configId . ToString ( ) ! = config . ConfigId . ToString ( ) )
{
Console . WriteLine ( $" 忽略 {seedType.FullName,-58} ({dbProvider.CurrentConnectionConfig.ConfigId}) 原因: 非Tenant表" ) ;
return default ;
2025-05-28 10:06:23 +08:00
}
2025-05-18 17:27:22 +08:00
}
var instance = Activator . CreateInstance ( seedType ) ;
var hasDataMethod = seedType . GetMethod ( "HasData" ) ;
var seedData = ( ( IEnumerable ) hasDataMethod ? . Invoke ( instance , null ) ) ? . Cast < object > ( ) . ToArray ( ) ? ? [ ] ;
2025-08-12 17:36:28 +08:00
if ( seedData . Length = = 0 )
{
Console . WriteLine ( $" 忽略 {seedType.FullName,-58} ({dbProvider.CurrentConnectionConfig.ConfigId}) 原因: 没有种子数据" ) ;
return default ;
2025-05-28 10:06:23 +08:00
}
2025-05-18 17:27:22 +08:00
// 若实体包含Id字段, 则设置为当前租户Id递增1
2025-06-11 01:18:59 +08:00
var idProperty = entityType . GetProperty ( nameof ( EntityBaseId . Id ) ) ;
2025-05-18 17:27:22 +08:00
var entityInfo = dbProvider . EntityMaintenance . GetEntityInfo ( entityType ) ;
2025-06-11 01:18:59 +08:00
if ( idProperty ! = null & & idProperty . PropertyType = = typeof ( long ) & & entityInfo . Columns . Any ( u = > u . PropertyName = = nameof ( EntityBaseId . Id ) ) )
2025-05-18 17:27:22 +08:00
{
var seedId = config . ConfigId . ToLong ( ) ;
foreach ( var sd in seedData )
{
2025-06-11 01:18:59 +08:00
var id = idProperty ! . GetValue ( sd , null ) ;
2025-05-18 17:27:22 +08:00
if ( id = = null | | id . ToString ( ) = = "0" | | string . IsNullOrWhiteSpace ( id . ToString ( ) ) )
2025-06-11 01:18:59 +08:00
idProperty . SetValue ( sd , + + seedId ) ;
2025-05-18 17:27:22 +08:00
}
}
// 执行前处理种子数据
if ( handleBefore ! = null ) foreach ( var sd in seedData ) handleBefore ( sd ) ;
2025-09-05 14:35:50 +08:00
dbProvider . QueryFilter . ClearAndBackup ( ) ;
2025-05-18 17:27:22 +08:00
int total , insertCount = 0 , updateCount = 0 ;
if ( entityType . GetCustomAttribute < SplitTableAttribute > ( true ) ! = null )
{
// 拆分表的操作需要实体类型,而通过反射很难实现
// 所以, 这里将Init方法写在“种子数据类”内部, 再传入 db 反射调用
var hasInitMethod = seedType . GetMethod ( "Init" ) ;
2025-05-19 12:43:54 +08:00
var parameters = new object [ ] { dbProvider } ;
2025-05-18 17:27:22 +08:00
var result = hasInitMethod ? . Invoke ( instance , parameters ) as ( int , int , int ) ? ;
total = result ? . Item1 ? ? 0 ;
insertCount = result ? . Item2 ? ? 0 ;
updateCount = result ? . Item3 ? ? 0 ;
}
else
{
var seedDataList = seedData . ToList ( ) ;
total = seedDataList . Count ;
// 按主键进行批量增加和更新
if ( entityInfo . Columns . Any ( u = > u . IsPrimarykey ) )
{
// 先修改再插入,否则会更新修改时间字段
var storage = dbProvider . StorageableByObject ( seedDataList ) . ToStorage ( ) ;
if ( seedType . GetCustomAttribute < IgnoreUpdateSeedAttribute > ( ) = = null ) // 有忽略更新种子特性时则不更新
{
updateCount = storage . AsUpdateable . IgnoreColumns ( entityInfo . Columns . Where ( u = > u . PropertyInfo . GetCustomAttribute < IgnoreUpdateSeedColumnAttribute > ( ) ! = null )
. Select ( u = > u . PropertyName ) . ToArray ( ) ) . ExecuteCommand ( ) ;
}
insertCount = storage . AsInsertable . ExecuteCommand ( ) ;
}
// 无主键则只进行插入
else
{
if ( ! dbProvider . Queryable ( entityInfo . DbTableName , entityInfo . DbTableName ) . Any ( ) )
{
insertCount = seedDataList . Count ;
dbProvider . InsertableByObject ( seedDataList ) . ExecuteCommand ( ) ;
}
}
}
2025-09-05 14:35:50 +08:00
dbProvider . QueryFilter . Restore ( ) ;
2025-05-18 17:27:22 +08:00
return ( total , insertCount , updateCount ) ;
}
2025-06-11 01:18:59 +08:00
#endregion 初 始 化 库 表 结 构 和 种 子 数 据
2025-05-19 02:08:46 +08:00
#region 视 图 操 作
/// <summary>
/// 获取映射SQL语句, 用于创建视图
/// </summary>
/// <param name="queryable"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static string ToMappedSqlString < T > ( this ISugarQueryable < T > queryable ) where T : class
{
ArgumentNullException . ThrowIfNull ( queryable ) ;
// 获取实体映射信息
2025-08-15 14:30:23 +08:00
var entityInfo = queryable . Context . EntityMaintenance . GetEntityInfo < T > ( ) ;
2025-05-19 02:08:46 +08:00
if ( entityInfo ? . Columns = = null | | entityInfo . Columns . Count = = 0 ) return queryable . ToSqlString ( ) ;
// 构建需要替换的字段名映射(只处理实际有差异的字段)
var nameMap = entityInfo . Columns
. Where ( c = > ! string . Equals ( c . PropertyName , c . DbColumnName , StringComparison . OrdinalIgnoreCase ) )
. ToDictionary ( k = > k . PropertyName . ToLower ( ) , v = > v . DbColumnName , StringComparer . OrdinalIgnoreCase ) ;
if ( nameMap . Count = = 0 ) return queryable . ToSqlString ( ) ;
// 预编译正则表达式提升性能
var sql = queryable . ToSqlString ( ) ;
foreach ( var kv in nameMap )
{
sql = Regex . Replace ( sql , $@"\b{kv.Key}\b" , kv . Value ? ? kv . Key , RegexOptions . IgnoreCase | RegexOptions . Compiled ) ; // 单词边界匹配
}
return sql ;
}
#endregion 视 图 操 作
2025-08-12 17:36:28 +08:00
2025-08-13 08:55:54 +08:00
#region 流 水 号 操 作
2025-08-13 10:57:54 +08:00
2025-08-13 08:55:54 +08:00
/// <summary>
2025-09-05 14:35:50 +08:00
/// 延迟初始化流水号服务实例
2025-08-13 08:55:54 +08:00
/// </summary>
2025-09-05 14:35:50 +08:00
private static readonly Lazy < SysSerialService > SerialService = new ( ( ) = > App . GetService < SysSerialService > ( ) ) ;
2025-08-13 08:55:54 +08:00
2025-09-05 00:45:32 +08:00
/// <summary>
2025-09-05 14:35:50 +08:00
/// 包含流水号特性的类型属性缓存表
2025-09-05 00:45:32 +08:00
/// </summary>
2025-09-05 14:35:50 +08:00
private static readonly ConcurrentDictionary < Type , Dictionary < string , ( PropertyInfo Prop , BindSerialAttribute Attr ) > ? > _serialPropCache = new ( ) ;
2025-09-05 00:45:32 +08:00
2025-08-13 08:55:54 +08:00
/// <summary>
/// 自动生成流水号到绑定字段
/// </summary>
/// <param name="entityInfo"></param>
public static void SetSerialProperty ( this DataFilterModel entityInfo )
{
// 仅处理新增操作
if ( entityInfo . OperationType is not DataFilterType . InsertByObject ) return ;
var entityValue = entityInfo . EntityValue ;
var entityType = entityValue . GetType ( ) ;
// 仅在目标属性值为空时触发自动生成
if ( entityValue . GetValue ( entityInfo . PropertyName ) ! = null ) return ;
// 获取或创建类型属性缓存(原子化操作避免竞争条件)
var propDict = _serialPropCache . GetOrAdd ( entityType , type = >
{
// 反射获取带[BindSerial]特性的属性,预存属性元数据
var props = type . GetProperties ( )
. Where ( p = > p . IsDefined ( typeof ( BindSerialAttribute ) ) )
. Select ( p = > ( p , p . GetCustomAttribute < BindSerialAttribute > ( ) ! ) )
. ToDictionary ( t = > t . p . Name , t = > ( t . p , t . Item2 ) ) ;
2025-08-15 14:30:23 +08:00
return props . Count ! = 0 ? props : null ;
2025-08-13 08:55:54 +08:00
} ) ;
// 无绑定属性或当前属性不匹配时提前结束
if ( propDict = = null | | ! propDict . TryGetValue ( entityInfo . PropertyName , out var propData ) ) return ;
// 解构预存的属性和特性信息
var ( _ , attribute ) = propData ;
// 使用线程安全的延迟初始化服务实例获取流水号
2025-09-05 14:35:50 +08:00
var serial = SerialService . Value
2025-09-05 00:45:32 +08:00
. NextSeqNo ( attribute . Type , attribute . IsGlobal )
2025-09-05 14:35:50 +08:00
. ConfigureAwait ( false )
2025-08-13 08:55:54 +08:00
. GetAwaiter ( )
. GetResult ( ) ;
entityInfo . SetValue ( serial ) ;
}
2025-08-13 10:57:54 +08:00
2025-08-13 08:55:54 +08:00
#endregion 流 水 号 操 作
2024-07-15 11:38:17 +08:00
}