YD_Event.Admin/YD_Event.Core/SqlSugar/SqlSugarExtensions.cs

262 lines
11 KiB
C#
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Reflection;
using YD_Event.Core.Const;
using YD_Event.Core.Entities;
using YD_Event.Core.Enum;
using YD_Event.Core.Options;
using Mapster;
using Newtonsoft.Json;
using Yitter.IdGenerator;
using Microsoft.Data.SqlClient;
namespace SqlSugar;
public static class SqlSugarExtensions
{
/// <summary>
/// 添加 SqlSugar 拓展(适用于单个数据库)
/// </summary>
/// <param name="services"></param>
/// <param name="config">数据库连接对象</param>
/// <param name="buildAction"></param>
/// <returns></returns>
public static IServiceCollection AddSqlSugar(this IServiceCollection services, ConnectionConfigExt config, Action<ISqlSugarClient> buildAction = default)
{
return services.AddSqlSugar(new List<ConnectionConfigExt>()
{
config
}, buildAction);
}
/// <summary>
/// 添加 SqlSugar 拓展(适用于多数据库)
/// </summary>
/// <param name="services"></param>
/// <param name="configs">数据库连接对象集合</param>
/// <param name="buildAction"></param>
/// <returns></returns>
public static IServiceCollection AddSqlSugar(this IServiceCollection services, List<ConnectionConfigExt> configs, Action<SqlSugarClient> buildAction = default)
{
SqlSugarScope sqlSugarScope = new SqlSugarScope(configs.Adapt<List<ConnectionConfig>>(), buildAction ?? Aop);
configs.ForEach(x =>
{
Init(sqlSugarScope, x);
});
services.AddSingleton<ISqlSugarClient>(sqlSugarScope);//使用单例注入
services.AddSingleton<ITenant>(sqlSugarScope);
services.AddUnitOfWork<SqlSugarUnitOfWork>();//事务
services.AddScoped(typeof(ISqlSugarRepository<>), typeof(SqlSugarRepository<>));//注入泛型仓储
return services;
}
/// <summary>
/// 初始化数据库
/// </summary>
/// <param name="client"></param>
/// <param name="config"></param>
static void Init(SqlSugarScope client, ConnectionConfigExt config)
{
if (!config.EnableInitDb)
{
return;
}
SqlSugarScopeProvider provider = client.GetConnectionScope(config.ConfigId);
//创建数据库
provider.DbMaintenance.CreateDatabase();
IEnumerable<Type> types = App.EffectiveTypes.Where(x => !x.IsInterface && x.IsClass && !x.IsAbstract && typeof(IEntity).IsAssignableFrom(x));
provider.CodeFirst.InitTables(types.ToArray());
//初始化数据
InitData(client);
}
/// <summary>
/// 添加 SqlSugar 服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddSqlSugar(this IServiceCollection services)
{
var options = App.GetOptions<DbConnectionOptions>();
if (string.Equals(Environment.GetEnvironmentVariable("YD_EVENT_ALLOW_UNENCRYPTED_SQL"), "1", StringComparison.Ordinal))
{
options.Connections.ForEach(connection =>
{
if (connection.DbType != DbType.SqlServer) return;
var builder = new SqlConnectionStringBuilder(connection.ConnectionString)
{
Encrypt = false,
TrustServerCertificate = true
};
connection.ConnectionString = builder.ConnectionString;
});
}
ICacheService ormCache = new SqlSugarCache();
options.Connections.ForEach(x =>
{
x.MoreSettings = new ConnMoreSettings()
{
//所有 增、删 、改 会自动调用.RemoveDataCache()
IsAutoRemoveDataCache = true
};
//配置缓存
x.ConfigureExternalServices = new ConfigureExternalServices()
{
DataInfoCacheService = ormCache,
EntityService = ((prop, column) =>
{
//如果实体不是主键,并且是可空类型,表列设置为可空(支持string?和string)
if (column.IsPrimarykey == false && new NullabilityInfoContext()
.Create(prop).WriteState is NullabilityState.Nullable)
{
column.IsNullable = true;
}
})
};
});
return AddSqlSugar(services, options.Connections, Aop);
}
/// <summary>
/// 拦截器
/// </summary>
private static readonly Action<SqlSugarClient> Aop = (db) =>
{
db.Aop.DataExecuting = (_, entityInfo) =>
{
try
{
switch (entityInfo.OperationType)
{
//执行insert时
case DataFilterType.InsertByObject:
//自动设置主键
if (entityInfo.EntityColumnInfo.IsPrimarykey &&
entityInfo.EntityValue is Entity<long> { Id: 0 } entity)
{
entity.Id = YitIdHelper.NextId();
}
// 创建审计字段只在调用方未赋值时补齐,保留数据迁移等场景的显式历史值。
if (entityInfo.PropertyName == nameof(ICreatedTime.CreatedTime) &&
entityInfo.EntityValue is ICreatedTime { CreatedTime: var createdAt } &&
createdAt == DateTime.MinValue)
{
entityInfo.SetValue(DateTime.Now);
}
if (entityInfo.PropertyName == nameof(ICreatedUserId.CreatedUserId) &&
entityInfo.EntityValue is ICreatedUserId { CreatedUserId: 0 } &&
TryGetCurrentUserId(out var createdUserId))
{
entityInfo.SetValue(createdUserId);
}
break;
case DataFilterType.UpdateByObject:
// 修改时间每次更新都刷新;有登录上下文时,修改人统一取当前操作者。
if (entityInfo.PropertyName == nameof(IUpdatedTime.UpdatedTime) &&
entityInfo.EntityValue is IUpdatedTime)
{
entityInfo.SetValue(DateTime.Now);
}
if (entityInfo.PropertyName == nameof(IUpdatedUserId.UpdatedUserId) &&
entityInfo.EntityValue is IUpdatedUserId updatedUser)
{
var claimValue = App.User?.FindFirst(AuthClaimsConst.AuthIdKey)?.Value;
var updatedUserId = ResolveUpdatedUserId(claimValue, updatedUser.UpdatedUserId);
if (updatedUserId.HasValue)
{
// 存在有效登录用户时始终覆盖显式值,确保记录最后一次实际操作人。
entityInfo.SetValue(updatedUserId.Value);
}
}
break;
case DataFilterType.DeleteByObject:
break;
}
}
catch (TargetInvocationException ex)
{
Console.WriteLine($"[Error] TargetInvocationException in DataExecuting: {ex.InnerException?.ToString() ?? ex.Message}");
throw ex.InnerException ?? ex;
}
catch (Exception ex)
{
Console.WriteLine($"[Error] Exception in DataExecuting: {ex.ToString()}");
throw;
}
};
// 文档地址:https://www.donet5.com/Home/Doc?typeId=1204
db.Aop.OnLogExecuting = (sql, parameters) =>
{
var originColor = Console.ForegroundColor;
if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
Console.ForegroundColor = ConsoleColor.Green;
if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
Console.ForegroundColor = ConsoleColor.Yellow;
if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase))
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("【" + DateTime.Now + "——执行SQL】\r\n" + UtilMethods.GetSqlString(db.CurrentConnectionConfig.DbType, sql, parameters) + "\r\n");
Console.ForegroundColor = originColor;
//App.PrintToMiniProfiler("SqlSugar", "Info", sql + "\r\n" + db.Utilities.SerializeObject(parameters.ToDictionary(it => it.ParameterName, it => it.Value)));
};
db.Aop.OnError = ex =>
{
if (ex.Parametres == null) return;
var originColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.DarkRed;
var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
Console.WriteLine("【" + DateTime.Now + "——错误SQL】\r\n" + UtilMethods.GetSqlString(db.CurrentConnectionConfig.DbType, ex.Sql, (SugarParameter[])ex.Parametres) + "\r\n");
Console.ForegroundColor = originColor;
// App.PrintToMiniProfiler("SqlSugar", "Error", $"{ex.Message}{Environment.NewLine}{ex.Sql}{pars}{Environment.NewLine}");
};
//配置逻辑删除过滤器(查询数据时过滤掉已被标记删除的数据)
db.QueryFilter.AddTableFilter<ISoftDelete>(it => it.DeleteMark == false);
};
private static bool TryGetCurrentUserId(out long userId)
{
var claimValue = App.User?.FindFirst(AuthClaimsConst.AuthIdKey)?.Value;
return long.TryParse(claimValue, out userId) && userId > 0;
}
/// <summary>
/// 有有效登录用户时以当前用户为准;无用户上下文时保留后台任务显式传入的操作者。
/// </summary>
internal static long? ResolveUpdatedUserId(string? currentUserClaim, long? originalUserId)
{
return long.TryParse(currentUserClaim, out var currentUserId) && currentUserId > 0
? currentUserId
: originalUserId;
}
/// <summary>
/// 初始化基础数据
/// </summary>
private static void InitData(SqlSugarScope client)
{
string path = Path.Combine(AppContext.BaseDirectory, "InitData");
var dir = new DirectoryInfo(path);
var files = dir.GetFiles("*.txt");
foreach (var file in files)
{
using var reader = file.OpenText();
string s = reader.ReadToEnd();
var table = JsonConvert.DeserializeObject<DataTable>(s);
if (table.Rows.Count == 0)
{
continue;
}
table.TableName = file.Name.Replace(".txt", "");
client.Storageable(table).WhereColumns("Id").ToStorage().AsInsertable.ExecuteCommand();
}
}
}