You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

320 lines
18 KiB

2 years ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Data;
  6. using System.Reflection;
  7. using System.Reflection.Emit;
  8. using System.Text.RegularExpressions;
  9. namespace SqlSugar
  10. {
  11. ///<summary>
  12. /// ** description:IDataReader Entity Builder
  13. /// ** author:sunkaixuan
  14. /// ** date:2017/4/2
  15. /// ** qq:610262374
  16. /// </summary>
  17. public partial class IDataReaderEntityBuilder<T>
  18. {
  19. #region Properies
  20. private List<string> ReaderKeys { get; set; }
  21. #endregion
  22. #region Fields
  23. private SqlSugarClient Context = null;
  24. private IDataReaderEntityBuilder<T> DynamicBuilder;
  25. private IDataRecord DataRecord;
  26. private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new Type[] { typeof(int) });
  27. private static readonly MethodInfo getValueMethod = typeof(IDataRecord).GetMethod("get_Item", new Type[] { typeof(int) });
  28. private static readonly MethodInfo getBoolean = typeof(IDataRecord).GetMethod("GetBoolean", new Type[] { typeof(int) });
  29. private static readonly MethodInfo getByte = typeof(IDataRecord).GetMethod("GetByte", new Type[] { typeof(int) });
  30. private static readonly MethodInfo getDateTime = typeof(IDataRecord).GetMethod("GetDateTime", new Type[] { typeof(int) });
  31. private static readonly MethodInfo getDecimal = typeof(IDataRecord).GetMethod("GetDecimal", new Type[] { typeof(int) });
  32. private static readonly MethodInfo getDouble = typeof(IDataRecord).GetMethod("GetDouble", new Type[] { typeof(int) });
  33. private static readonly MethodInfo getFloat = typeof(IDataRecord).GetMethod("GetFloat", new Type[] { typeof(int) });
  34. private static readonly MethodInfo getGuid = typeof(IDataRecord).GetMethod("GetGuid", new Type[] { typeof(int) });
  35. private static readonly MethodInfo getInt16 = typeof(IDataRecord).GetMethod("GetInt16", new Type[] { typeof(int) });
  36. private static readonly MethodInfo getInt32 = typeof(IDataRecord).GetMethod("GetInt32", new Type[] { typeof(int) });
  37. private static readonly MethodInfo getInt64 = typeof(IDataRecord).GetMethod("GetInt64", new Type[] { typeof(int) });
  38. private static readonly MethodInfo getString = typeof(IDataRecord).GetMethod("GetString", new Type[] { typeof(int) });
  39. private static readonly MethodInfo getdatetimeoffset = typeof(IDataRecordExtensions).GetMethod("Getdatetimeoffset");
  40. private static readonly MethodInfo getdatetimeoffsetDate = typeof(IDataRecordExtensions).GetMethod("GetdatetimeoffsetDate");
  41. private static readonly MethodInfo getStringGuid = typeof(IDataRecordExtensions).GetMethod("GetStringGuid");
  42. private static readonly MethodInfo getConvertStringGuid = typeof(IDataRecordExtensions).GetMethod("GetConvertStringGuid");
  43. private static readonly MethodInfo getEnum = typeof(IDataRecordExtensions).GetMethod("GetEnum");
  44. private static readonly MethodInfo getConvertString = typeof(IDataRecordExtensions).GetMethod("GetConvertString");
  45. private static readonly MethodInfo getConvertFloat = typeof(IDataRecordExtensions).GetMethod("GetConvertFloat");
  46. private static readonly MethodInfo getConvertBoolean = typeof(IDataRecordExtensions).GetMethod("GetConvertBoolean");
  47. private static readonly MethodInfo getConvertByte = typeof(IDataRecordExtensions).GetMethod("GetConvertByte");
  48. private static readonly MethodInfo getConvertChar = typeof(IDataRecordExtensions).GetMethod("GetConvertChar");
  49. private static readonly MethodInfo getConvertDateTime = typeof(IDataRecordExtensions).GetMethod("GetConvertDateTime");
  50. private static readonly MethodInfo getConvertDecimal = typeof(IDataRecordExtensions).GetMethod("GetConvertDecimal");
  51. private static readonly MethodInfo getConvertDouble = typeof(IDataRecordExtensions).GetMethod("GetConvertDouble");
  52. private static readonly MethodInfo getConvertGuid = typeof(IDataRecordExtensions).GetMethod("GetConvertGuid");
  53. private static readonly MethodInfo getConvertInt16 = typeof(IDataRecordExtensions).GetMethod("GetConvertInt16");
  54. private static readonly MethodInfo getConvertInt32 = typeof(IDataRecordExtensions).GetMethod("GetConvertInt32");
  55. private static readonly MethodInfo getConvertInt64 = typeof(IDataRecordExtensions).GetMethod("GetConvetInt64");
  56. private static readonly MethodInfo getConvertEnum_Null = typeof(IDataRecordExtensions).GetMethod("GetConvertEnum_Null");
  57. private static readonly MethodInfo getConvertdatetimeoffset = typeof(IDataRecordExtensions).GetMethod("GetConvertdatetimeoffset");
  58. private static readonly MethodInfo getConvertdatetimeoffsetDate = typeof(IDataRecordExtensions).GetMethod("GetConvertdatetimeoffsetDate");
  59. private static readonly MethodInfo getOtherNull = typeof(IDataRecordExtensions).GetMethod("GetOtherNull");
  60. private static readonly MethodInfo getOther = typeof(IDataRecordExtensions).GetMethod("GetOther");
  61. private static readonly MethodInfo getSqliteTypeNull = typeof(IDataRecordExtensions).GetMethod("GetSqliteTypeNull");
  62. private static readonly MethodInfo getSqliteType = typeof(IDataRecordExtensions).GetMethod("GetSqliteType");
  63. private static readonly MethodInfo getEntity = typeof(IDataRecordExtensions).GetMethod("GetEntity", new Type[] { typeof(SqlSugarClient) });
  64. private delegate T Load(IDataRecord dataRecord);
  65. private Load handler;
  66. #endregion
  67. #region Constructor
  68. private IDataReaderEntityBuilder()
  69. {
  70. }
  71. public IDataReaderEntityBuilder(SqlSugarClient context, IDataRecord dataRecord,List<string> fieldNames)
  72. {
  73. this.Context = context;
  74. this.DataRecord = dataRecord;
  75. this.DynamicBuilder = new IDataReaderEntityBuilder<T>();
  76. this.ReaderKeys = fieldNames;
  77. }
  78. #endregion
  79. #region Public methods
  80. public T Build(IDataRecord dataRecord)
  81. {
  82. return handler(dataRecord);
  83. }
  84. public IDataReaderEntityBuilder<T> CreateBuilder(Type type)
  85. {
  86. DynamicMethod method = new DynamicMethod("SqlSugarEntity", type,
  87. new Type[] { typeof(IDataRecord) }, type, true);
  88. ILGenerator generator = method.GetILGenerator();
  89. LocalBuilder result = generator.DeclareLocal(type);
  90. generator.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
  91. generator.Emit(OpCodes.Stloc, result);
  92. var mappingColumns = Context.MappingColumns.Where(it => it.EntityName.Equals(type.Name, StringComparison.CurrentCultureIgnoreCase)).ToList();
  93. var properties = type.GetProperties();
  94. foreach (var propertyInfo in properties)
  95. {
  96. string fileName = propertyInfo.Name;
  97. if (mappingColumns != null)
  98. {
  99. var mappInfo = mappingColumns.SingleOrDefault(it => it.EntityName == type.Name && it.PropertyName.Equals(propertyInfo.Name));
  100. if (mappInfo != null)
  101. {
  102. if (!ReaderKeys.Contains(mappInfo.DbColumnName))
  103. {
  104. fileName = ReaderKeys.First(it => it.Equals(mappInfo.DbColumnName, StringComparison.CurrentCultureIgnoreCase)|| it.Equals(mappInfo.PropertyName, StringComparison.CurrentCultureIgnoreCase));
  105. }
  106. else
  107. {
  108. fileName = mappInfo.DbColumnName;
  109. }
  110. }
  111. }
  112. if (IsIgnore(type, propertyInfo)&&!this.ReaderKeys.Any(it=>it==fileName))
  113. {
  114. continue;
  115. }
  116. if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
  117. {
  118. if (propertyInfo.PropertyType.IsClass() && propertyInfo.PropertyType != UtilConstants.ByteArrayType && propertyInfo.PropertyType != UtilConstants.ObjType)
  119. {
  120. BindClass(generator, result, propertyInfo);
  121. }
  122. else
  123. {
  124. if (this.ReaderKeys.Any(it => it.Equals(fileName, StringComparison.CurrentCultureIgnoreCase)))
  125. {
  126. BindField(generator, result, propertyInfo, ReaderKeys.First(it => it.Equals(fileName, StringComparison.CurrentCultureIgnoreCase)));
  127. }
  128. }
  129. }
  130. }
  131. generator.Emit(OpCodes.Ldloc, result);
  132. generator.Emit(OpCodes.Ret);
  133. DynamicBuilder.handler = (Load)method.CreateDelegate(typeof(Load));
  134. return DynamicBuilder;
  135. }
  136. #endregion
  137. #region Private methods
  138. private bool IsIgnore(Type type, PropertyInfo propertyInfo)
  139. {
  140. return Context.IgnoreColumns != null && Context.IgnoreColumns.Any(it => it.PropertyName.Equals(propertyInfo.Name, StringComparison.CurrentCultureIgnoreCase)
  141. && it.EntityName.Equals(type.Name, StringComparison.CurrentCultureIgnoreCase));
  142. }
  143. private void BindClass(ILGenerator generator, LocalBuilder result, PropertyInfo propertyInfo)
  144. {
  145. }
  146. private void BindField(ILGenerator generator, LocalBuilder result, PropertyInfo propertyInfo, string fileName)
  147. {
  148. int i = DataRecord.GetOrdinal(fileName);
  149. Label endIfLabel = generator.DefineLabel();
  150. generator.Emit(OpCodes.Ldarg_0);
  151. generator.Emit(OpCodes.Ldc_I4, i);
  152. generator.Emit(OpCodes.Callvirt, isDBNullMethod);
  153. generator.Emit(OpCodes.Brtrue, endIfLabel);
  154. generator.Emit(OpCodes.Ldloc, result);
  155. generator.Emit(OpCodes.Ldarg_0);
  156. generator.Emit(OpCodes.Ldc_I4, i);
  157. BindMethod(generator, propertyInfo, i);
  158. generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
  159. generator.MarkLabel(endIfLabel);
  160. }
  161. private void BindMethod(ILGenerator generator, PropertyInfo bindProperty, int ordinal)
  162. {
  163. IDbBind bind = Context.Ado.DbBind;
  164. bool isNullableType = false;
  165. MethodInfo method = null;
  166. Type bindPropertyType = UtilMethods.GetUnderType(bindProperty, ref isNullableType);
  167. string dbTypeName = UtilMethods.GetParenthesesValue(DataRecord.GetDataTypeName(ordinal));
  168. string propertyName = bindProperty.Name;
  169. string validPropertyName = bind.GetPropertyTypeName(dbTypeName);
  170. validPropertyName = validPropertyName == "byte[]" ? "byteArray" : validPropertyName;
  171. CSharpDataType validPropertyType = (CSharpDataType)Enum.Parse(typeof(CSharpDataType), validPropertyName);
  172. #region Sqlite Logic
  173. if (this.Context.CurrentConnectionConfig.DbType == DbType.Sqlite)
  174. {
  175. if (bindPropertyType.IsEnum())
  176. {
  177. method = isNullableType ? getConvertEnum_Null.MakeGenericMethod(bindPropertyType) : getEnum.MakeGenericMethod(bindPropertyType);
  178. }
  179. else if (bindPropertyType == UtilConstants.IntType)
  180. {
  181. method = isNullableType ? getConvertInt32 : getInt32;
  182. }
  183. else if (bindPropertyType == UtilConstants.StringType)
  184. {
  185. method = getString;
  186. }
  187. else if (bindPropertyType == UtilConstants.ByteArrayType)
  188. {
  189. method = getValueMethod;
  190. generator.Emit(OpCodes.Call, method);
  191. generator.Emit(OpCodes.Unbox_Any, bindProperty.PropertyType);
  192. return;
  193. }
  194. else
  195. {
  196. method = isNullableType ? getSqliteTypeNull.MakeGenericMethod(bindPropertyType) : getSqliteType.MakeGenericMethod(bindPropertyType);
  197. }
  198. generator.Emit(OpCodes.Call, method);
  199. return;
  200. };
  201. #endregion
  202. #region Common Database Logic
  203. string bindProperyTypeName = bindPropertyType.Name.ToLower();
  204. bool isEnum = bindPropertyType.IsEnum();
  205. if (isEnum) { validPropertyType = CSharpDataType.@enum; }
  206. switch (validPropertyType)
  207. {
  208. case CSharpDataType.@int:
  209. CheckType(bind.IntThrow, bindProperyTypeName, validPropertyName, propertyName);
  210. if (bindProperyTypeName.IsContainsIn("int", "int32"))
  211. method = isNullableType ? getConvertInt32 : getInt32;
  212. if (bindProperyTypeName.IsContainsIn("int64"))
  213. method = isNullableType ? getConvertInt64 : getInt64;
  214. if (bindProperyTypeName.IsContainsIn("byte"))
  215. method = isNullableType ? getConvertByte : getByte;
  216. if (bindProperyTypeName.IsContainsIn("int16"))
  217. method = isNullableType ? getConvertInt16 : getInt16;
  218. break;
  219. case CSharpDataType.@bool:
  220. if (bindProperyTypeName == "bool" || bindProperyTypeName == "boolean")
  221. method = isNullableType ? getConvertBoolean : getBoolean;
  222. break;
  223. case CSharpDataType.@string:
  224. CheckType(bind.StringThrow, bindProperyTypeName, validPropertyName, propertyName);
  225. method = getString;
  226. if (bindProperyTypeName == "guid")
  227. {
  228. method = isNullableType ? getConvertStringGuid : getStringGuid;
  229. }
  230. break;
  231. case CSharpDataType.DateTime:
  232. CheckType(bind.DateThrow, bindProperyTypeName, validPropertyName, propertyName);
  233. if (bindProperyTypeName == "datetime")
  234. method = isNullableType ? getConvertDateTime : getDateTime;
  235. break;
  236. case CSharpDataType.@decimal:
  237. CheckType(bind.DecimalThrow, bindProperyTypeName, validPropertyName, propertyName);
  238. if (bindProperyTypeName == "decimal")
  239. method = isNullableType ? getConvertDecimal : getDecimal;
  240. break;
  241. case CSharpDataType.@float:
  242. case CSharpDataType.@double:
  243. CheckType(bind.DoubleThrow, bindProperyTypeName, validPropertyName, propertyName);
  244. if (bindProperyTypeName.IsIn( "double", "single")&&dbTypeName!="real")
  245. method = isNullableType ? getConvertDouble : getDouble;
  246. else
  247. method = isNullableType ? getConvertFloat : getFloat;
  248. break;
  249. case CSharpDataType.Guid:
  250. CheckType(bind.GuidThrow, bindProperyTypeName, validPropertyName, propertyName);
  251. if (bindProperyTypeName == "guid")
  252. method = isNullableType ? getConvertGuid : getGuid;
  253. break;
  254. case CSharpDataType.@byte:
  255. if (bindProperyTypeName == "byte")
  256. method = isNullableType ? getConvertByte : getByte;
  257. break;
  258. case CSharpDataType.@enum:
  259. method = isNullableType ? getConvertEnum_Null.MakeGenericMethod(bindPropertyType) : getEnum.MakeGenericMethod(bindPropertyType);
  260. break;
  261. case CSharpDataType.@short:
  262. CheckType(bind.ShortThrow, bindProperyTypeName, validPropertyName, propertyName);
  263. if (bindProperyTypeName == "int16" || bindProperyTypeName == "short")
  264. method = isNullableType ? getConvertInt16 : getInt16;
  265. break;
  266. case CSharpDataType.@long:
  267. if (bindProperyTypeName == "int64" || bindProperyTypeName == "long")
  268. method = isNullableType ? getConvertInt64 : getInt64;
  269. break;
  270. case CSharpDataType.DateTimeOffset:
  271. method = isNullableType ? getConvertdatetimeoffset : getdatetimeoffset;
  272. if (bindProperyTypeName == "datetime")
  273. method = isNullableType ? getConvertdatetimeoffsetDate : getdatetimeoffsetDate;
  274. break;
  275. default:
  276. method = getValueMethod;
  277. break;
  278. }
  279. if (method == null && bindPropertyType == UtilConstants.StringType)
  280. {
  281. method = getConvertString;
  282. }
  283. if (bindPropertyType == UtilConstants.ObjType)
  284. {
  285. method = getValueMethod;
  286. }
  287. if (method == null)
  288. method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
  289. generator.Emit(OpCodes.Call, method);
  290. if (method == getValueMethod)
  291. {
  292. generator.Emit(OpCodes.Unbox_Any, bindProperty.PropertyType);
  293. }
  294. #endregion
  295. }
  296. private void CheckType(List<string> invalidTypes, string bindProperyTypeName, string validPropertyType, string propertyName)
  297. {
  298. var isAny = invalidTypes.Contains(bindProperyTypeName);
  299. if (isAny)
  300. {
  301. throw new UtilExceptions(string.Format("{0} can't convert {1} to {2}", propertyName, validPropertyType, bindProperyTypeName));
  302. }
  303. }
  304. #endregion
  305. }
  306. }