EntityMaintenance.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. using SqlSugar.DbConvert;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Xml.Linq;
  8. namespace SqlSugar
  9. {
  10. public class EntityMaintenance
  11. {
  12. public SqlSugarProvider Context { get; set; }
  13. public EntityInfo GetEntityInfo<T>()
  14. {
  15. return GetEntityInfo(typeof(T));
  16. }
  17. public EntityInfo GetEntityInfoWithAttr(Type type)
  18. {
  19. return GetEntityInfo(type);
  20. }
  21. public EntityInfo GetEntityInfo(Type type)
  22. {
  23. var attr = type?.GetCustomAttribute<TenantAttribute>();
  24. if (attr == null)
  25. {
  26. return _GetEntityInfo(type);
  27. }
  28. else if (attr.configId.ObjToString() == this.Context?.CurrentConnectionConfig?.ConfigId + "")
  29. {
  30. return _GetEntityInfo(type);
  31. }
  32. else if (this.Context.Root == null)
  33. {
  34. return _GetEntityInfo(type);
  35. }
  36. else if (!this.Context.Root.IsAnyConnection(attr.configId))
  37. {
  38. return _GetEntityInfo(type);
  39. }
  40. else
  41. {
  42. return this.Context.Root.GetConnection(attr.configId).EntityMaintenance._GetEntityInfo(type);
  43. }
  44. }
  45. private EntityInfo _GetEntityInfo(Type type)
  46. {
  47. string cacheKey = "GetEntityInfo" + type.GetHashCode() + type.FullName + this.Context?.CurrentConnectionConfig?.ConfigId;
  48. return this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey,
  49. () =>
  50. {
  51. return GetEntityInfoNoCache(type);
  52. });
  53. }
  54. public EntityInfo GetEntityInfoNoCache(Type type)
  55. {
  56. EntityInfo result = new EntityInfo();
  57. var sugarAttributeInfo = type.GetTypeInfo().GetCustomAttributes(typeof(SugarTable), false).Where(it => it is SugarTable).SingleOrDefault();
  58. if (sugarAttributeInfo.HasValue())
  59. {
  60. var sugarTable = (SugarTable)sugarAttributeInfo;
  61. result.DbTableName = sugarTable.TableName;
  62. result.TableDescription = sugarTable.TableDescription.ToSqlFilter();
  63. result.IsDisabledUpdateAll = sugarTable.IsDisabledUpdateAll;
  64. result.IsDisabledDelete = sugarTable.IsDisabledDelete;
  65. result.IsCreateTableFiledSort = sugarTable.IsCreateTableFiledSort;
  66. result.Discrimator = sugarTable.Discrimator;
  67. }
  68. var indexs = type.GetCustomAttributes(typeof(SugarIndexAttribute));
  69. if (indexs != null && indexs.Any())
  70. {
  71. result.Indexs = indexs.Select(it => it as SugarIndexAttribute).ToList();
  72. }
  73. if (result.TableDescription.IsNullOrEmpty()) result.TableDescription = GetTableAnnotation(type);
  74. if (this.Context.CurrentConnectionConfig.ConfigureExternalServices != null && this.Context.CurrentConnectionConfig.ConfigureExternalServices.EntityNameService != null)
  75. {
  76. if (result.DbTableName == null)
  77. {
  78. result.DbTableName = type.Name;
  79. }
  80. this.Context.CurrentConnectionConfig.ConfigureExternalServices.EntityNameService(type, result);
  81. }
  82. result.Type = type;
  83. result.EntityName = result.Type.Name;
  84. result.Columns = new List<EntityColumnInfo>();
  85. SetColumns(result);
  86. return result;
  87. }
  88. public string GetTableName<T>()
  89. {
  90. return GetTableName(typeof(T));
  91. }
  92. public string GetTableName(Type entityType)
  93. {
  94. var typeName = entityType.Name;
  95. if (this.Context.MappingTables == null || this.Context.MappingTables.Count == 0 || !this.Context.MappingTables.Any(it => it.EntityName == typeName))
  96. {
  97. var entity = this.GetEntityInfo(entityType);
  98. if (entity.DbTableName.HasValue()) return entity.DbTableName;
  99. else return entity.EntityName;
  100. }
  101. else
  102. {
  103. var mappingInfo = this.Context.MappingTables.SingleOrDefault(it => it.EntityName == typeName);
  104. return mappingInfo == null ? typeName : mappingInfo.DbTableName;
  105. }
  106. }
  107. public string GetTableName(string entityName)
  108. {
  109. var typeName = entityName;
  110. if (this.Context.MappingTables == null || this.Context.MappingTables.Count == 0) return typeName;
  111. else
  112. {
  113. var mappingInfo = this.Context.MappingTables.SingleOrDefault(it => it.EntityName == typeName);
  114. return mappingInfo == null ? typeName : mappingInfo.DbTableName;
  115. }
  116. }
  117. public string GetEntityName(string tableName)
  118. {
  119. if (this.Context.MappingTables == null || this.Context.MappingTables.Count == 0) return tableName;
  120. else
  121. {
  122. var mappingInfo = this.Context.MappingTables.SingleOrDefault(it => it.DbTableName == tableName);
  123. return mappingInfo == null ? tableName : mappingInfo.EntityName;
  124. }
  125. }
  126. public string GetEntityName<T>()
  127. {
  128. return this.Context.EntityMaintenance.GetEntityInfo<T>().EntityName;
  129. }
  130. public string GetEntityName(Type type)
  131. {
  132. return this.Context.EntityMaintenance.GetEntityInfo(type).EntityName;
  133. }
  134. public string GetDbColumnName<T>(string propertyName)
  135. {
  136. return GetDbColumnName(propertyName, typeof(T));
  137. }
  138. public string GetDbColumnName(string propertyName, Type entityType)
  139. {
  140. var isAny = this.GetEntityInfo(entityType).Columns.Any(it => it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase));
  141. Check.Exception(!isAny, "Property " + propertyName + " is Invalid");
  142. var typeName = entityType.Name;
  143. if (this.Context.MappingColumns == null || this.Context.MappingColumns.Count == 0 || !this.Context.MappingColumns.Any(it => it.EntityName == typeName && it.PropertyName == propertyName))
  144. {
  145. var column = this.GetEntityInfo(entityType).Columns.First(it => it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase));
  146. if (column.DbColumnName.HasValue()) return column.DbColumnName;
  147. else return column.PropertyName;
  148. }
  149. else
  150. {
  151. var mappingInfo = this.Context.MappingColumns.SingleOrDefault(it => it.EntityName == typeName && it.PropertyName == propertyName);
  152. return mappingInfo == null ? propertyName : mappingInfo.DbColumnName;
  153. }
  154. }
  155. public string GetPropertyName<T>(string dbColumnName)
  156. {
  157. var columnInfo=this.Context.EntityMaintenance.GetEntityInfo<T>().Columns.FirstOrDefault(it=>it.DbColumnName.EqualCase(dbColumnName));
  158. if (columnInfo != null)
  159. {
  160. return columnInfo.PropertyName;
  161. }
  162. var typeName = typeof(T).Name;
  163. if (this.Context.MappingColumns == null || this.Context.MappingColumns.Count == 0) return dbColumnName;
  164. else
  165. {
  166. var mappingInfo = this.Context.MappingColumns.SingleOrDefault(it => it.EntityName == typeName && it.DbColumnName.Equals(dbColumnName, StringComparison.CurrentCultureIgnoreCase));
  167. return mappingInfo == null ? dbColumnName : mappingInfo.PropertyName;
  168. }
  169. }
  170. public string GetPropertyName(string dbColumnName, Type entityType)
  171. {
  172. var columnInfo = this.Context.EntityMaintenance.GetEntityInfo(entityType).Columns.FirstOrDefault(it => it.DbColumnName.EqualCase(dbColumnName));
  173. if (columnInfo != null)
  174. {
  175. return columnInfo.PropertyName;
  176. }
  177. var typeName = entityType.Name;
  178. if (this.Context.MappingColumns == null || this.Context.MappingColumns.Count == 0) return dbColumnName;
  179. else
  180. {
  181. var mappingInfo = this.Context.MappingColumns.SingleOrDefault(it => it.EntityName == typeName && it.DbColumnName.Equals(dbColumnName, StringComparison.CurrentCultureIgnoreCase));
  182. return mappingInfo == null ? dbColumnName : mappingInfo.PropertyName;
  183. }
  184. }
  185. public PropertyInfo GetProperty<T>(string dbColumnName)
  186. {
  187. var propertyName = GetPropertyName<T>(dbColumnName);
  188. return typeof(T).GetProperties().First(it => it.Name == propertyName);
  189. }
  190. /// <summary>
  191. /// Gets the text contents of this XML element node
  192. /// </summary>
  193. /// <param name="entityType">entity type</param>
  194. /// <param name="nodeAttributeName">The value of the name attribute of the XML node</param>
  195. /// <returns>the text contents of this XML element node</returns>
  196. public string GetXElementNodeValue(Type entityType, string nodeAttributeName)
  197. {
  198. try
  199. {
  200. if (this.Context.CurrentConnectionConfig?.MoreSettings?.IsNoReadXmlDescription == true)
  201. {
  202. return "";
  203. }
  204. if (entityType.Assembly.IsDynamic&& entityType.Assembly.FullName.StartsWith("Dynamic"))
  205. {
  206. return null;
  207. }
  208. var path = entityType.Assembly.Location;
  209. if (string.IsNullOrEmpty(path))
  210. {
  211. return null;
  212. }
  213. FileInfo file = new FileInfo(path);
  214. string xmlPath = entityType.Assembly.Location.Replace(file.Extension, ".xml");
  215. if (!File.Exists(xmlPath))
  216. {
  217. return string.Empty;
  218. }
  219. XElement xe =new ReflectionInoCacheService().GetOrCreate("EntityXml_"+xmlPath,()=> XElement.Load(xmlPath));
  220. if (xe == null)
  221. {
  222. return string.Empty;
  223. }
  224. var xeNode = xe.Element("members").Elements("member").Where(ele => ele.Attribute("name").Value == nodeAttributeName).FirstOrDefault();
  225. if (xeNode == null)
  226. {
  227. return string.Empty;
  228. }
  229. var summary = xeNode.Element("summary");
  230. if (summary != null)
  231. {
  232. return summary.Value.ToSqlFilter().Trim();
  233. }
  234. else
  235. {
  236. var summaryValue = xeNode.Elements().Where(x => x.Name.ToString().EqualCase("summary")).Select(it => it.Value).FirstOrDefault();
  237. if(summaryValue==null)
  238. return string.Empty;
  239. else
  240. return summaryValue.ToSqlFilter().Trim()??"";
  241. }
  242. }
  243. catch
  244. {
  245. Check.ExceptionEasy("ORM error reading entity class XML, check entity XML or disable reading XML: MoreSettings IsNoReadXmlDescription set to true (same place to set DbType)", "ORM读取实体类的XML出现错误,检查实体XML或者禁用读取XML: MoreSettings里面的IsNoReadXmlDescription设为true (设置DbType的同一个地方)");
  246. throw;
  247. }
  248. }
  249. /// <summary>
  250. /// Gets the code annotation for the database table
  251. /// </summary>
  252. /// <param name="entityType">entity type</param>
  253. /// <returns>the code annotation for the database table</returns>
  254. public string GetTableAnnotation(Type entityType)
  255. {
  256. if (entityType.IsClass() == false)
  257. {
  258. return null;
  259. }
  260. var result= GetXElementNodeValue(entityType, $"T:{entityType.FullName}");
  261. if (string.IsNullOrEmpty(result))
  262. {
  263. return null;
  264. }
  265. else
  266. {
  267. return result;
  268. }
  269. }
  270. /// <summary>
  271. /// Gets the code annotation for the field
  272. /// </summary>
  273. /// <param name="entityType">entity type</param>
  274. /// <param name="dbColumnName">column name</param>
  275. /// <returns>the code annotation for the field</returns>
  276. public string GetPropertyAnnotation(Type entityType, string dbColumnName)
  277. {
  278. if (entityType.IsClass() == false || entityType == typeof(object))
  279. {
  280. return null;
  281. }
  282. var result = GetXElementNodeValue(entityType, $"P:{entityType.FullName}.{dbColumnName}");
  283. if (string.IsNullOrEmpty(result))
  284. {
  285. return GetPropertyAnnotation(entityType.BaseType, dbColumnName);
  286. }
  287. else
  288. {
  289. return result;
  290. }
  291. }
  292. #region Primary key
  293. private void SetColumns(EntityInfo result)
  294. {
  295. foreach (var property in result.Type.GetProperties())
  296. {
  297. EntityColumnInfo column = new EntityColumnInfo();
  298. //var isVirtual = property.GetGetMethod().IsVirtual;
  299. //if (isVirtual) continue;
  300. var navigat=property.GetCustomAttribute(typeof(Navigate));
  301. if (navigat != null)
  302. {
  303. column.IsIgnore = true;
  304. column.Navigat = navigat as Navigate;
  305. }
  306. var sugarColumn = property.GetCustomAttributes(typeof(SugarColumn), true)
  307. .Where(it => it is SugarColumn)
  308. .Select(it => (SugarColumn)it)
  309. .FirstOrDefault();
  310. column.ExtendedAttribute = sugarColumn?.ExtendedAttribute;
  311. column.DbTableName = result.DbTableName;
  312. column.EntityName = result.EntityName;
  313. column.PropertyName = property.Name;
  314. column.PropertyInfo = property;
  315. column.UnderType = UtilMethods.GetUnderType(column.PropertyInfo.PropertyType);
  316. if (sugarColumn?.IsOwnsOne==true)
  317. {
  318. SetValueObjectColumns(result, property, column);
  319. }
  320. if (sugarColumn.IsNullOrEmpty())
  321. {
  322. column.DbColumnName = property.Name;
  323. }
  324. else
  325. {
  326. if (sugarColumn.IsIgnore == false)
  327. {
  328. column.DbColumnName = sugarColumn.ColumnName.IsNullOrEmpty() ? property.Name : sugarColumn.ColumnName;
  329. column.IsPrimarykey = sugarColumn.IsPrimaryKey;
  330. column.IsIdentity = sugarColumn.IsIdentity;
  331. column.ColumnDescription = sugarColumn.ColumnDescription.ToSqlFilter();
  332. column.IsNullable = sugarColumn.IsNullable;
  333. column.Length = sugarColumn.Length;
  334. column.OldDbColumnName = sugarColumn.OldColumnName;
  335. column.DataType = sugarColumn.ColumnDataType;
  336. column.DecimalDigits = sugarColumn.DecimalDigits;
  337. column.OracleSequenceName = sugarColumn.OracleSequenceName;
  338. column.IsOnlyIgnoreInsert = sugarColumn.IsOnlyIgnoreInsert;
  339. column.IsEnableUpdateVersionValidation = sugarColumn.IsEnableUpdateVersionValidation;
  340. column.IsTranscoding = sugarColumn.IsTranscoding;
  341. column.SerializeDateTimeFormat = sugarColumn.SerializeDateTimeFormat;
  342. column.IsJson = sugarColumn.IsJson;
  343. column.NoSerialize = sugarColumn.NoSerialize;
  344. column.DefaultValue = sugarColumn.DefaultValue;
  345. column.IndexGroupNameList = sugarColumn.IndexGroupNameList;
  346. column.UIndexGroupNameList = sugarColumn.UniqueGroupNameList;
  347. column.IsOnlyIgnoreUpdate = sugarColumn.IsOnlyIgnoreUpdate;
  348. column.IsArray = sugarColumn.IsArray;
  349. column.IsTreeKey = sugarColumn.IsTreeKey;
  350. column.SqlParameterDbType = sugarColumn.SqlParameterDbType;
  351. column.SqlParameterSize = sugarColumn.SqlParameterSize;
  352. column.CreateTableFieldSort = sugarColumn.CreateTableFieldSort;
  353. column.InsertServerTime = sugarColumn.InsertServerTime;
  354. column.InsertSql = sugarColumn.InsertSql;
  355. column.UpdateServerTime= sugarColumn.UpdateServerTime;
  356. column.UpdateSql= sugarColumn.UpdateSql;
  357. column.IsDisabledAlterColumn = sugarColumn.IsDisabledAlterColumn;
  358. column.QuerySql = sugarColumn.QuerySql;
  359. if (sugarColumn.IsJson && String.IsNullOrEmpty(sugarColumn.ColumnDataType))
  360. {
  361. if (this.Context.CurrentConnectionConfig.DbType == DbType.PostgreSQL)
  362. {
  363. column.DataType = "json";
  364. }
  365. else if (column.Length > 0)
  366. {
  367. column.DataType = "varchar";
  368. }
  369. else
  370. {
  371. column.DataType = "varchar(4000)";
  372. }
  373. }
  374. else if (typeof(Nvarchar2PropertyConvert).Equals(sugarColumn.SqlParameterDbType)&&column.DataType==null)
  375. {
  376. column.DataType = "nvarchar2";
  377. if (column.Length == 0)
  378. {
  379. column.Length = 200;
  380. }
  381. }
  382. if (column.IsPrimarykey && column.IsOnlyIgnoreUpdate)
  383. {
  384. column.IsOnlyIgnoreUpdate = false;
  385. }
  386. }
  387. else
  388. {
  389. column.IsIgnore = true;
  390. column.NoSerialize = sugarColumn.NoSerialize;
  391. column.ColumnDescription = sugarColumn.ColumnDescription;
  392. column.IsJson = sugarColumn.IsJson;
  393. column.IsArray = sugarColumn.IsArray;
  394. }
  395. }
  396. if (column.ColumnDescription.IsNullOrEmpty()) column.ColumnDescription = GetPropertyAnnotation(result.Type, column.PropertyName);
  397. if (this.Context.MappingColumns.HasValue())
  398. {
  399. var golbalMappingInfo = this.Context.MappingColumns.FirstOrDefault(it => it.EntityName.Equals(result.EntityName, StringComparison.CurrentCultureIgnoreCase) && it.PropertyName == column.PropertyName);
  400. if (golbalMappingInfo != null)
  401. column.DbColumnName = golbalMappingInfo.DbColumnName;
  402. }
  403. if (this.Context.IgnoreColumns.HasValue())
  404. {
  405. var golbalMappingInfo = this.Context.IgnoreColumns.FirstOrDefault(it => it.EntityName.Equals(result.EntityName, StringComparison.CurrentCultureIgnoreCase) && it.PropertyName == column.PropertyName);
  406. if (golbalMappingInfo != null)
  407. column.IsIgnore = true;
  408. }
  409. if (this.Context.CurrentConnectionConfig.ConfigureExternalServices != null && this.Context.CurrentConnectionConfig.ConfigureExternalServices.EntityService != null)
  410. {
  411. if (!column.EntityName.ObjToString().StartsWith("<>f__AnonymousType"))
  412. {
  413. this.Context.CurrentConnectionConfig.ConfigureExternalServices.EntityService(property, column);
  414. }
  415. }
  416. if (column.PropertyInfo.DeclaringType != null
  417. && column.PropertyInfo.DeclaringType != result.Type
  418. &&result.Columns.Any(x=>x.PropertyName==column.PropertyName))
  419. {
  420. continue;
  421. }
  422. if (column.DataType == null&& property != null&& property.PropertyType.Name.IsIn("TimeOnly"))
  423. {
  424. column.DataType = "time";
  425. }
  426. if (column.DataType == null && property != null && property.PropertyType.Name.IsIn("DateOnly"))
  427. {
  428. column.DataType = "date";
  429. }
  430. if (column.DataType == null&&column.UnderType == typeof(TimeSpan) )
  431. {
  432. column.DataType = "time";
  433. column.Length = 0;
  434. column.DecimalDigits = 0;
  435. }
  436. if (column.OracleSequenceName.HasValue() &&
  437. this.Context.CurrentConnectionConfig?.MoreSettings?.EnableOracleIdentity == true)
  438. {
  439. column.OracleSequenceName = null;
  440. }
  441. result.Columns.Add(column);
  442. }
  443. }
  444. private void SetValueObjectColumns(EntityInfo result, PropertyInfo property, EntityColumnInfo column)
  445. {
  446. column.IsIgnore = true;
  447. column.IsOwnsOne = true;
  448. Check.ExceptionEasy(property.PropertyType.IsClass() == false, column.PropertyName + " IsOwnsOne必须用在类上面", column.PropertyName + "IsOwnsOne must be used on the class");
  449. Check.ExceptionEasy(property.PropertyType.FullName.IsCollectionsList() == true, column.PropertyName + " IsOwnsOne必须用在类上面", column.PropertyName + "IsOwnsOne must be used on the class");
  450. var ownsOne = this.GetEntityInfoNoCache(property.PropertyType);
  451. foreach (var item in ownsOne.Columns)
  452. {
  453. if (result.Columns.Any(it => it.PropertyName.EqualCase(item.PropertyName) || it.DbColumnName.EqualCase(item.DbColumnName)))
  454. {
  455. Check.ExceptionEasy($" {result.EntityName} "+ item.PropertyName+ " 存在重复定义 (IsOwnsOne) ", $" {result.EntityName} " + item.PropertyName + " Duplicate definition exists (IsOwnsOne)");
  456. }
  457. item.ForOwnsOnePropertyInfo = column.PropertyInfo;
  458. result.Columns.Add(item);
  459. }
  460. }
  461. #endregion
  462. }
  463. }