using System; using System.Collections; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Linq.Dynamic.Core; using System.Linq.Expressions; using System.Reflection; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.RegularExpressions; using System.Text.Unicode; using Hotline.Share.Tools; using Hotline.Users; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Serilog; using SqlSugar; using XF.Domain.Entities; using XF.Domain.Extensions; using XF.Domain.Options; using XF.Domain.Repository; using XF.Domain.Repository.Events; using XF.Utility.SequentialId; namespace Hotline.Repository.SqlSugar.Extensions { public static class SqlSugarStartupExtensions { public static void AddSqlSugar(this IServiceCollection services, IConfiguration configuration, string dbName = "Hotline") { //services.AddScoped(); //多租户 new SqlSugarScope(List,db=>{}); var slaveConnections = new List(); var slaveConnection1 = configuration.GetConnectionString("Hotline1"); if (!string.IsNullOrEmpty(slaveConnection1)) slaveConnections.Add(new SlaveConnectionConfig { ConnectionString = slaveConnection1 }); SqlSugarScope sqlSugar = new SqlSugarScope(new ConnectionConfig() { DbType = DbType.PostgreSQL, ConnectionString = configuration.GetConnectionString(dbName), SlaveConnectionConfigs = slaveConnections, IsAutoCloseConnection = true, ConfigureExternalServices = new ConfigureExternalServices { EntityService = (property, column) => { var attributes = property.GetCustomAttributes(true); //get all attributes //if (attributes.Any(it => it is KeyAttribute))// by attribute set primarykey //{ // column.IsPrimarykey = true; //有哪些特性可以看 1.2 特性明细 //} ////可以写多个,这边可以断点调试 //// if (attributes.Any(it => it is NotMappedAttribute)) ////{ //// column.IsIgnore= true; ////} //if (attributes.Any(it => it is DbNullableAttribute)) //{ // column.IsNullable = true; //} //if (attributes.Any(it => it is DbJsonAttribute)) //{ // column.DataType = "varchar(3000)"; // column.IsJson = true; //} //if (attributes.Any(it => it is DbLengthAttribute)) //{ // column.Length = (attributes.First(d => d is DbLengthAttribute) as DbLengthAttribute)?.MaxLength ?? 255; //} //if (attributes.Any(it => it is DbIgnoreAttribute)) //{ // column.IsIgnore = true; //} //if (property.PropertyType.IsGenericType && // property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) //{ // column.IsNullable = true; //} if (column.PropertyName.ToLower() == "id" || attributes.Any(it => it is KeyAttribute)) //是id的设为主键 { column.IsPrimarykey = true; column.Length = 36; } //if (!column.DbColumnName.Contains("_")) // column.DbColumnName = UtilMethods.ToUnderLine(column.DbColumnName);//ToUnderLine驼峰转下划线 //column.ColumnDescription = (attributes.FirstOrDefault(d => d is DescriptionAttribute) as DescriptionAttribute)?.Description ?? string.Empty; //统一设置 nullable等于isnullable=true if (!column.IsPrimarykey && new NullabilityInfoContext().Create(property).WriteState is NullabilityState.Nullable) { column.IsNullable = true; } }, EntityNameService = (type, entity) => { var attributes = type.GetCustomAttributes(true); //if (attributes.Any(it => it is TableAttribute)) //{ // entity.DbTableName = (attributes.First(it => it is TableAttribute) as TableAttribute).UserName; //} entity.DbTableName = entity.DbTableName.ToSnakeCase(); //if (!entity.DbTableName.Contains("_")) // entity.DbTableName = UtilMethods.ToUnderLine(entity.DbTableName);//ToUnderLine驼峰转下划线方法 if (attributes.Any(d => d is DescriptionAttribute)) { entity.TableDescription = (attributes.First(d => d is DescriptionAttribute) as DescriptionAttribute) .Description; } } }, MoreSettings = new ConnMoreSettings { PgSqlIsAutoToLower = false,//增删查改支持驼峰表 PgSqlIsAutoToLowerCodeFirst = false, // 建表建驼峰表。5.1.3.30 } }, db => { SetDbAop(db, services); db.QueryFilter.AddTableFilter(entity => !entity.IsDeleted); } ); ISugarUnitOfWork context = new SugarUnitOfWork(sqlSugar); services.AddSingleton(context); InitDatabase(context, configuration); } private static void InitDatabase(ISugarUnitOfWork context, IConfiguration configuration) { var dbOptions = configuration.GetSection("DatabaseConfiguration").Get() ?? new DatabaseOptions(); if (dbOptions.ApplyDbMigrations) { context.Db.DbMaintenance.CreateDatabase(); var types = typeof(User).Assembly.GetTypes() .Where(d => d.GetInterfaces().Any(x => x == typeof(ITable) && !d.IsAbstract)) .Distinct() .ToArray(); context.Db.CodeFirst.InitTables(types);//根据types创建表 } if (dbOptions.ApplySeed) { var allTypes = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(d => d.GetTypes()); var seedDataTypes = allTypes.Where(d => !d.IsInterface && !d.IsAbstract && d.IsClass && d.HasImplementedOf(typeof(ISeedData<>))); foreach (var seedType in seedDataTypes) { var instance = Activator.CreateInstance(seedType); var hasDataMethod = seedType.GetMethod("HasData"); var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast(); if (seedData == null) continue; var entityType = seedType.GetInterfaces().First().GetGenericArguments().First(); var entityInfo = context.Db.EntityMaintenance.GetEntityInfo(entityType); if (entityInfo.Columns.Any(d => d.IsPrimarykey)) { var storage = context.Db.StorageableByObject(seedData.ToList()).ToStorage(); storage.AsInsertable.ExecuteCommand(); } else { // 无主键则只进行插入 if (!context.Db.Queryable(entityInfo.DbTableName, entityInfo.DbTableName).Any()) context.Db.InsertableByObject(seedData.ToList()).ExecuteCommand(); } } ////持久化需要持久化的runtime常量表 //var constTypes = allTypes.Where(d=> !d.IsInterface && !d.IsAbstract && d.IsClass // && d.HasImplementedOf(typeof(IConstTable<>))); //foreach (var constType in constTypes) //{ // var instance = Activator.CreateInstance(constType); // var hasDataMethod = constType.GetMethod("GetData"); // var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast(); // if (seedData == null) continue; // var entityType = constType.GetInterfaces().First().GetGenericArguments().First(); // var entityInfo = context.Db.EntityMaintenance.GetEntityInfo(entityType); // if (entityInfo.Columns.Any(d => d.IsPrimarykey)) // { // var storage = context.Db.StorageableByObject(seedData.ToList()).ToStorage(); // storage.AsInsertable.ExecuteCommand(); // } // else // { // // 无主键则只进行插入 // if (!context.Db.Queryable(entityInfo.DbTableName, entityInfo.DbTableName).Any()) // context.Db.InsertableByObject(seedData.ToList()).ExecuteCommand(); // } //} } } #region private private static void SetDbAop(SqlSugarClient db, IServiceCollection services) { /***写AOP等方法***/ db.Aop.OnLogExecuting = (sql, pars) => { //Log.Warning($"sql参数:{JsonSerializer.Serialize(db.Ado.SqlStackTrace, new JsonSerializerOptions { Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs) })}"); ////获取原生SQL推荐 5.1.4.63 性能OK //Log.Information(UtilMethods.GetNativeSql(sql, pars)); //Log.Information("Sql: {0}", sql); //Log.Information("SqlParameters: {0}", string.Join(',', pars.Select(d => d.Value))); }; db.Aop.OnError = (exp) =>//SQL报错 { //获取原生SQL推荐 5.1.4.63 性能OK Log.Error(UtilMethods.GetNativeSql(exp.Sql, (SugarParameter[])exp.Parametres)); //Log.Error(exp.InnerException.Message); //5.0.8.2 获取无参数化 SQL 对性能有影响,特别大的SQL参数多的,调试使用 //UtilMethods.GetSqlString(DbType.SqlServer, exp.sql, exp.parameters) }; //db.Aop.OnExecutingChangeSql = (sql, pars) => //可以修改SQL和参数的值 //{ // //sql=newsql // //foreach (var p in pars) //修改 // //{ // //} // return new KeyValuePair(sql, pars); //}; db.Aop.OnLogExecuted = (sql, p) => { //执行时间超过1秒 if (db.Ado.SqlExecutionTime.TotalSeconds > 1) { //代码CS文件名 var fileName = db.Ado.SqlStackTrace.FirstFileName; //代码行数 var fileLine = db.Ado.SqlStackTrace.FirstLine; //方法名 var FirstMethodName = db.Ado.SqlStackTrace.FirstMethodName; //db.Ado.SqlStackTrace.MyStackTraceList[1].xxx 获取上层方法的信息 Log.Warning("slow query ==> fileName: {fileName}, fileLine: {fileLine}, FirstMethodName: {FirstMethodName}", fileName, fileLine, FirstMethodName); Log.Warning(UtilMethods.GetNativeSql(sql, p)); Log.Warning("slow query totalSeconds: {sec}", db.Ado.SqlExecutionTime.TotalSeconds); } }; db.Aop.DataChangesExecuted = (sql, entityInfo) => { if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.OperationType == DataFilterType.UpdateByObject) { services.BuildServiceProvider() .GetService()? .Dispatch(entityInfo.EntityValue, entityInfo.OperationType, false); } if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.OperationType == DataFilterType.InsertByObject) { services.BuildServiceProvider() .GetService()? .Dispatch(entityInfo.EntityValue, entityInfo.OperationType, false); } }; db.Aop.DataExecuting = (oldValue, entityInfo) => { //inset生效 if (entityInfo.PropertyName == "CreationTime" && entityInfo.OperationType == DataFilterType.InsertByObject) { if (oldValue is DateTime createTime) { if (createTime == DateTime.MinValue) { entityInfo.SetValue(DateTime.Now);//修改CreateTime字段 //entityInfo有字段所有参数 } } } //update生效 else if (entityInfo.PropertyName == "LastModificationTime" && entityInfo.OperationType == DataFilterType.UpdateByObject) { entityInfo.SetValue(DateTime.Now);//修改UpdateTime字段 } //根据当前列修改另一列 可以么写 //if(当前列逻辑==XXX) //var properyDate = entityInfo.EntityValue.GetType().GetProperty("Date"); //if(properyDate!=null) //properyDate.SetValue(entityInfo.EntityValue,1); else if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.EntityColumnInfo.PropertyName.ToLower() == "id" && entityInfo.OperationType == DataFilterType.InsertByObject && oldValue is null) //通过主键保证只进一次事件 { //var propertyId = entityInfo.EntityValue.GetType().GetProperty("Id"); //if (propertyId is not null) //{ // var idValue = propertyId.GetValue(entityInfo.EntityValue); // if (idValue is null) // //这样每条记录就只执行一次 // entityInfo.SetValue(SequentialStringGenerator.Create()); //} entityInfo.SetValue(SequentialStringGenerator.Create()); } }; //SetDeletedEntityFilter(db); } //private static void SetDeletedEntityFilter(SqlSugarClient db) //{ // var cacheKey = $"DbFilter:{db.CurrentConnectionConfig.ConfigId}:IsDeleted"; // var tableFilterItemList = db.DataCache.Get>>(cacheKey); // if (tableFilterItemList == null) // { // // 获取基类实体数据表 // var entityTypes = AppDomain.CurrentDomain.GetAssemblies() // .SelectMany(d => d.GetTypes()) // .Where(d => !d.IsInterface // && !d.IsAbstract // && d.IsClass // && d.GetInterfaces().Any(x => x == typeof(ISoftDelete))); // if (!entityTypes.Any()) return; // var tableFilterItems = new List>(); // foreach (var entityType in entityTypes) // { // if (entityType.GetProperty("IsDeleted") is null) continue; // //// 排除非当前数据库实体 // //var tAtt = entityType.GetCustomAttribute(); // //if ((tAtt != null && (string)db.CurrentConnectionConfig.ConfigId != tAtt.configId.ToString()) || // // (tAtt == null && (string)db.CurrentConnectionConfig.ConfigId != SqlSugarConst.ConfigId)) // // continue; // var lambda = DynamicExpressionParser.ParseLambda(new[] { // Expression.Parameter(entityType, "d") }, // typeof(bool), // $"{nameof(SoftDeleteEntity.IsDeleted)} == @0", false); // var tableFilterItem = new TableFilterItem(entityType, lambda); // tableFilterItems.Add(tableFilterItem); // db.QueryFilter.Add(tableFilterItem); // } // db.DataCache.Add(cacheKey, tableFilterItems); // } // else // { // tableFilterItemList.ForEach(u => // { // db.QueryFilter.Add(u); // }); // } //} #endregion } }