mh_frame_sps/APT.Infrastructure.EF/EfDbContext.cs

1538 lines
63 KiB
C#
Raw Permalink Normal View History

2026-04-07 13:47:52 +08:00
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<IConnectionResolver>();
}
return _connectionResolver;
}
}
//public static Action<string> SQL_LOG { get; set; }
int _TranscationLocker = 0;
IDbContextTransaction _dbContextTransaction;
private static EFModel _efModel;
#region ctor.
public EfDbContext() : base()
{
}
/// <summary>
/// 初始化一个<see cref="CodeFirstDbContext"/>类型的新实例
/// </summary>
//<summary>
// 使用连接名称或连接字符串 初始化一个<see cref= "CodeFirstDbContext" /> 类型的新实例
// </ summary >
public EfDbContext(string nameOrConnectionString) : base()
{
this.namedConnection = nameOrConnectionString;
}
public TenantInfo TenantInfo => tenantInfo;
private readonly TenantInfo tenantInfo;
public EfDbContext(DbContextOptions<EfDbContext> 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<DbDataReader> readerAction)
{
ExecuteReader(sql, readerColumns, null, readerAction);
}
public void ExecuteReader(string sql, ReaderColumn[] readerColumns, DbParameter[] parameters, Action<DbDataReader> readerAction)
{
ExecuteReader(CommandType.Text, sql, readerColumns, parameters, readerAction);
}
public void ExecuteReader(CommandType commandType, string sql, ReaderColumn[] readerColumns, DbParameter[] parameters, Action<DbDataReader> readerAction)
{
var concurrencyDetector = Database.GetService<IConcurrencyDetector>();
using (concurrencyDetector.EnterCriticalSection())
{
IReadOnlyDictionary<string, object> paramValues = null;
IRelationalCommand relationalCommand = GetRelationComman(sql, parameters, out paramValues);
var paramObject = new RelationalCommandParameterObject(
Database.GetService<IRelationalConnection>(),
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<IConcurrencyDetector>();
using (concurrencyDetector.EnterCriticalSection())
{
IReadOnlyDictionary<string, object> paramValues = null;
IRelationalCommand relationalCommand = GetRelationComman(sql, parameters, out paramValues);
var paramObject = new RelationalCommandParameterObject(
Database.GetService<IRelationalConnection>(),
paramValues, readerColumns,
this,
null
);
var dr = relationalCommand
.ExecuteNonQuery(paramObject);
return dr;
}
}
private IRelationalCommand GetRelationComman(string sql, object[] parameters, out IReadOnlyDictionary<string, object> paramValues)
{
paramValues = null;
IRelationalCommand relationalCommand = null;
if (parameters != null && parameters.Any())
{
var rawSqlCommand = Database
.GetService<IRawSqlCommandBuilder>()
.Build(sql, parameters);
relationalCommand = rawSqlCommand
.RelationalCommand;
paramValues = rawSqlCommand.ParameterValues;
}
else
{
relationalCommand = Database
.GetService<IRawSqlCommandBuilder>()
.Build(sql);
}
return relationalCommand;
}
public IEnumerable SqlQuery(Type elementType, string sql, ReaderColumn[] readCloums, params object[] parameters)
{
var concurrencyDetector = Database.GetService<IConcurrencyDetector>();
using (concurrencyDetector.EnterCriticalSection())
{
IReadOnlyDictionary<string, object> paramValues = null;
IRelationalCommand relationalCommand = GetRelationComman(sql, parameters, out paramValues);
var paramObject = new RelationalCommandParameterObject(
Database.GetService<IRelationalConnection>(),
paramValues, readCloums,
this,
null
);
var dr = relationalCommand
.ExecuteReader(paramObject);
return dr.DbDataReader;
}
}
#region GetTreeOrderEntities
public IEnumerable<TreeNode<T>> GetTreeOrderEntities<T>(Expression<Func<T, bool>> expression, BaseFilter filter,
Expression<Func<T, bool>> orgExpress, out List<T> resultList, bool isTracking = true,
params string[] paths) where T : TreeEntityBase<T>, new()
{
List<T> allData = new List<T>();
//List<T> data = new List<T>();
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<T>(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<TreeNode<T>> treeNodes = new List<TreeNode<T>>();
foreach (var d in parentData)
{
var node = new TreeNode<T>();
node.Node = d;
node.Children = new List<TreeNode<T>>();
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<T>(List<T> data, List<T> childNodeList, TreeNode<T> theNode, int level) where T : TreeEntityBase<T>, new()
{
foreach (var d in childNodeList)
{
var node = new TreeNode<T>();
node.Node = d;
node.Children = new List<TreeNode<T>>();
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<T>(List<T> allData, List<T> data, Guid[] dataIds) where T : TreeEntityBase<T>, 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<T>(List<TreeNode<T>> treeNodes, BaseFilter filter) where T : TreeEntityBase<T>, new()
{
SortBy<T>(filter, treeNodes);
foreach (var node in treeNodes)
{
if (node.Children != null && node.Children.Any())
{
ReSort(node.Children, filter);
}
}
}
private static IList<T> SortBy<T>(BaseFilter filter, IList<T> parentData) where T : TreeEntityBase<T>, 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<TreeNode<T>> SortBy<T>(BaseFilter filter, List<TreeNode<T>> treeNodes) where T : TreeEntityBase<T>, 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<string> typeNames = new List<string>();
Dictionary<Type, Guid> DicInitTree = new Dictionary<Type, Guid>();
var isReInitTree = false;
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
var dbExceptions = new List<ValidationException>();
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;
}
}
/// <summary>
/// 统一处理数据库报的异常
/// </summary>
/// <param name="ex">DbUpdateException</param>
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<Npgsql.NpgsqlParameter>;
// 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<DataFieldLengthAttribute>();
// 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<DescriptionAttribute>();
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 + "]";
}
/// <summary>
/// 自定义报错
/// </summary>
/// <returns>格式化的自定义报错信息</returns>
/// <param name="ex">SqlException</param>
public string CustomErrorFormatter(SqlException ex)
{
var message = ex.Errors[0]?.Message;
//这里根据自己定义的错误格式分别处理比如说Error:{ErrorMessage}
var customErrorCode = message?.Split(":")?.First();
return "自定义错误:" + customErrorCode;
}
public void InitTreeData<T>(Guid orgId) where T : TreeEntityBase<T>, new()
{
List<T> updateEntites = new List<T>();
//var selectFields = new string[] { "ID", "IS_LEAF" };
var data = this.GetEntities<T>(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<T>(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<int> ExecuteSqlCommandAsync(TransactionalBehavior transactionalBehavior, string sql, params object[] parameters)
{
return await Database.ExecuteSqlRawAsync(sql, parameters);
}
public override async Task<int> 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<ValidationException>();
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>(T entity) where T : class
{
if (entity == null) return null;
return this.Add<T>(entity).Entity;
}
public void AddEntities<T>(IEnumerable<T> 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>(T entity) where T : class
{
this.ChangeTracker.AutoDetectChangesEnabled = false;
try
{
this.Update(entity);
}
finally
{
this.ChangeTracker.AutoDetectChangesEnabled = true;
}
}
public void UpdateEntities<T>(IEnumerable<T> 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<T>(IEnumerable<T> 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<T>(i);
foreach (var prop in updateField)
{
//IsPrimitive=true .NET内置的类型
if (prop != "ID")
entitybase.Property(prop).IsModified = true;
}
}
}
}
public void DeleteEntities<T>(IEnumerable<T> 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<T>(Expression<Func<T, bool>> expression, string[] selectField, params string[] paths) where T : class
{
return this.GetEntity<T>(expression, selectField, false, paths);
}
public T GetEntity<T>(Expression<Func<T, bool>> expression, string[] selectField, bool isTracking = true, params string[] paths) where T : class
{
//var queryTable = this.DoGetQuerTable<T>(expression, isTracking, paths);
Dictionary<string, DbOrder> order = new Dictionary<string, DbOrder>();
order.TryAdd("ID", DbOrder.ASC);
return this.GetEntity(expression, order, selectField, isTracking, paths);
}
public T GetEntity<T>(Expression<Func<T, bool>> expression, Dictionary<string, DbOrder> orders, string[] selectField,
bool isTracking = true, params string[] paths) where T : class
{
var queryTable = this.DoGetQuerTable<T>(expression, isTracking, paths);
var linq = new Orderable<T>(queryTable);
queryTable = linq.Queryable;
bool isThen = false;
if (orders == null)
orders = new Dictionary<string, DbOrder>();
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<T>(Expression<Func<T, bool>> expression) where T : class
{
var queryTable = this.DoGetQuerTable<T>(expression);
return queryTable.Count();
}
public IEnumerable<T> GetEntities<T>(Expression<Func<T, bool>> expression, string[] selectField, params string[] paths) where T : class
{
return this.GetEntities<T>(expression, selectField, true, paths);
}
public IEnumerable<T> GetEntities<T>(Expression<Func<T, bool>> expression, string[] selectField, bool isTracking = true, params string[] paths) where T : class
{
var queryTable = this.DoGetQuerTable<T>(expression, isTracking, paths);
if (selectField != null && selectField.Any())
{
queryTable = DynamicSelect(selectField, queryTable);
}
return queryTable.ToList();
}
public IEnumerable<T> GetOrderEntities<T>(Expression<Func<T, bool>> expression,
Dictionary<string, DbOrder> orders, string[] selectField,
params string[] paths) where T : class
{
return this.GetOrderEntities<T>(expression, orders, null, selectField, true, paths);
}
public IEnumerable<T> GetOrderEntities<T>(Expression<Func<T, bool>> expression,
Dictionary<string, DbOrder> orders, string[] selectField,
bool isTracking = true, params string[] paths) where T : class
{
return this.GetOrderEntities<T>(expression, orders, null, selectField, isTracking, paths);
}
public IEnumerable<T> GetOrderEntities<T>(Expression<Func<T, bool>> expression,
Dictionary<string, DbOrder> orders, Action<IOrderable<T>> orderBy, string[] selectField,
bool isTracking = true, params string[] paths) where T : class
{
var queryTable = this.DoGetQuerTable<T>(expression, isTracking, paths);
var linq = new Orderable<T>(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<T> GetOrderPageEntities<T>(Expression<Func<T, bool>> expression, Dictionary<string, DbOrder> 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<T> GetOrderPageEntities<T>(Expression<Func<T, bool>> expression, Dictionary<string, DbOrder> 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<T> GetOrderPageEntities<T>(Expression<Func<T, bool>> expression, Dictionary<string, DbOrder> orders,
int pageSize, int startIndex, Action<IOrderable<T>> orderBy, bool isTracking = true,
params string[] paths) where T : class
{
if (pageSize == 0) throw new Exception("请设置PageSize");
var queryTable = this.DoGetQuerTable<T>(expression, isTracking, paths);
var linq = new Orderable<T>(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<T>();
result.TotalCount = queryTable.Count();
result.Items = queryTable.Skip(startIndex)
.Take(pageSize)
.ToArray();
return result;
}
public PagedResultDto<T> GetOrderPageEntities<T>(Expression<Func<T, bool>> expression, Dictionary<string, DbOrder> orders, string[] selectField,
int pageSize, int startIndex, Action<IOrderable<T>> orderBy, bool isTracking = true,
params string[] paths) where T : class
{
if (pageSize == 0) throw new Exception("请设置PageSize");
var queryTable = this.DoGetQuerTable<T>(expression, isTracking, paths);
var linq = new Orderable<T>(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<T>();
result.TotalCount = queryTable.Count();
result.Items = queryTable.Skip(startIndex)
.Take(pageSize)
.ToArray();
return result;
}
//
public async Task<PagedResultDto<T>> GetOrderPageEntitiesSync<T>(Expression<Func<T, bool>> expression, Dictionary<string, DbOrder> orders,
int pageSize, int startIndex,
params string[] paths) where T : class
{
return await this.GetOrderPageEntitiesSync(expression, orders, pageSize, startIndex, true, paths);
}
public async Task<PagedResultDto<T>> GetOrderPageEntitiesSync<T>(Expression<Func<T, bool>> expression, Dictionary<string, DbOrder> 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<PagedResultDto<T>> GetOrderPageEntitiesSync<T>(Expression<Func<T, bool>> expression, Dictionary<string, DbOrder> orders,
int pageSize, int startIndex, Action<IOrderable<T>> orderBy, bool isTracking = true,
params string[] paths) where T : class
{
if (pageSize == 0) throw new Exception("请设置PageSize");
var queryTable = this.DoGetQuerTable<T>(expression, isTracking, paths);
var linq = new Orderable<T>(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<T>();
result.TotalCount = await queryTable.CountAsync();
result.Items = await queryTable.Skip(startIndex)
.Take(pageSize)
.ToArrayAsync();
return result;
}
public PagedResultDto<T> GetPageEntities<T>(Expression<Func<T, bool>> expression, Dictionary<string, DbOrder> orders, string[] selectField,
int pageSize, int startIndex, Action<IOrderable<T>> orderBy, bool isTracking = true,
params string[] paths) where T : class
{
if (pageSize == 0) throw new Exception("请设置PageSize");
var queryTable = this.DoGetQuerTable<T>(expression, isTracking, paths);
var linq = new Orderable<T>(queryTable);
if (orderBy != null)
orderBy(linq);
queryTable = linq.Queryable;
var result = new PagedResultDto<T>();
result.TotalCount = queryTable.Count();
result.Items = queryTable.Skip(startIndex)
.Take(pageSize)
.ToArray();
return result;
}
private IQueryable<T> DynamicSelect<T>(string[] selectField, IQueryable<T> 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<T>(selectSql);
}
return queryTable;
}
private IQueryable<T> DoGetQuerTable<T>(Expression<Func<T, bool>> expression, bool isTracking = true, params string[] paths) where T : class
{
var queryTable = this.Set<T>().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>(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<ILoggerFactory>();
var logProvider = ServiceLocator.Instance.GetService<ILoggerProvider>();
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);
}
}
}
/// <summary>
/// 根据表名获取表数据结构
/// </summary>
/// <param name="tableName"></param>
/// <returns></returns>
public EFModelTable GetModelTable(string tableName)
{
//if (_efModel == null)
// this.GetEntity<T_FM_ORGANIZATION>(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<EFModelForeignKey> GetModelForeignKey(string tableName)
{
var table = this.GetModelTable(tableName);
if (table == null) return null;
var fkey = table.ForeignKeys;
return fkey;
}
/// <summary>
/// 根据表名、字段名称获取字段结构
/// </summary>
/// <param name="tableName"></param>
/// <param name="fieldName"></param>
/// <returns></returns>
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);
}
/// <summary>
/// 根据表名,导航名称获取外键信息
/// </summary>
/// <param name="tableName"></param>
/// <param name="navName"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 根据表名ID字段名称获取外键信息
/// </summary>
/// <param name="tableName"></param>
/// <param name="idFieldName"></param>
/// <returns></returns>
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<T> RawSqlQuery<T>(string query, Func<DbDataReader, T> 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<T>();
while (result.Read())
{
entities.Add(map(result));
}
return entities;
}
}
}
}
}