mh_frame_sps/APT.Infrastructure.EF/EfDbContext.cs
2026-04-07 13:47:52 +08:00

1538 lines
63 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

using 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;
}
}
}
}
}