|
@@ -0,0 +1,260 @@
|
|
|
+using System.Collections;
|
|
|
+using System.ComponentModel;
|
|
|
+using System.ComponentModel.DataAnnotations;
|
|
|
+using System.Reflection;
|
|
|
+using Hotline.Users;
|
|
|
+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.Utility.SequentialId;
|
|
|
+
|
|
|
+namespace Hotline.Repository.SqlSugar.Extensions
|
|
|
+{
|
|
|
+ public static class SqlSugarStartupExtensions
|
|
|
+ {
|
|
|
+ public static void AddSqlSugar(this IServiceCollection services, IConfiguration configuration, string dbName = "Hotline")
|
|
|
+ {
|
|
|
+ //多租户 new SqlSugarScope(List<ConnectionConfig>,db=>{});
|
|
|
+
|
|
|
+ SqlSugarScope sqlSugar = new SqlSugarScope(new ConnectionConfig()
|
|
|
+ {
|
|
|
+ DbType = DbType.MySql,
|
|
|
+ ConnectionString = configuration.GetConnectionString(dbName),
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ //column.ColumnDescription = (attributes.FirstOrDefault(d => d is DescriptionAttribute) as DescriptionAttribute)?.Description ?? string.Empty;
|
|
|
+ },
|
|
|
+ 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 (attributes.Any(d => d is DescriptionAttribute))
|
|
|
+ {
|
|
|
+ entity.TableDescription =
|
|
|
+ (attributes.First(d => d is DescriptionAttribute) as DescriptionAttribute)
|
|
|
+ .Description;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ SetDbAop
|
|
|
+ );
|
|
|
+ ISugarUnitOfWork<HotlineDbContext> context = new SugarUnitOfWork<HotlineDbContext>(sqlSugar);
|
|
|
+
|
|
|
+ context.Db.QueryFilter.Add(new SqlFilterItem
|
|
|
+ {
|
|
|
+ FilterValue = d => new SqlFilterResult { Sql = " IsDeleted=0 " },
|
|
|
+ IsJoinQuery = true
|
|
|
+ });
|
|
|
+ ////全局过滤
|
|
|
+ //var deletionTypes = AppDomain.CurrentDomain.GetAssemblies().ToList()
|
|
|
+ // .SelectMany(d=>d.GetTypes()).Where(d=>d.GetInterfaces()/*.Any(x=>x == typeof(ISoftDelete))*/);//todo 找出即实现ISoftDelete 又实现IEntity
|
|
|
+ //foreach (var deletionType in deletionTypes)
|
|
|
+ //{
|
|
|
+
|
|
|
+ //}
|
|
|
+
|
|
|
+ InitDatabase(context, configuration);
|
|
|
+
|
|
|
+ services.AddSingleton(context);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void InitDatabase(ISugarUnitOfWork<HotlineDbContext> context, IConfiguration configuration)
|
|
|
+ {
|
|
|
+ context.Db.DbMaintenance.CreateDatabase();
|
|
|
+
|
|
|
+ var dbOptions = configuration.GetSection("DatabaseConfiguration").Get<DatabaseOptions>() ?? new DatabaseOptions();
|
|
|
+ if (dbOptions.ApplyDbMigrations)
|
|
|
+ {
|
|
|
+ var types = typeof(User).Assembly.GetTypes()
|
|
|
+ .Where(d => d.GetInterfaces().Any(x => x == typeof(ITable)))
|
|
|
+ .Distinct()
|
|
|
+ .ToArray();
|
|
|
+
|
|
|
+ context.Db.CodeFirst.InitTables(types);//根据types创建表
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dbOptions.ApplySeed)
|
|
|
+ {
|
|
|
+ var seedDataTypes = AppDomain.CurrentDomain.GetAssemblies()
|
|
|
+ .SelectMany(d => d.GetTypes())
|
|
|
+ .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<object>();
|
|
|
+ if (seedData == null) continue;
|
|
|
+
|
|
|
+ var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
|
|
|
+ var tableName = context.Db.EntityMaintenance.GetTableName(entityType);
|
|
|
+
|
|
|
+ var seedDataTable = seedData.ToList().ToDataTable(tableName);
|
|
|
+
|
|
|
+ if (seedDataTable.Columns.Contains(SqlSugarConst.PrimaryKey))
|
|
|
+ {
|
|
|
+ var storage = context.Db.Storageable(seedDataTable)
|
|
|
+ //.SplitInsert(d => !d.Any())
|
|
|
+ .WhereColumns(SqlSugarConst.PrimaryKey).ToStorage();
|
|
|
+ storage.AsInsertable.ExecuteCommand();
|
|
|
+ //var ignoreUpdate = hasDataMethod.GetCustomAttribute<IgnoreUpdateAttribute>();
|
|
|
+ //if (ignoreUpdate == null) storage.AsUpdateable.ExecuteCommand();
|
|
|
+ }
|
|
|
+ else // 没有主键或者不是预定义的主键(有重复的可能)
|
|
|
+ {
|
|
|
+ var storage = context.Db.Storageable(seedDataTable)
|
|
|
+ .SplitDelete(d=>true)
|
|
|
+ .ToStorage();
|
|
|
+ storage.AsInsertable.ExecuteCommand();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #region private
|
|
|
+
|
|
|
+ private static void SetDbAop(SqlSugarClient db)
|
|
|
+ {
|
|
|
+ /***写AOP等方法***/
|
|
|
+ db.Aop.OnLogExecuting = (sql, pars) =>
|
|
|
+ {
|
|
|
+ //Log.Information(sql);
|
|
|
+ };
|
|
|
+ db.Aop.OnError = (exp) =>//SQL报错
|
|
|
+ {
|
|
|
+ //exp.sql 这样可以拿到错误SQL,性能无影响拿到ORM带参数使用的SQL
|
|
|
+ Log.Error("SqlError: {0}", exp.Sql);
|
|
|
+
|
|
|
+ //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<string, SugarParameter[]>(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);
|
|
|
+ }
|
|
|
+ //相当于EF的 PrintToMiniProfiler
|
|
|
+ };
|
|
|
+
|
|
|
+ db.Aop.DataExecuting = (oldValue, entityInfo) =>
|
|
|
+ {
|
|
|
+ //inset生效
|
|
|
+ if (entityInfo.PropertyName == "CreationTime" && entityInfo.OperationType == DataFilterType.InsertByObject)
|
|
|
+ {
|
|
|
+ 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) //通过主键保证只进一次事件
|
|
|
+ {
|
|
|
+ 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());
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+ }
|
|
|
+
|
|
|
+ public class SqlSugarConst
|
|
|
+ {
|
|
|
+ public static string PrimaryKey = "Id";
|
|
|
+ }
|
|
|
+}
|