using APT.Infrastructure.Core; using APT.Infrastructure.Utility; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.Logging; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Data; using System.Data.Common; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.InteropServices.ComTypes; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Linq.Dynamic.Core; using System.Collections.Concurrent; using APT.Infrastructure.EF.Infrastructure; using APT.Infrastructure.Api; using System.Text.RegularExpressions; using Microsoft.Data.SqlClient; //using Npgsql; using APT.Infrastructure.Core.Refctor; using System.ComponentModel; namespace APT.Infrastructure.EF { public class EfDbContext : TenantBaseDbContext, IUnitOfWork, IDependency { private readonly string namedConnection; private const string dbsRedisKey = "DbConn_RedisKey"; private static IConnectionResolver _connectionResolver; private static IConnectionResolver ConnectionResolver { get { if (_connectionResolver == null) { ServiceLocator serviceLocator = ServiceLocator.Instance; _connectionResolver = serviceLocator.GetService(); } return _connectionResolver; } } //public static Action SQL_LOG { get; set; } int _TranscationLocker = 0; IDbContextTransaction _dbContextTransaction; private static EFModel _efModel; #region ctor. public EfDbContext() : base() { } /// /// 初始化一个类型的新实例 /// // // 使用连接名称或连接字符串 初始化一个 类型的新实例 // public EfDbContext(string nameOrConnectionString) : base() { this.namedConnection = nameOrConnectionString; } public TenantInfo TenantInfo => tenantInfo; private readonly TenantInfo tenantInfo; public EfDbContext(DbContextOptions options, TenantInfo tenant, IServiceProvider serviceProvider) : base(options, tenant, serviceProvider) { this.tenantInfo = tenant; } #endregion #region IUnitOfWork 成员 public string ConnectionString { get { return ConfigurationManager.ConnectionStrings[namedConnection] ?? namedConnection; } } public int ExecuteSqlCommand(string sql, params object[] parameters) { return ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, sql, parameters); } public int ExecuteSqlCommand(TransactionalBehavior transactionalBehavior, string sql, params object[] parameters) { return Database.ExecuteSqlRaw(sql, parameters); } public void ExecuteReader(string sql, ReaderColumn[] readerColumns, Action readerAction) { ExecuteReader(sql, readerColumns, null, readerAction); } public void ExecuteReader(string sql, ReaderColumn[] readerColumns, DbParameter[] parameters, Action readerAction) { ExecuteReader(CommandType.Text, sql, readerColumns, parameters, readerAction); } public void ExecuteReader(CommandType commandType, string sql, ReaderColumn[] readerColumns, DbParameter[] parameters, Action readerAction) { var concurrencyDetector = Database.GetService(); using (concurrencyDetector.EnterCriticalSection()) { IReadOnlyDictionary paramValues = null; IRelationalCommand relationalCommand = GetRelationComman(sql, parameters, out paramValues); var paramObject = new RelationalCommandParameterObject( Database.GetService(), paramValues, readerColumns, this, null ); var relR = relationalCommand .ExecuteReader( paramObject); if (relR != null && readerAction != null) readerAction(relR.DbDataReader); } } public int ExecuteNonQuery(CommandType commandType, string sql, ReaderColumn[] readerColumns, DbParameter[] parameters) { var concurrencyDetector = Database.GetService(); using (concurrencyDetector.EnterCriticalSection()) { IReadOnlyDictionary paramValues = null; IRelationalCommand relationalCommand = GetRelationComman(sql, parameters, out paramValues); var paramObject = new RelationalCommandParameterObject( Database.GetService(), paramValues, readerColumns, this, null ); var dr = relationalCommand .ExecuteNonQuery(paramObject); return dr; } } private IRelationalCommand GetRelationComman(string sql, object[] parameters, out IReadOnlyDictionary paramValues) { paramValues = null; IRelationalCommand relationalCommand = null; if (parameters != null && parameters.Any()) { var rawSqlCommand = Database .GetService() .Build(sql, parameters); relationalCommand = rawSqlCommand .RelationalCommand; paramValues = rawSqlCommand.ParameterValues; } else { relationalCommand = Database .GetService() .Build(sql); } return relationalCommand; } public IEnumerable SqlQuery(Type elementType, string sql, ReaderColumn[] readCloums, params object[] parameters) { var concurrencyDetector = Database.GetService(); using (concurrencyDetector.EnterCriticalSection()) { IReadOnlyDictionary paramValues = null; IRelationalCommand relationalCommand = GetRelationComman(sql, parameters, out paramValues); var paramObject = new RelationalCommandParameterObject( Database.GetService(), paramValues, readCloums, this, null ); var dr = relationalCommand .ExecuteReader(paramObject); return dr.DbDataReader; } } #region GetTreeOrderEntities public IEnumerable> GetTreeOrderEntities(Expression> expression, BaseFilter filter, Expression> orgExpress, out List resultList, bool isTracking = true, params string[] paths) where T : TreeEntityBase, new() { List allData = new List(); //List data = new List(); if (filter == null) { filter = new BaseFilter(); } var selectFields = filter.SelectField; if (selectFields.Any()) { if (!string.IsNullOrEmpty(filter.Sort)) { if (!selectFields.Contains(filter.Sort)) selectFields = selectFields.Append(filter.Sort).ToList(); } else if (filter.Orders != null && filter.Orders.Any()) { foreach (var o in filter.Orders) { if (!selectFields.Contains(o.Field)) selectFields = selectFields.Append(o.Field).ToList(); } } selectFields = selectFields.Append("ORG_ID").ToList(); } if (filter.IsParentData) { if (selectFields.Any() && !selectFields.Contains("ORG_ID")) selectFields = selectFields.Append("ORG_ID").ToList(); allData = this.GetEntities(orgExpress, selectFields.ToArray(), isTracking, paths).ToList(); resultList = allData.AsQueryable().Where(expression).ToList(); var dataIds = resultList.Select(i => i.ID).ToArray(); AddParentData(allData, resultList, dataIds); } else { resultList = this.GetEntities(expression, selectFields.ToArray(), isTracking, paths).ToList(); } //有条件,需要再次查询,查询结果的父级 var ids = resultList.Select(i => i.ID).ToArray(); var parentData = resultList.Where(i => !ids.Contains(i.PARENT_ID ?? Guid.Empty) || i.PARENT_ID == null).ToList(); List> treeNodes = new List>(); foreach (var d in parentData) { var node = new TreeNode(); node.Node = d; node.Children = new List>(); node.Level = filter.Level < 0 ? 0 : filter.Level; node.IsLeaf = filter.Level < 0 ? true : d.IS_LEAF; if (filter.Level < 0)//<0 取全部数据,>=0 表示异步取数 { var childNodes = resultList.Where(i => i.PARENT_ID == d.ID).ToList(); if (childNodes.Any()) { node.IsLeaf = false; InitFullTree(resultList, childNodes, node, node.Level + 1); } } treeNodes.Add(node); } ReSort(treeNodes, filter); return treeNodes; } private static void InitFullTree(List data, List childNodeList, TreeNode theNode, int level) where T : TreeEntityBase, new() { foreach (var d in childNodeList) { var node = new TreeNode(); node.Node = d; node.Children = new List>(); node.Level = level; node.IsLeaf = true; var childNodes = data.Where(i => i.PARENT_ID == d.ID).ToList(); if (childNodes.Any()) { node.IsLeaf = false; InitFullTree(data, childNodes, node, level + 1); } theNode.Children.Add(node); } } private static void AddParentData(List allData, List data, Guid[] dataIds) where T : TreeEntityBase, new() { var emptyDataIds = data.Where(i => i.PARENT_ID != null && !dataIds.Contains(i.PARENT_ID ?? Guid.Empty)).Select(i => i.PARENT_ID ?? Guid.Empty).ToArray(); if (emptyDataIds.Any()) { var emptyData = allData.Where(i => emptyDataIds.Contains(i.ID)); if (emptyData.Any()) { data.AddRange(emptyData); dataIds = data.Select(i => i.ID).ToArray(); AddParentData(allData, data, dataIds); } else { return; } } return; } private static void ReSort(List> treeNodes, BaseFilter filter) where T : TreeEntityBase, new() { SortBy(filter, treeNodes); foreach (var node in treeNodes) { if (node.Children != null && node.Children.Any()) { ReSort(node.Children, filter); } } } private static IList SortBy(BaseFilter filter, IList parentData) where T : TreeEntityBase, new() { if (!string.IsNullOrEmpty(filter.Sort)) { if (filter.Order == DbOrder.ASC) parentData = parentData.AsQueryable().OrderBy(filter.Sort + " asc").ToList(); else parentData = parentData.AsQueryable().OrderBy(filter.Sort + " desc").ToList(); } else { if (filter.Orders != null && filter.Orders.Any()) { var sortSql = ""; foreach (var o in filter.Orders) { sortSql += o.Field + (o.Order == DbOrder.ASC ? " asc," : " desc,"); } sortSql = sortSql.TrimEnd(','); parentData = parentData.AsQueryable().OrderBy(sortSql).ToList(); } } return parentData; } private static IList> SortBy(BaseFilter filter, List> treeNodes) where T : TreeEntityBase, new() { if (!string.IsNullOrEmpty(filter.Sort)) { if (filter.Order == DbOrder.ASC) { var orderyNodes = treeNodes.AsQueryable().OrderBy("Node." + filter.Sort + " asc").ToList(); treeNodes.Clear(); treeNodes.AddRange(orderyNodes); } else { var orderyNodes = treeNodes.AsQueryable().OrderBy("Node." + filter.Sort + " desc").ToList(); treeNodes.Clear(); treeNodes.AddRange(orderyNodes); } } else { if (filter.Orders != null && filter.Orders.Any()) { var sortSql = ""; foreach (var o in filter.Orders) { sortSql += "Node." + o.Field + (o.Order == DbOrder.ASC ? " asc," : " desc,"); } sortSql = sortSql.TrimEnd(','); var orderyNodes = treeNodes.AsQueryable().OrderBy(sortSql).ToList(); treeNodes.Clear(); treeNodes.AddRange(orderyNodes); } } return treeNodes; } #endregion public override int SaveChanges() { var entities = from e in ChangeTracker.Entries() where e.State == EntityState.Added || e.State == EntityState.Modified select e.Entity; var delEntities = from e in ChangeTracker.Entries() where e.State == EntityState.Deleted select e.Entity; List typeNames = new List(); Dictionary DicInitTree = new Dictionary(); var isReInitTree = false; foreach (var entity in entities) { var validationContext = new ValidationContext(entity); var dbExceptions = new List(); try { Validator.ValidateObject(entity, validationContext); } catch (ValidationException dbEx) { var errorBuilder = new StringBuilder(); errorBuilder.AppendLine(dbEx.Message); } if (dbExceptions.Count > 0) { var errorBuilder = new StringBuilder(); dbExceptions.ForEach(ve => errorBuilder.AppendLine(ve.Message)); throw new DomainException(errorBuilder.ToString()); } var type = entity.GetType(); if (!typeNames.Contains(type.Name)) { typeNames.Add(type.Name); if (type.BaseType.Name.StartsWith("TreeEntityBase`1") || type.BaseType.Name.StartsWith("MesTreeEntityBase")) { isReInitTree = true; var orgId = type.GetProperty("ORG_ID").GetValue(entity); if (orgId == null) orgId = Guid.Empty; DicInitTree.TryAdd(type, Guid.Parse(orgId.ToString())); } } } foreach (var entity in delEntities) { var type = entity.GetType(); if (!typeNames.Contains(type.Name)) { typeNames.Add(type.Name); if (entity is MesTreeEntityBase) { isReInitTree = true; DicInitTree.TryAdd(type, Guid.Parse(type.GetProperty("ORG_ID").GetValue(entity).ToString())); } } } try { var intResult = base.SaveChanges(); if (isReInitTree) { foreach (var dic in DicInitTree) { MethodInfo methodInfo = this.GetType().GetMethod("InitTreeData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static); methodInfo.MakeGenericMethod(new Type[] { dic.Key }). Invoke(this, new object[] { dic.Value }); } var ret = base.SaveChanges(); } return intResult; } catch (DbUpdateException dbEx) { HandleDbUpdateException(dbEx); return 0; } catch (Exception ex) { throw ex; } } /// /// 统一处理数据库报的异常 /// /// DbUpdateException public void HandleDbUpdateException(DbUpdateException ex) { var dbType = this.DBType(); if (ex.Entries.Count == 1) { var entries = ex.Entries.FirstOrDefault(); var data = entries.Entity; ExcetionTrow(ex, dbType, data); } else { if (dbType == DataBaseType.Postgresql) { //var errMsg = ex.InnerException != null ? ex.InnerException.Message : ex.Message; //if (ex.GetBaseException() is NpgsqlException pgException) //{ // var p = pgException.GetType().GetProperty("Statement"); // if (p != null) // { // object stateMent = p.GetValue(pgException); // var parameterProp = p.PropertyType.GetProperty("InputParameters"); // if (parameterProp != null) // { // var parameters = parameterProp.GetValue(stateMent) as List; // if (parameters != null && parameters.Any()) // { // var idstr = parameters.FirstOrDefault().NpgsqlValue.ToString(); // foreach (var e in ex.Entries) // { // var entity = e.Entity; // if (entity.GetType().GetProperty("ID").GetValue(entity).ToString() == idstr) // { // ExcetionTrow(ex, dbType, entity); // break; // } // } // } // } // throw new DomainException(errMsg); // } //} } throw new DomainException(ex.InnerException != null ? ex.InnerException.Message : ex.Message); } } private void ExcetionTrow(DbUpdateException ex, DataBaseType dbType, object data) { var tableName = data.GetType().Name; //var keys = GetModelForeignKey(tableName); //获取外键信息 var id = data.GetType().GetProperty("ID").GetValue(data); if (dbType == DataBaseType.Oracle) { } else if (dbType == DataBaseType.MySQL) { } //else if (dbType == DataBaseType.Postgresql) //{ // if (ex.GetBaseException() is NpgsqlException pgException) // { // var code = pgException.Data["SqlState"].ToString(); // var type = ReflectHelper.FindTypeInCurrentDomain(tableName); // var props = type.GetProperties(); // switch (code) // { // //case 547: // // //外键报错 // // //errMsg = ForeignKeyErrorFormatter(sqlException); // // break; // //case 2601: // case "23505": // if (type != null) // { // //唯一约束报错 // //var tableName = pgException.Data["TableName"].ToString(); // var constraintName = pgException.Data["ConstraintName"].ToString(); // var clomsMsg = "存在重复键值数据!"; // if (constraintName.StartsWith("PK"))//主键 // { // clomsMsg = "主键重复。"; // } // else // { // var startIndex = constraintName.IndexOf(tableName); // if (startIndex >= 0) // { // var temp = constraintName.Substring(startIndex + tableName.Length).Trim('_'); // //var cloums = temp.Split(new char[] { '_' }, StringSplitOptions.RemoveEmptyEntries); // bool check = true; // while (check && temp.Length > 0) // { // check = false; // foreach (var c in props) // { // if (c.Name == "ID") // continue; // if (temp.StartsWith(c.Name)) // { // //var fKey = keys.FirstOrDefault(i => i.ForeignFieldName == c.Name); // //if (fKey == null) // //{ // clomsMsg = GetProMsgVal(data, clomsMsg, c); // //} // //else // //{ // // var navName = fKey.ForeignNavName; // // var navPro = props.FirstOrDefault(i => i.Name == navName); // // if (navPro != null) // // { // // clomsMsg = GetProMsgVal(data, clomsMsg, navPro); // // } // //} // temp = temp.Substring(c.Name.Length).Trim('_'); // check = true; // break; // } // } // } // } // } // clomsMsg = clomsMsg + "[ID]:" + id; // throw new DomainException(clomsMsg); // } // break; // case "23503": // if (type != null) // { // var constraintName = pgException.Data["ConstraintName"].ToString(); // var clomsMsg = "外键字段数据异常!"; // if (constraintName.StartsWith("FK"))//外键FK_T_BD_ENERGY_TYPE_T_FM_ENUM_ITEM_UNIT_ENUM_ITEM_ID // { // var tmp = constraintName.Replace("FK_", "").Replace(tableName + "_", ""); // var keys = GetModelForeignKey(tableName); // if (keys != null && keys.Any()) // { // foreach (var key in keys) // { // tmp = tmp.Replace(key.ForeignTableName + "_", ""); // } // //if (tmp.Length > 0) // //{ // // clomsMsg += "[" + tmp + "]"; // //} // var fkProp = data.GetType().GetProperty(tmp); // if (fkProp != null) // { // clomsMsg = GetProMsgVal(data, clomsMsg, fkProp); // } // else // { // clomsMsg += ex.InnerException.Message; // } // clomsMsg = clomsMsg.TrimEnd(','); // throw new DomainException(clomsMsg); // } // } // } // break; // case "22001": // { // var msg = pgException.Message + "."; // foreach (var c in props) // { // var lengthAttribute = c.GetAttribute(); // if (lengthAttribute != null) // { // var val = c.GetValue(data); // if (val != null && lengthAttribute.Length < val.ToString().Length) // msg += $"[{c.Name}]:{val},"; // } // } // msg = msg + "[ID]:" + id; // throw new DomainException(msg); // } // break; // default: // //自定义报错 // //errMsg = CustomErrorFormatter(sqlException); // break; // } // } //} else if (dbType == DataBaseType.SQL) { if (ex.GetBaseException() is SqlException sqlException) { var errMsg = ""; switch (sqlException.Number) { case 547: //外键报错 errMsg = sqlException.Message; break; case 2601: case 2627: //唯一约束报错 errMsg = sqlException.Message; break; case 50000: //自定义报错 errMsg = CustomErrorFormatter(sqlException); break; } errMsg = errMsg + "[ID]:" + id; throw new DomainException(errMsg); } } var tmsg = ex.InnerException != null ? ex.InnerException.Message : ex.Message; tmsg = tmsg + "[ID]:" + id; throw new DomainException(tmsg); } private static string GetProMsgVal(object data, string clomsMsg, PropertyInfo c) { var desc = c.GetAttribute(); if (desc != null) { clomsMsg += "[" + desc.Description + "]:" + c.GetValue(data) + ","; } else { clomsMsg += "[" + c.PropertyType.Name + "]:" + c.GetValue(data) + ","; } return clomsMsg; } //报错信息样例:Cannot insert duplicate key row in object 'dbo.TableName' with unique index 'IX_TableName_FieldName'. The duplicate key value is (XXX). private readonly Regex UniqueConstraintRegex_Table = new Regex(@"'dbo.(\w*)'", RegexOptions.Compiled); public string UniqueErrorFormatter(SqlException ex) { var message = ex.Errors[0]?.Message; //利用正则表达式匹配到表名 var tableMatch = UniqueConstraintRegex_Table.Match(message); if (!tableMatch.Success) { return null; } var tableName = tableMatch?.Groups[1]?.Value; //默认情况下,EF Core生成的唯一索引的命名是有规律的(如果是自定义的,也必须有规律),这样就用样可以通过正则表达式匹配到外键对应的字段 Regex UniqueConstraintRegex = new Regex("'IX_" + tableName + @"_(\w*)'", RegexOptions.Compiled); var columnMatch = UniqueConstraintRegex.Match(message); if (!columnMatch.Success) { UniqueConstraintRegex = new Regex("'AK_" + tableName + @"_(\w*)'", RegexOptions.Compiled); columnMatch = UniqueConstraintRegex.Match(message); if (!columnMatch.Success) { return null; } } var columnName = columnMatch?.Groups[1]?.Value; //外键报错末尾是“The duplicate key value is (XXX).”,可以再次通过正则表达式匹配到重复数据 var dupPart = ""; var openingBadValue = message.IndexOf("(", StringComparison.Ordinal); if (openingBadValue > 0) { dupPart = message.Substring(openingBadValue + 1, message.Length - openingBadValue - 3); } //这样就解析出了报错信息,具体怎么组合就看需求了 return "存在重复的键,字段:" + columnName + ",值:" + dupPart; } private readonly Regex ForeignKeyRegex_Table = new Regex(@"table ""dbo.(\w*)""", RegexOptions.Compiled); private readonly Regex ForeignKeyRegex_Column = new Regex(@"column '(\w*)'", RegexOptions.Compiled); //报错样例:The INSERT statement conflicted with the FOREIGN KEY constraint "FK_XXX". The conflict occurred in database "DBName", table "dbo.TableName", column 'ColumnName'. private readonly string Operation_Insert = "INSERT"; private readonly string Operation_Merge = "MERGE"; private readonly string Operation_Update = "UPDATE"; private readonly string Operation_Delete = "DELETE"; public string ForeignKeyErrorFormatter(SqlException ex) { var message = ex.Errors[0]?.Message; //利用规则匹配到操作:The {Operation} statement var operationName = message?.Substring(4, 6)?.Trim(); //利用正则表达式匹配到表名 var tableMatch = ForeignKeyRegex_Table.Match(message); if (!tableMatch.Success) { return null; } var tableName = tableMatch?.Groups[1]?.Value; //增改涉及到的都是表自身,到这一步就结束了 if (operationName == Operation_Merge || operationName == Operation_Insert) { return "新增数据时,外键约束报错:" + tableMatch; } if (operationName == Operation_Update) { return "更新数据时,外键约束报错:" + tableMatch; } //删除操作,涉及到的还有影响到的表 var columnMatch = ForeignKeyRegex_Column.Match(message); if (!columnMatch.Success) { return null; } var columnName = columnMatch?.Groups[1]?.Value; Regex ForeignKeyRegex = new Regex($@"""FK_{tableName}_(\w*)_{columnName}""", RegexOptions.Compiled); var targetTableMatch = ForeignKeyRegex.Match(message); if (!targetTableMatch.Success) { return null; } var targetTableName = targetTableMatch?.Groups[1]?.Value; return "删除数据时,外键约束报错:表[" + targetTableName + "],字段:[" + columnName + "]"; } /// /// 自定义报错 /// /// 格式化的自定义报错信息 /// SqlException public string CustomErrorFormatter(SqlException ex) { var message = ex.Errors[0]?.Message; //这里根据自己定义的错误格式分别处理,比如说:Error:{ErrorMessage} var customErrorCode = message?.Split(":")?.First(); return "自定义错误:" + customErrorCode; } public void InitTreeData(Guid orgId) where T : TreeEntityBase, new() { List updateEntites = new List(); //var selectFields = new string[] { "ID", "IS_LEAF" }; var data = this.GetEntities(i => i.IS_LEAF && i.Nav_Children.Any(), null, true); if (data.Any()) { data.ForEach(i => i.IS_LEAF = false); updateEntites.AddRange(data); } var leafData = this.GetEntities(i => !i.IS_LEAF && !i.Nav_Children.Any(), null, true); if (leafData.Any()) { leafData.ForEach(i => i.IS_LEAF = true); updateEntites.AddRange(leafData); } if (updateEntites.Any()) this.UpdateEntities(updateEntites); } public async Task ExecuteSqlCommandAsync(TransactionalBehavior transactionalBehavior, string sql, params object[] parameters) { return await Database.ExecuteSqlRawAsync(sql, parameters); } public override async Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)) { var entities = from e in ChangeTracker.Entries() where e.State == EntityState.Added || e.State == EntityState.Modified select e.Entity; foreach (var entity in entities) { var validationContext = new ValidationContext(entity); var dbExceptions = new List(); try { Validator.ValidateObject(entity, validationContext); } catch (ValidationException dbEx) { var errorBuilder = new StringBuilder(); errorBuilder.AppendLine(dbEx.Message); } if (dbExceptions.Count > 0) { var errorBuilder = new StringBuilder(); dbExceptions.ForEach(ve => errorBuilder.AppendLine(ve.Message)); throw new DomainException(errorBuilder.ToString()); } } int save = await base.SaveChangesAsync(cancellationToken); return save; } public void BeginTransaction() { this._TranscationLocker++; if (this._dbContextTransaction != null) return; this._dbContextTransaction = this.Database.BeginTransaction(); } public void CommitTransaction() { if (this._dbContextTransaction == null) return; this._TranscationLocker--; if (_TranscationLocker == 0) { this._dbContextTransaction.Commit(); this._dbContextTransaction.Dispose(); this._dbContextTransaction = null; } } public void RollbackTransaction() { if (this._dbContextTransaction == null) return; this._TranscationLocker = 0; this._dbContextTransaction.Rollback(); this._dbContextTransaction.Dispose(); this._dbContextTransaction = null; } public T AddEntity(T entity) where T : class { if (entity == null) return null; return this.Add(entity).Entity; } public void AddEntities(IEnumerable entities) where T : class { if (entities == null || !entities.Any()) return; this.ChangeTracker.AutoDetectChangesEnabled = false; try { this.AddRange(entities); } finally { this.ChangeTracker.AutoDetectChangesEnabled = true; } } public void UpdateEntity(T entity) where T : class { this.ChangeTracker.AutoDetectChangesEnabled = false; try { this.Update(entity); } finally { this.ChangeTracker.AutoDetectChangesEnabled = true; } } public void UpdateEntities(IEnumerable entities) where T : class { if (entities == null || !entities.Any()) return; this.ChangeTracker.AutoDetectChangesEnabled = false; try { this.UpdateRange(entities); } finally { this.ChangeTracker.AutoDetectChangesEnabled = true; } } public void UpdateEntities(IEnumerable entities, params string[] updateField) where T : class { if (entities == null || !entities.Any()) return; if (updateField == null || !updateField.Any()) { this.ChangeTracker.AutoDetectChangesEnabled = false; try { this.UpdateRange(entities); } finally { this.ChangeTracker.AutoDetectChangesEnabled = true; } } else { foreach (var i in entities) { var entitybase = this.Entry(i); foreach (var prop in updateField) { //IsPrimitive=true .NET内置的类型 if (prop != "ID") entitybase.Property(prop).IsModified = true; } } } } public void DeleteEntities(IEnumerable entities) where T : class { if (entities == null || !entities.Any()) return; this.ChangeTracker.AutoDetectChangesEnabled = false; try { this.RemoveRange(entities); } finally { this.ChangeTracker.AutoDetectChangesEnabled = true; } } public DataBaseType GetDataBaseType() { return this.DBType(); } public T GetEntity(Expression> expression, string[] selectField, params string[] paths) where T : class { return this.GetEntity(expression, selectField, false, paths); } public T GetEntity(Expression> expression, string[] selectField, bool isTracking = true, params string[] paths) where T : class { //var queryTable = this.DoGetQuerTable(expression, isTracking, paths); Dictionary order = new Dictionary(); order.TryAdd("ID", DbOrder.ASC); return this.GetEntity(expression, order, selectField, isTracking, paths); } public T GetEntity(Expression> expression, Dictionary orders, string[] selectField, bool isTracking = true, params string[] paths) where T : class { var queryTable = this.DoGetQuerTable(expression, isTracking, paths); var linq = new Orderable(queryTable); queryTable = linq.Queryable; bool isThen = false; if (orders == null) orders = new Dictionary(); if (!orders.Any()) orders.TryAdd("ID", DbOrder.ASC); if (orders != null && orders.Any()) { foreach (var item in orders) { queryTable = queryTable.SetQueryableOrder(item.Key, item.Value.GetDescription(), isThen); isThen = true; } } if (selectField != null && selectField.Any()) queryTable = DynamicSelect(selectField, queryTable); return queryTable.FirstOrDefault(); } public int GetCount(Expression> expression) where T : class { var queryTable = this.DoGetQuerTable(expression); return queryTable.Count(); } public IEnumerable GetEntities(Expression> expression, string[] selectField, params string[] paths) where T : class { return this.GetEntities(expression, selectField, true, paths); } public IEnumerable GetEntities(Expression> expression, string[] selectField, bool isTracking = true, params string[] paths) where T : class { var queryTable = this.DoGetQuerTable(expression, isTracking, paths); if (selectField != null && selectField.Any()) { queryTable = DynamicSelect(selectField, queryTable); } return queryTable.ToList(); } public IEnumerable GetOrderEntities(Expression> expression, Dictionary orders, string[] selectField, params string[] paths) where T : class { return this.GetOrderEntities(expression, orders, null, selectField, true, paths); } public IEnumerable GetOrderEntities(Expression> expression, Dictionary orders, string[] selectField, bool isTracking = true, params string[] paths) where T : class { return this.GetOrderEntities(expression, orders, null, selectField, isTracking, paths); } public IEnumerable GetOrderEntities(Expression> expression, Dictionary orders, Action> orderBy, string[] selectField, bool isTracking = true, params string[] paths) where T : class { var queryTable = this.DoGetQuerTable(expression, isTracking, paths); var linq = new Orderable(queryTable); if (orderBy != null) orderBy(linq); queryTable = linq.Queryable; bool isThen = false; if (orders != null && orders.Any()) { foreach (var item in orders) { queryTable = queryTable.SetQueryableOrder(item.Key, item.Value.GetDescription(), isThen); isThen = true; } } if (selectField != null && selectField.Any()) queryTable = DynamicSelect(selectField, queryTable); return queryTable.ToList(); } public PagedResultDto GetOrderPageEntities(Expression> expression, Dictionary orders, string[] selectField, int pageSize, int startIndex, params string[] paths) where T : class { return this.GetOrderPageEntities(expression, orders, selectField, pageSize, startIndex, true, paths); } public PagedResultDto GetOrderPageEntities(Expression> expression, Dictionary orders, string[] selectField, int pageSize, int startIndex, bool isTracking = true, params string[] paths) where T : class { return this.GetOrderPageEntities(expression, orders, selectField, pageSize, startIndex, null, isTracking, paths); } public PagedResultDto GetOrderPageEntities(Expression> expression, Dictionary orders, int pageSize, int startIndex, Action> orderBy, bool isTracking = true, params string[] paths) where T : class { if (pageSize == 0) throw new Exception("请设置PageSize"); var queryTable = this.DoGetQuerTable(expression, isTracking, paths); var linq = new Orderable(queryTable); if (orderBy != null) orderBy(linq); queryTable = linq.Queryable; bool isThen = false; if (orders != null && orders.Any()) { foreach (var item in orders) { queryTable = queryTable.SetQueryableOrder(item.Key, item.Value.GetDescription(), isThen); isThen = true; } } var result = new PagedResultDto(); result.TotalCount = queryTable.Count(); result.Items = queryTable.Skip(startIndex) .Take(pageSize) .ToArray(); return result; } public PagedResultDto GetOrderPageEntities(Expression> expression, Dictionary orders, string[] selectField, int pageSize, int startIndex, Action> orderBy, bool isTracking = true, params string[] paths) where T : class { if (pageSize == 0) throw new Exception("请设置PageSize"); var queryTable = this.DoGetQuerTable(expression, isTracking, paths); var linq = new Orderable(queryTable); if (orderBy != null) orderBy(linq); queryTable = linq.Queryable; bool isThen = false; if (orders != null && orders.Any()) { foreach (var item in orders) { queryTable = queryTable.SetQueryableOrder(item.Key, item.Value.GetDescription(), isThen); isThen = true; } } if (selectField != null && selectField.Any()) queryTable = DynamicSelect(selectField, queryTable); var result = new PagedResultDto(); result.TotalCount = queryTable.Count(); result.Items = queryTable.Skip(startIndex) .Take(pageSize) .ToArray(); return result; } // public async Task> GetOrderPageEntitiesSync(Expression> expression, Dictionary orders, int pageSize, int startIndex, params string[] paths) where T : class { return await this.GetOrderPageEntitiesSync(expression, orders, pageSize, startIndex, true, paths); } public async Task> GetOrderPageEntitiesSync(Expression> expression, Dictionary orders, int pageSize, int startIndex, bool isTracking = true, params string[] paths) where T : class { return await this.GetOrderPageEntitiesSync(expression, orders, pageSize, startIndex, null, isTracking, paths); } public async Task> GetOrderPageEntitiesSync(Expression> expression, Dictionary orders, int pageSize, int startIndex, Action> orderBy, bool isTracking = true, params string[] paths) where T : class { if (pageSize == 0) throw new Exception("请设置PageSize"); var queryTable = this.DoGetQuerTable(expression, isTracking, paths); var linq = new Orderable(queryTable); if (orderBy != null) orderBy(linq); queryTable = linq.Queryable; bool isThen = false; if (orders != null && orders.Any()) { foreach (var item in orders) { queryTable = queryTable.SetQueryableOrder(item.Key, item.Value.GetDescription(), isThen); isThen = true; } } var result = new PagedResultDto(); result.TotalCount = await queryTable.CountAsync(); result.Items = await queryTable.Skip(startIndex) .Take(pageSize) .ToArrayAsync(); return result; } public PagedResultDto GetPageEntities(Expression> expression, Dictionary orders, string[] selectField, int pageSize, int startIndex, Action> orderBy, bool isTracking = true, params string[] paths) where T : class { if (pageSize == 0) throw new Exception("请设置PageSize"); var queryTable = this.DoGetQuerTable(expression, isTracking, paths); var linq = new Orderable(queryTable); if (orderBy != null) orderBy(linq); queryTable = linq.Queryable; var result = new PagedResultDto(); result.TotalCount = queryTable.Count(); result.Items = queryTable.Skip(startIndex) .Take(pageSize) .ToArray(); return result; } private IQueryable DynamicSelect(string[] selectField, IQueryable queryTable) { if (!selectField.Contains("ORG_ID")) selectField = selectField.Append("ORG_ID").ToArray(); var selectSql = "New("; if (selectField != null && selectField.Any()) { var type = typeof(T); foreach (var s in selectField) { if (s.Contains(".") || type.GetProperty(s) == null) continue; selectSql += s + ","; } selectSql = selectSql.Trim(','); selectSql += ")"; queryTable = queryTable.Select(selectSql); } return queryTable; } private IQueryable DoGetQuerTable(Expression> expression, bool isTracking = true, params string[] paths) where T : class { var queryTable = this.Set().AsQueryable(); if (expression != null) queryTable = queryTable.Where(expression); if (paths != null && paths.Any()) { foreach (var item in paths) queryTable = queryTable.Include(item); } if (!isTracking) queryTable = queryTable.AsNoTracking(); return queryTable; } #endregion #region redis private string GetRedisPropertyValue(T entity, string name) { try { var properties = entity.GetType().GetProperties(); var property = properties.FirstOrDefault(o => o.Name == name); if (property != null) { return property.GetValue(entity, null).ToString(); } return string.Empty; } catch (Exception ex) { return string.Empty; } } #endregion #region methods protected override void OnModelCreating(ModelBuilder modelBuilder) { foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { entityType.SetTableName(entityType.DisplayName()); entityType.GetForeignKeys() .Where(fk => fk.DeleteBehavior == DeleteBehavior.Cascade) .ToList() .ForEach(fk => fk.DeleteBehavior = DeleteBehavior.Restrict); } foreach (var property in modelBuilder.Model.GetEntityTypes() .SelectMany(t => t.GetProperties()) .Where(p => p.ClrType == typeof(decimal))) { property.SetColumnType("decimal(18, 6)"); } var files = Directory.GetFiles(System.AppContext.BaseDirectory, "*.Data.dll").ToList(); var BaseFiles = Directory.GetFiles(System.AppContext.BaseDirectory, "*APT.Infrastructure.EF.dll").ToList(); files.AddRange(BaseFiles); if (files.Count > 0) { foreach (var file in files) { var assembly = Assembly.LoadFrom(file); if (assembly != null) { modelBuilder.ApplyConfigurationsFromAssembly(assembly); } } } base.OnModelCreating(modelBuilder); this.GetEfModelByModel(modelBuilder.Model); } private void GetEfModelByModel(IModel model) { _efModel = new EFModel(); foreach (var entity in model.GetEntityTypes()) { EFModelTable efTable = new EFModelTable(); efTable.Name = entity.ClrType.Name; var properties = entity.GetProperties().ToList(); foreach (var property in properties) { EFModelField field = new EFModelField(); field.FieldType = property.ClrType.GetUnNullableType(); field.IsNull = property.IsNullable; field.Name = property.Name; field.TypeName = field.FieldType.Name; field.PropertyInfo = property.PropertyInfo; efTable.Fields.Add(field); } var foreignKeys = entity.GetForeignKeys().ToList(); foreach (var fKey in foreignKeys) { EFModelForeignKey foreignKey = CreateEFModelForeignKey(fKey); if (foreignKey == null) continue; efTable.ForeignKeys.Add(foreignKey); } var navigations = entity.GetNavigations().ToList(); foreach (var nav in navigations) { if (nav.ForeignKey == null) continue; EFModelForeignKey foreignKey = CreateEFModelForeignKey(nav.ForeignKey); if (foreignKey == null) continue; efTable.AllForeignKeys.Add(foreignKey); } _efModel.Tables.Add(efTable); } } private EFModelForeignKey CreateEFModelForeignKey(IForeignKey fKey) { if (!fKey.Properties.Any() || fKey.DependentToPrincipal == null && fKey.PrincipalToDependent == null) return null; EFModelForeignKey foreignKey = new EFModelForeignKey(); foreignKey.ForeignFieldName = fKey.Properties[0].Name; foreignKey.IsNull = fKey.Properties[0].IsNullable; if (fKey.DependentToPrincipal != null) { foreignKey.ForeignNavName = fKey.DependentToPrincipal.Name; foreignKey.ForeignTableName = fKey.DependentToPrincipal.PropertyInfo.PropertyType.Name; } if (fKey.PrincipalToDependent != null) foreignKey.MasterNavName = fKey.PrincipalToDependent.Name; foreignKey.ForeignKey = fKey; return foreignKey; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { var isSqlLog = LibUtils.ToBoolean(ConfigurationManager.AppSettings["isSqlLog"]); var logFactory = ServiceLocator.Instance.GetService(); var logProvider = ServiceLocator.Instance.GetService(); if (isSqlLog && logFactory != null && logProvider != null) { //logFactory.AddProvider(logProvider); optionsBuilder.EnableSensitiveDataLogging().UseLoggerFactory(logFactory); } //optionsBuilder.AddInterceptors(new QueryWithNoLockDbCommandInterceptor()); if (!string.IsNullOrWhiteSpace(namedConnection)) { var dbType = this.DBType(); if (dbType == DataBaseType.Oracle) { //optionsBuilder // .UseLazyLoadingProxies(false) // .UseOracle(this.ConnectionString); } else if (dbType == DataBaseType.MySQL) { //optionsBuilder // .UseLazyLoadingProxies(false) // .UseMySQL(this.ConnectionString); } else if (dbType == DataBaseType.Postgresql) { //optionsBuilder // .UseLazyLoadingProxies(false) // .UseNpgsql(this.ConnectionString); } else if (dbType == DataBaseType.SQL) { optionsBuilder .UseLazyLoadingProxies(false) .UseSqlServer(this.ConnectionString); } } } /// /// 根据表名获取表数据结构 /// /// /// public EFModelTable GetModelTable(string tableName) { //if (_efModel == null) // this.GetEntity(t => t.CODE == "0000", null); if (_efModel == null && this.Model != null) this.GetEfModelByModel(this.Model); if (_efModel == null || _efModel.Tables == null || string.IsNullOrEmpty(tableName)) return null; return _efModel.Tables.FirstOrDefault(t => string.Compare(t.Name, tableName, true) == 0); } public List GetModelForeignKey(string tableName) { var table = this.GetModelTable(tableName); if (table == null) return null; var fkey = table.ForeignKeys; return fkey; } /// /// 根据表名、字段名称获取字段结构 /// /// /// /// public EFModelField GetModelField(string tableName, string fieldName) { var table = this.GetModelTable(tableName); if (table == null) return null; return table.Fields.FirstOrDefault(t => string.Compare(t.Name, fieldName, true) == 0); } /// /// 根据表名,导航名称获取外键信息 /// /// /// /// public EFModelForeignKey GetModelForeignKey(string tableName, string navName) { var table = this.GetModelTable(tableName); if (table == null) return null; var fkey = table.ForeignKeys.FirstOrDefault(t => string.Compare(t.ForeignNavName, navName, true) == 0); if (fkey == null) { fkey = table.AllForeignKeys.FirstOrDefault(t => string.Compare(t.MasterNavName, navName, true) == 0); } return fkey; } /// /// 根据表名,ID字段名称获取外键信息 /// /// /// /// public EFModelForeignKey GetModelForeignKeyById(string tableName, string idFieldName) { var table = this.GetModelTable(tableName); if (table == null) return null; return table.ForeignKeys.FirstOrDefault(t => string.Compare(t.ForeignFieldName, idFieldName, true) == 0); } #endregion private List RawSqlQuery(string query, Func map) { using (var command = this.Database.GetDbConnection().CreateCommand()) { command.CommandText = query; command.CommandType = CommandType.Text; this.Database.OpenConnection(); using (var result = command.ExecuteReader()) { var entities = new List(); while (result.Read()) { entities.Add(map(result)); } return entities; } } } } }