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.

1672 lines
76 KiB

5 years ago
4 years ago
4 years ago
5 years ago
4 years ago
5 years ago
3 years ago
5 years ago
4 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
4 years ago
5 years ago
3 years ago
5 years ago
5 years ago
5 years ago
5 years ago
3 years ago
5 years ago
5 years ago
3 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
4 years ago
5 years ago
4 years ago
3 years ago
4 years ago
5 years ago
5 years ago
5 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
5 years ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data;
  4. using System.Reflection;
  5. using System.Runtime.InteropServices.ComTypes;
  6. using System.Text;
  7. namespace Apewer.Source
  8. {
  9. /// <summary>ORM 帮助程序。</summary>
  10. public static class SourceUtility
  11. {
  12. #region ORM
  13. /// <summary>读取所有行,生成列表。</summary>
  14. public static T[] Fill<T>(this IQuery query) where T : class, new()
  15. {
  16. var objects = Fill(query, typeof(T));
  17. var array = CollectionUtility.As<object, T>(objects);
  18. return array;
  19. }
  20. /// <summary>读取所有行填充到 T,组成 T[]。</summary>
  21. /// <exception cref="ArgumentNullException"></exception>
  22. /// <exception cref="ArgumentException"></exception>
  23. public static object[] Fill(this IQuery query, Type model)
  24. {
  25. if (query == null) return new object[0];
  26. if (query.Table == null) return new object[0];
  27. if (model == null) return new object[0];
  28. return Fill(query.Table, model);
  29. }
  30. /// <summary>将 Query 的行,填充到模型实体。</summary>
  31. /// <remarks>填充失败时返回 NULL 值。</remarks>
  32. /// <exception cref="Exception"></exception>
  33. public static object FillRow(IQuery query, int rowIndex, Type model, TableStructure structure) => FillRow(query?.Table, rowIndex, model, structure);
  34. /// <summary>将 Query 的行,填充到模型实体。</summary>
  35. /// <remarks>填充失败时返回 NULL 值。</remarks>
  36. /// <exception cref="Exception"></exception>
  37. public static object FillRow(DataTable table, int rowIndex, Type model, TableStructure structure)
  38. {
  39. // 检查参数。
  40. if (table == null || model == null || structure == null) return null;
  41. if (rowIndex < 0 || rowIndex >= table.Rows.Count) return null;
  42. if (!RuntimeUtility.CanNew(model)) return null;
  43. // 变量别名。
  44. var ts = structure;
  45. var r = rowIndex;
  46. var columns = ts.Columns;
  47. // 检查模型的属性,按属性从表中取相应的列。
  48. var record = Activator.CreateInstance(model);
  49. var properties = model.GetProperties();
  50. foreach (var property in properties)
  51. {
  52. // 在表结构中检查,是否包含此属性,并获取 ColumnAttribute 中的 Field。
  53. var field = null as string;
  54. for (var j = 0; j < columns.Length; j++)
  55. {
  56. if (columns[j].PropertyName == property.Name)
  57. {
  58. field = columns[j].Field;
  59. break;
  60. }
  61. }
  62. if (field == null)
  63. {
  64. if (ts && ts.Table.AllProperties) continue;
  65. field = property.Name;
  66. }
  67. var value = table.Rows[r][field];
  68. if (value != null && value.Equals(DBNull.Value)) value = null;
  69. var setted = Set(record, property, value);
  70. }
  71. return record;
  72. }
  73. static bool Set(object record, PropertyInfo property, object value)
  74. {
  75. // 读取值。
  76. if (value == null) return false;
  77. if (value.Equals(DBNull.Value)) return false;
  78. // 必须有 setter 访问器。
  79. var setter = property.GetSetMethod();
  80. if (setter == null) return false;
  81. // 根据属性类型设置值。
  82. var pt = property.PropertyType;
  83. if (pt.Equals(typeof(object))) setter.Invoke(record, new object[] { value });
  84. else if (pt.Equals(typeof(byte[]))) setter.Invoke(record, new object[] { (byte[])value });
  85. else if (pt.Equals(typeof(string))) setter.Invoke(record, new object[] { value.ToString() });
  86. else if (pt.Equals(typeof(DateTime))) setter.Invoke(record, new object[] { value });
  87. else if (pt.Equals(typeof(bool))) setter.Invoke(record, new object[] { NumberUtility.Boolean(value) });
  88. else if (pt.Equals(typeof(byte))) setter.Invoke(record, new object[] { NumberUtility.Byte(value) });
  89. else if (pt.Equals(typeof(sbyte))) setter.Invoke(record, new object[] { NumberUtility.SByte(value) });
  90. else if (pt.Equals(typeof(short))) setter.Invoke(record, new object[] { NumberUtility.Int16(value) });
  91. else if (pt.Equals(typeof(ushort))) setter.Invoke(record, new object[] { NumberUtility.UInt16(value) });
  92. else if (pt.Equals(typeof(int))) setter.Invoke(record, new object[] { NumberUtility.Int32(value) });
  93. else if (pt.Equals(typeof(uint))) setter.Invoke(record, new object[] { NumberUtility.UInt32(value) });
  94. else if (pt.Equals(typeof(long))) setter.Invoke(record, new object[] { NumberUtility.Int64(value) });
  95. else if (pt.Equals(typeof(ulong))) setter.Invoke(record, new object[] { NumberUtility.UInt64(value) });
  96. else if (pt.Equals(typeof(float))) setter.Invoke(record, new object[] { NumberUtility.Single(value) });
  97. else if (pt.Equals(typeof(double))) setter.Invoke(record, new object[] { NumberUtility.Double(value) });
  98. else if (pt.Equals(typeof(decimal))) setter.Invoke(record, new object[] { NumberUtility.Decimal(value) });
  99. #if !NET20
  100. else if (pt.Equals(typeof(Nullable<DateTime>))) setter.Invoke(record, new object[] { new Nullable<DateTime>((DateTime)value) });
  101. else if (pt.Equals(typeof(Nullable<bool>))) setter.Invoke(record, new object[] { new Nullable<bool>(NumberUtility.Boolean(value)) });
  102. else if (pt.Equals(typeof(Nullable<byte>))) setter.Invoke(record, new object[] { new Nullable<byte>(NumberUtility.Byte(value)) });
  103. else if (pt.Equals(typeof(Nullable<sbyte>))) setter.Invoke(record, new object[] { new Nullable<sbyte>(NumberUtility.SByte(value)) });
  104. else if (pt.Equals(typeof(Nullable<short>))) setter.Invoke(record, new object[] { new Nullable<short>(NumberUtility.Int16(value)) });
  105. else if (pt.Equals(typeof(Nullable<ushort>))) setter.Invoke(record, new object[] { new Nullable<int>(NumberUtility.UInt16(value)) });
  106. else if (pt.Equals(typeof(Nullable<int>))) setter.Invoke(record, new object[] { new Nullable<int>(NumberUtility.Int32(value)) });
  107. else if (pt.Equals(typeof(Nullable<uint>))) setter.Invoke(record, new object[] { new Nullable<uint>(NumberUtility.UInt32(value)) });
  108. else if (pt.Equals(typeof(Nullable<long>))) setter.Invoke(record, new object[] { new Nullable<long>(NumberUtility.Int64(value)) });
  109. else if (pt.Equals(typeof(Nullable<ulong>))) setter.Invoke(record, new object[] { new Nullable<ulong>(NumberUtility.UInt64(value)) });
  110. else if (pt.Equals(typeof(Nullable<float>))) setter.Invoke(record, new object[] { new Nullable<float>(NumberUtility.Single(value)) });
  111. else if (pt.Equals(typeof(Nullable<double>))) setter.Invoke(record, new object[] { new Nullable<double>(NumberUtility.Double(value)) });
  112. else if (pt.Equals(typeof(Nullable<decimal>))) setter.Invoke(record, new object[] { new Nullable<decimal>(NumberUtility.Decimal(value)) });
  113. #endif
  114. else
  115. {
  116. try
  117. {
  118. setter.Invoke(record, new object[] { value });
  119. return true;
  120. }
  121. catch { }
  122. }
  123. return false;
  124. }
  125. /// <summary>解析 DataTable,填充没行到到指定的类型中,形成数组。</summary>
  126. /// <param name="table">将要读取的表。</param>
  127. /// <param name="compatible">当类型不同时,尝试转换以兼容。</param>
  128. /// <returns>由指定类型组成的数组。</returns>
  129. /// <exception cref="ArgumentNullException"></exception>
  130. /// <exception cref="ArgumentException"></exception>
  131. public static T[] Fill<T>(this DataTable table, bool compatible = true)
  132. {
  133. if (table == null) throw new ArgumentNullException(nameof(table), $"参数 {table} 无效。");
  134. var objects = Fill(table, typeof(T), compatible);
  135. var count = objects.Length;
  136. var array = new T[count];
  137. for (var i = 0; i < count; i++) array[i] = (T)objects[i];
  138. return array;
  139. }
  140. /// <summary>解析 DataTable,填充没行到到指定的类型中,形成数组。</summary>
  141. /// <param name="table">将要读取的表。</param>
  142. /// <param name="model">要填充的目标类型,必须是可实例化的引用类型。</param>
  143. /// <param name="compatible">当类型不同时,尝试转换以兼容。</param>
  144. /// <returns>由指定类型组成的数组。</returns>
  145. /// <exception cref="ArgumentNullException"></exception>
  146. /// <exception cref="ArgumentException"></exception>
  147. public static object[] Fill(this DataTable table, Type model, bool compatible = true)
  148. {
  149. if (table == null) throw new ArgumentNullException(nameof(table), $"参数 {table} 无效。");
  150. if (model == null) throw new ArgumentNullException(nameof(model), $"参数 {model} 无效。");
  151. // 检查模型是否允许填充。
  152. var ts = TableStructure.Parse(model, true, true);
  153. if (ts == null) throw new ArgumentException($"无法填充到类型 {model.FullName} 中。");
  154. // 检查行数。
  155. var rows = table.Rows;
  156. var rowsCount = rows.Count;
  157. if (rowsCount < 1) return new object[0];
  158. // 确定数组。
  159. var array = new object[rowsCount];
  160. for (var i = 0; i < rowsCount; i++) array[i] = Activator.CreateInstance(model, true);
  161. // 检查列数。
  162. var columns = table.Columns;
  163. var columnsCount = columns.Count;
  164. if (columnsCount < 1) return array;
  165. // 解析表头,仅保留有名称的列。
  166. var sc = 0;
  167. var sfs = new string[columnsCount];
  168. var sts = new Type[columnsCount];
  169. var sis = new int[columnsCount];
  170. for (var i = 0; i < columnsCount; i++)
  171. {
  172. var column = columns[i];
  173. var key = column.ColumnName.Lower();
  174. if (string.IsNullOrEmpty(key)) continue;
  175. if (sfs.Contains(key)) continue;
  176. sfs[sc] = key;
  177. sts[sc] = column.DataType;
  178. sis[sc] = i;
  179. sc++;
  180. }
  181. if (sc < 1) return array;
  182. // 解析模型列。
  183. var cas = ts.Fillable;
  184. var dc = 0;
  185. var dfs = new string[cas.Length];
  186. var dts = new ColumnAttribute[cas.Length];
  187. for (var i = 0; i < cas.Length; i++)
  188. {
  189. var ca = cas[i];
  190. var key = ca.Field.Lower();
  191. if (string.IsNullOrEmpty(key)) continue;
  192. if (dfs.Contains(key)) continue;
  193. dfs[dc] = key;
  194. dts[dc] = ca;
  195. dc++;
  196. }
  197. if (dc < 1) return array;
  198. // 遍历、填充。
  199. for (var r = 0; r < rowsCount; r++)
  200. {
  201. var record = array[r];
  202. // 遍历 table 的列。
  203. for (var s = 0; s < sc; s++)
  204. {
  205. var sf = sfs[s];
  206. // 遍历 model 的列。
  207. for (var d = 0; d < dc; d++)
  208. {
  209. var df = dfs[d];
  210. if (df != sf) continue;
  211. // 取值、填充。
  212. var value = rows[r][sis[s]];
  213. Fill(record, dts[d], sts[s], value, compatible);
  214. break;
  215. }
  216. }
  217. }
  218. return array;
  219. }
  220. static bool Fill(object record, ColumnAttribute ca, Type st, object value, bool compatible)
  221. {
  222. // 如果是 NULL 则忽略填充。
  223. if (value.IsNull()) return false;
  224. // 获取属性的类型,必须与 table 中的类型相同。
  225. var prop = ca.Property;
  226. if (prop.PropertyType == st)
  227. {
  228. prop.SetValue(record, value, null);
  229. return true;
  230. }
  231. // 类型不同且不需要兼容时,不填充。
  232. if (!compatible) return false;
  233. // 根据属性类型设置值。
  234. var pt = prop.PropertyType;
  235. if (pt.Equals(typeof(object))) prop.SetValue(record, value, null);
  236. else if (pt.Equals(typeof(byte[]))) prop.SetValue(record, (byte[])value, null);
  237. else if (pt.Equals(typeof(string))) prop.SetValue(record, value.ToString(), null);
  238. else if (pt.Equals(typeof(DateTime))) prop.SetValue(record, value, null);
  239. else if (pt.Equals(typeof(bool))) prop.SetValue(record, NumberUtility.Boolean(value), null);
  240. else if (pt.Equals(typeof(byte))) prop.SetValue(record, NumberUtility.Byte(value), null);
  241. else if (pt.Equals(typeof(sbyte))) prop.SetValue(record, NumberUtility.SByte(value), null);
  242. else if (pt.Equals(typeof(short))) prop.SetValue(record, NumberUtility.Int16(value), null);
  243. else if (pt.Equals(typeof(ushort))) prop.SetValue(record, NumberUtility.UInt16(value), null);
  244. else if (pt.Equals(typeof(int))) prop.SetValue(record, NumberUtility.Int32(value), null);
  245. else if (pt.Equals(typeof(uint))) prop.SetValue(record, NumberUtility.UInt32(value), null);
  246. else if (pt.Equals(typeof(long))) prop.SetValue(record, NumberUtility.Int64(value), null);
  247. else if (pt.Equals(typeof(ulong))) prop.SetValue(record, NumberUtility.UInt64(value), null);
  248. else if (pt.Equals(typeof(float))) prop.SetValue(record, NumberUtility.Single(value), null);
  249. else if (pt.Equals(typeof(double))) prop.SetValue(record, NumberUtility.Double(value), null);
  250. else if (pt.Equals(typeof(decimal))) prop.SetValue(record, NumberUtility.Decimal(value), null);
  251. else if (pt.Equals(typeof(Nullable<DateTime>))) prop.SetValue(record, new Nullable<DateTime>((DateTime)value), null);
  252. else if (pt.Equals(typeof(Nullable<bool>))) prop.SetValue(record, new Nullable<bool>(NumberUtility.Boolean(value)), null);
  253. else if (pt.Equals(typeof(Nullable<byte>))) prop.SetValue(record, new Nullable<byte>(NumberUtility.Byte(value)), null);
  254. else if (pt.Equals(typeof(Nullable<sbyte>))) prop.SetValue(record, new Nullable<sbyte>(NumberUtility.SByte(value)), null);
  255. else if (pt.Equals(typeof(Nullable<short>))) prop.SetValue(record, new Nullable<short>(NumberUtility.Int16(value)), null);
  256. else if (pt.Equals(typeof(Nullable<ushort>))) prop.SetValue(record, new Nullable<int>(NumberUtility.UInt16(value)), null);
  257. else if (pt.Equals(typeof(Nullable<int>))) prop.SetValue(record, new Nullable<int>(NumberUtility.Int32(value)), null);
  258. else if (pt.Equals(typeof(Nullable<uint>))) prop.SetValue(record, new Nullable<uint>(NumberUtility.UInt32(value)), null);
  259. else if (pt.Equals(typeof(Nullable<long>))) prop.SetValue(record, new Nullable<long>(NumberUtility.Int64(value)), null);
  260. else if (pt.Equals(typeof(Nullable<ulong>))) prop.SetValue(record, new Nullable<ulong>(NumberUtility.UInt64(value)), null);
  261. else if (pt.Equals(typeof(Nullable<float>))) prop.SetValue(record, new Nullable<float>(NumberUtility.Single(value)), null);
  262. else if (pt.Equals(typeof(Nullable<double>))) prop.SetValue(record, new Nullable<double>(NumberUtility.Double(value)), null);
  263. else if (pt.Equals(typeof(Nullable<decimal>))) prop.SetValue(record, new Nullable<decimal>(NumberUtility.Decimal(value)), null);
  264. else
  265. {
  266. try
  267. {
  268. prop.SetValue(record, value, null);
  269. return true;
  270. }
  271. catch { }
  272. }
  273. return false;
  274. }
  275. #endregion
  276. #region Record
  277. /// <summary>修复记录属性。</summary>
  278. public static void FixProperties(object record)
  279. {
  280. if (record == null) return;
  281. if (record is IRecord key)
  282. {
  283. if (string.IsNullOrEmpty(key.Key)) key.ResetKey();
  284. }
  285. if (record is IRecordMoment moment)
  286. {
  287. var now = moment.GenerateMoment();
  288. if (string.IsNullOrEmpty(moment.Created)) moment.Created = now;
  289. if (string.IsNullOrEmpty(moment.Updated)) moment.Updated = now;
  290. }
  291. if (record is IRecordStamp stamp)
  292. {
  293. var now = stamp.GenerateStamp();
  294. if (stamp.Created == 0L) stamp.Created = now;
  295. if (stamp.Updated == 0L) stamp.Updated = now;
  296. }
  297. // 类型缓存
  298. var DateTimeType = typeof(DateTime);
  299. var MinDateTime = new DateTime(1753, 1, 1, 0, 0, 0, 0);
  300. var MaxDateTime = new DateTime(9999, 12, 31, 23, 59, 59, 997);
  301. var NullableDateTimeType = typeof(DateTime?);
  302. var StringType = typeof(string);
  303. #if DEBUG // 待测试
  304. // 遍历字段
  305. var recordType = record.GetType();
  306. var tableStructure = TableStructure.Parse(recordType);
  307. foreach (var column in tableStructure.Columns)
  308. {
  309. // 字符串 NOT NULL
  310. if (column.Property.PropertyType.Equals(StringType))
  311. {
  312. var value = column.Property.GetGetMethod().Invoke(record, null);
  313. if (value == null)
  314. {
  315. var notNull = RuntimeUtility.GetAttribute<NotNullAttribute>(column.Property);
  316. if (notNull != null)
  317. {
  318. column.Property.GetSetMethod().Invoke(record, new object[] { "" });
  319. }
  320. }
  321. continue;
  322. }
  323. // DateTime
  324. // min = 1753-01-01 00:00:00.000
  325. // max = 9999-12-31 23:59:59.997
  326. if (column.Property.PropertyType.Equals(DateTimeType))
  327. {
  328. var value = (DateTime)column.Property.GetGetMethod().Invoke(record, null);
  329. if (value < MinDateTime)
  330. {
  331. column.Property.GetSetMethod().Invoke(record, new object[] { MinDateTime });
  332. }
  333. else if (value > MaxDateTime)
  334. {
  335. column.Property.GetSetMethod().Invoke(record, new object[] { MaxDateTime });
  336. }
  337. continue;
  338. }
  339. if (column.Property.PropertyType.Equals(NullableDateTimeType))
  340. {
  341. var value = (DateTime?)column.Property.GetGetMethod().Invoke(record, null);
  342. if (value == null)
  343. {
  344. var notNull = RuntimeUtility.GetAttribute<NotNullAttribute>(column.Property);
  345. if (notNull != null)
  346. {
  347. column.Property.GetSetMethod().Invoke(record, new object[] { MinDateTime });
  348. }
  349. }
  350. else
  351. {
  352. if (value < MinDateTime)
  353. {
  354. column.Property.GetSetMethod().Invoke(record, new object[] { MinDateTime });
  355. }
  356. else if (value > MaxDateTime)
  357. {
  358. column.Property.GetSetMethod().Invoke(record, new object[] { MaxDateTime });
  359. }
  360. }
  361. continue;
  362. }
  363. }
  364. #endif
  365. }
  366. /// <summary>设置 Updated 属性。</summary>
  367. /// <returns>TRUE:设置成功;FALSE:设置失败。</returns>
  368. public static bool SetUpdated(object record)
  369. {
  370. if (record == null) return false;
  371. var setted = false;
  372. if (record is IRecordMoment moment)
  373. {
  374. moment.Updated = moment.GenerateMoment();
  375. setted = true;
  376. }
  377. if (record is IRecordStamp stamp)
  378. {
  379. stamp.Updated = stamp.GenerateStamp();
  380. setted = true;
  381. }
  382. return setted;
  383. }
  384. /// <summary>枚举带有 Table 特性的 <typeparamref name="T"/> 派生类型。</summary>
  385. public static Type[] EnumerateRecords<T>() where T : IRecord => EnumerateRecords(typeof(T));
  386. /// <summary>枚举带有 Table 特性的派生类型。</summary>
  387. /// <exception cref="ArgumentNullException"></exception>
  388. public static Type[] EnumerateRecords(Type baseType)
  389. {
  390. if (baseType == null) throw new ArgumentNullException(nameof(baseType));
  391. var assemblies = AppDomain.CurrentDomain.GetAssemblies();
  392. var builder = new ArrayBuilder<Type>();
  393. foreach (var assembly in assemblies)
  394. {
  395. var types = RuntimeUtility.GetTypes(assembly);
  396. foreach (var type in types)
  397. {
  398. if (!EnumerateRecords(type, baseType)) continue;
  399. if (builder.Contains(type)) continue;
  400. builder.Add(type);
  401. }
  402. }
  403. return builder.Export();
  404. }
  405. static bool EnumerateRecords(Type type, Type @base)
  406. {
  407. if (type == null || @base == null) return false;
  408. if (type.IsAbstract) return false;
  409. if (!RuntimeUtility.Contains<TableAttribute>(type, false)) return false;
  410. if (type.Equals(@base)) return true;
  411. if (RuntimeUtility.IsInherits(type, @base)) return true;
  412. return false;
  413. }
  414. #endregion
  415. #region Query
  416. /// <summary>简单查询:取结果中第 0 列所有单元格的文本形式,返回结果中不包含无效文本和空文本。</summary>
  417. /// <param name="source">数据库客户端。</param>
  418. /// <param name="sql">用于查询的 SQL 语句。</param>
  419. /// <param name="parameters">SQL 参数。</param>
  420. /// <exception cref="SqlException"></exception>
  421. public static string[] Column(this IDbAdo source, string sql, object parameters = null) => Column(source, sql, parameters, TextUtility.Text).FindAll(x => x.NotEmpty());
  422. /// <summary>简单查询:取结果中第 0 列所有单元格。</summary>
  423. /// <param name="source">数据库客户端。</param>
  424. /// <param name="sql">用于查询的 SQL 语句。</param>
  425. /// <param name="parameters">SQL 参数。</param>
  426. /// <param name="getter">获取单元格内容的程序。value 有可能是 NULL 值,需要 getter 自行处理。</param>
  427. /// <exception cref="ArgumentNullException"></exception>
  428. /// <exception cref="SqlException"></exception>
  429. public static T[] Column<T>(this IDbAdo source, string sql, object parameters, Func<object, T> getter)
  430. {
  431. if (source == null) throw new ArgumentNullException(nameof(source));
  432. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  433. if (getter == null) throw new ArgumentNullException(nameof(getter));
  434. using (var query = source.Query(sql, parameters))
  435. {
  436. if (query == null) throw new SqlException("方法 IDbAdo.Query 返回了 NULL 值。");
  437. if (!query.Success) throw new SqlException(query, sql);
  438. var rows = query.Rows;
  439. if (rows < 1) return new T[0];
  440. var list = new List<T>();
  441. list.Capacity = rows;
  442. for (int i = 0; i < rows; i++)
  443. {
  444. var value = query.Value(i, 0);
  445. if (value != null && value.Equals(DBNull.Value)) value = null;
  446. // 此处 value 有可能是 null
  447. var item = getter.Invoke(value);
  448. list.Add(item);
  449. }
  450. return list.ToArray();
  451. }
  452. }
  453. /// <summary>简单查询:取结果中第 0 行、第 0 列单元格中的文本。</summary>
  454. /// <param name="source">数据库客户端。</param>
  455. /// <param name="sql">用于查询的 SQL 语句。</param>
  456. /// <param name="parameters">SQL 参数。</param>
  457. /// <exception cref="ArgumentNullException"></exception>
  458. /// <exception cref="SqlException"></exception>
  459. public static string Cell(this IDbAdo source, string sql, object parameters = null) => Cell(source, sql, parameters, TextUtility.Text);
  460. /// <summary>简单查询:取结果中第 0 行、第 0 列单元格。</summary>
  461. /// <param name="source">数据库客户端。</param>
  462. /// <param name="sql">用于查询的 SQL 语句。</param>
  463. /// <param name="parameters">SQL 参数。</param>
  464. /// <param name="getter">获取单元格内容的程序。value 有可能是 NULL 值,需要 getter 自行处理。</param>
  465. /// <exception cref="ArgumentNullException"></exception>
  466. /// <exception cref="SqlException"></exception>
  467. public static T Cell<T>(this IDbAdo source, string sql, object parameters, Func<object, T> getter)
  468. {
  469. if (source == null) throw new ArgumentNullException(nameof(source));
  470. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  471. if (getter == null) throw new ArgumentNullException(nameof(getter));
  472. using (var query = source.Query(sql, parameters))
  473. {
  474. if (query == null) throw new SqlException("方法 IDbAdo.Query 返回了 NULL 值。");
  475. if (!query.Success) throw new SqlException(query, sql);
  476. var value = query.Value(0, 0);
  477. if (value != null && value.Equals(DBNull.Value)) value = null;
  478. // 此处 value 有可能是 null
  479. var result = getter.Invoke(value);
  480. return result;
  481. }
  482. }
  483. /// <summary>查询。</summary>
  484. /// <param name="dbClient">数据库连接。</param>
  485. /// <param name="sql">SQL 语句。</param>
  486. /// <param name="parameters">SQL 参数。</param>
  487. /// <exception cref="ArgumentNullException"></exception>
  488. public static IQuery Query(this IDbAdo dbClient, string sql, IEnumerable<KeyValuePair<string, object>> parameters)
  489. {
  490. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  491. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  492. var ps = Parameters(dbClient, sql, parameters);
  493. return dbClient.Query(sql, ps);
  494. }
  495. /// <summary>查询。</summary>
  496. /// <param name="dbClient">数据库连接。</param>
  497. /// <param name="sql">SQL 语句。</param>
  498. /// <param name="parameters">参数容器,每个属性表示一个 SQL 参数。此方法将会自动补足参数名称的 @ 前缀。</param>
  499. /// <exception cref="ArgumentNullException"></exception>
  500. public static IQuery Query(this IDbAdo dbClient, string sql, object parameters = null)
  501. {
  502. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  503. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  504. if (parameters is IEnumerable<KeyValuePair<string, object>> kvps)
  505. {
  506. var ps = Parameters(dbClient, sql, kvps);
  507. return dbClient.Query(sql, ps);
  508. }
  509. else
  510. {
  511. var ps = ParametersByProperites(dbClient, sql, parameters);
  512. return dbClient.Query(sql, ps);
  513. }
  514. }
  515. /// <summary>执行 SELECT 语句,获取查询结果。</summary>
  516. /// <param name="connection">数据库连接。</param>
  517. /// <param name="transaction">事务。</param>
  518. /// <param name="sql">SQL 语句。</param>
  519. /// <param name="parameters">参数。</param>
  520. /// <param name="timeout">超时秒数。</param>
  521. /// <returns>查询结果。</returns>
  522. public static DataTable Query(this IDbConnection connection, IDbTransaction transaction, string sql, IEnumerable<IDbDataParameter> parameters = null, int timeout = 3600)
  523. {
  524. if (connection == null) throw new ArgumentNullException(nameof(connection));
  525. if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql));
  526. if (connection.State != ConnectionState.Open) connection.Open();
  527. using (var command = connection.CreateCommand())
  528. {
  529. if (transaction != null) command.Transaction = transaction;
  530. if (timeout > 0) command.CommandTimeout = timeout;
  531. command.CommandText = sql;
  532. if (parameters != null)
  533. {
  534. foreach (var parameter in parameters)
  535. {
  536. if (parameter == null) continue;
  537. command.Parameters.Add(parameter);
  538. }
  539. }
  540. using (var reader = command.ExecuteReader())
  541. {
  542. var table = new DataTable();
  543. table.Load(reader);
  544. return table;
  545. }
  546. }
  547. }
  548. /// <summary>查询。</summary>
  549. /// <param name="dbClient">数据库连接。</param>
  550. /// <param name="sql">SQL 语句。</param>
  551. /// <param name="parameters">参数容器,每个属性表示一个 SQL 参数。此方法将会自动补足参数名称的 @ 前缀。</param>
  552. /// <exception cref="ArgumentNullException"></exception>
  553. /// <exception cref="NotImplementedException"></exception>
  554. public static T[] Query<T>(this IDbOrm dbClient, string sql, object parameters = null) where T : class, new()
  555. {
  556. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  557. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  558. if (dbClient is IDbAdo ado)
  559. {
  560. if (parameters is IEnumerable<KeyValuePair<string, object>> kvps)
  561. {
  562. var ps = Parameters(ado, sql, kvps);
  563. return dbClient.Query<T>(sql, ps);
  564. }
  565. else
  566. {
  567. var ps = ParametersByProperites(ado, sql, parameters);
  568. return dbClient.Query<T>(sql, ps);
  569. }
  570. }
  571. else
  572. {
  573. throw new NotImplementedException($"连接未实现 {nameof(IDbAdo)} 接口,无法创建参数。");
  574. }
  575. }
  576. #if !NET20
  577. /// <summary>执行查询,将结果输出为元组数组。</summary>
  578. /// <typeparam name="T">元组中的值类型。</typeparam>
  579. /// <param name="dbClient">数据库连接。</param>
  580. /// <param name="sql">SQL 语句。</param>
  581. /// <param name="parameters">SQL 参数。</param>
  582. /// <param name="getValue">获取第一个值的回调函数。</param>
  583. /// <returns>由元组组成的数组。</returns>
  584. /// <exception cref="ArgumentNullException"></exception>
  585. /// <exception cref="SqlException"></exception>
  586. public static Tuple<T>[] Query<T>(this IDbAdo dbClient, string sql, object parameters, Func<object, T> getValue)
  587. {
  588. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  589. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  590. if (getValue == null) throw new ArgumentNullException(nameof(getValue));
  591. using (var query = dbClient.Query(sql, parameters))
  592. {
  593. if (!query.Success) throw new SqlException(query, sql);
  594. var tuples = new Tuple<T>[query.Rows];
  595. for (var i = 0; i < query.Rows; i++)
  596. {
  597. var value1 = getValue(query.Value(i, 0));
  598. tuples[i] = new Tuple<T>(value1);
  599. }
  600. return tuples;
  601. }
  602. }
  603. /// <summary>执行查询,将结果输出为元组数组。</summary>
  604. /// <param name="dbClient">数据库连接。</param>
  605. /// <param name="sql">SQL 语句。</param>
  606. /// <param name="parameters">SQL 参数。</param>
  607. /// <param name="getValue1">获取第一个值的回调函数。</param>
  608. /// <param name="getValue2">获取第二个值的回调函数。</param>
  609. /// <returns>由元组组成的数组。</returns>
  610. /// <exception cref="ArgumentNullException"></exception>
  611. /// <exception cref="SqlException"></exception>
  612. public static Tuple<T1, T2>[] Query<T1, T2>(this IDbAdo dbClient, string sql, object parameters,
  613. Func<object, T1> getValue1,
  614. Func<object, T2> getValue2)
  615. {
  616. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  617. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  618. if (getValue1 == null) throw new ArgumentNullException(nameof(getValue1));
  619. if (getValue2 == null) throw new ArgumentNullException(nameof(getValue2));
  620. using (var query = dbClient.Query(sql, parameters))
  621. {
  622. if (!query.Success) throw new SqlException(query, sql);
  623. var tuples = new Tuple<T1, T2>[query.Rows];
  624. for (var i = 0; i < query.Rows; i++)
  625. {
  626. var v1 = getValue1(query.Value(i, 0));
  627. var v2 = getValue2(query.Value(i, 1));
  628. tuples[i] = new Tuple<T1, T2>(v1, v2);
  629. }
  630. return tuples;
  631. }
  632. }
  633. /// <summary>执行查询,将结果输出为元组数组。</summary>
  634. /// <param name="dbClient">数据库连接。</param>
  635. /// <param name="sql">SQL 语句。</param>
  636. /// <param name="parameters">SQL 参数。</param>
  637. /// <param name="getValue1">获取第一个值的回调函数。</param>
  638. /// <param name="getValue2">获取第二个值的回调函数。</param>
  639. /// <param name="getValue3">获取第三个值的回调函数。</param>
  640. /// <returns>由元组组成的数组。</returns>
  641. /// <exception cref="ArgumentNullException"></exception>
  642. /// <exception cref="SqlException"></exception>
  643. public static Tuple<T1, T2, T3>[] Query<T1, T2, T3>(this IDbAdo dbClient, string sql, object parameters,
  644. Func<object, T1> getValue1,
  645. Func<object, T2> getValue2,
  646. Func<object, T3> getValue3)
  647. {
  648. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  649. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  650. if (getValue1 == null) throw new ArgumentNullException(nameof(getValue1));
  651. if (getValue2 == null) throw new ArgumentNullException(nameof(getValue2));
  652. if (getValue3 == null) throw new ArgumentNullException(nameof(getValue3));
  653. using (var query = dbClient.Query(sql, parameters))
  654. {
  655. if (!query.Success) throw new SqlException(query, sql);
  656. var tuples = new Tuple<T1, T2, T3>[query.Rows];
  657. for (var i = 0; i < query.Rows; i++)
  658. {
  659. var v1 = getValue1(query.Value(i, 0));
  660. var v2 = getValue2(query.Value(i, 1));
  661. var v3 = getValue3(query.Value(i, 2));
  662. tuples[i] = new Tuple<T1, T2, T3>(v1, v2, v3);
  663. }
  664. return tuples;
  665. }
  666. }
  667. /// <summary>执行查询,将结果输出为元组数组。</summary>
  668. /// <param name="dbClient">数据库连接。</param>
  669. /// <param name="sql">SQL 语句。</param>
  670. /// <param name="parameters">SQL 参数。</param>
  671. /// <param name="getValue1">获取第一个值的回调函数。</param>
  672. /// <param name="getValue2">获取第二个值的回调函数。</param>
  673. /// <param name="getValue3">获取第三个值的回调函数。</param>
  674. /// <param name="getValue4">获取第四个值的回调函数。</param>
  675. /// <returns>由元组组成的数组。</returns>
  676. /// <exception cref="ArgumentNullException"></exception>
  677. /// <exception cref="SqlException"></exception>
  678. public static Tuple<T1, T2, T3, T4>[] Query<T1, T2, T3, T4>(this IDbAdo dbClient, string sql, object parameters,
  679. Func<object, T1> getValue1,
  680. Func<object, T2> getValue2,
  681. Func<object, T3> getValue3,
  682. Func<object, T4> getValue4)
  683. {
  684. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  685. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  686. if (getValue1 == null) throw new ArgumentNullException(nameof(getValue1));
  687. if (getValue2 == null) throw new ArgumentNullException(nameof(getValue2));
  688. if (getValue3 == null) throw new ArgumentNullException(nameof(getValue3));
  689. if (getValue4 == null) throw new ArgumentNullException(nameof(getValue4));
  690. using (var query = dbClient.Query(sql, parameters))
  691. {
  692. if (!query.Success) throw new SqlException(query, sql);
  693. var tuples = new Tuple<T1, T2, T3, T4>[query.Rows];
  694. for (var i = 0; i < query.Rows; i++)
  695. {
  696. var v1 = getValue1(query.Value(i, 0));
  697. var v2 = getValue2(query.Value(i, 1));
  698. var v3 = getValue3(query.Value(i, 2));
  699. var v4 = getValue4(query.Value(i, 3));
  700. tuples[i] = new Tuple<T1, T2, T3, T4>(v1, v2, v3, v4);
  701. }
  702. return tuples;
  703. }
  704. }
  705. /// <summary>执行查询,将结果输出为元组数组。</summary>
  706. /// <param name="dbClient">数据库连接。</param>
  707. /// <param name="sql">SQL 语句。</param>
  708. /// <param name="parameters">SQL 参数。</param>
  709. /// <param name="getValue1">获取第一个值的回调函数。</param>
  710. /// <param name="getValue2">获取第二个值的回调函数。</param>
  711. /// <param name="getValue3">获取第三个值的回调函数。</param>
  712. /// <param name="getValue4">获取第四个值的回调函数。</param>
  713. /// <param name="getValue5">获取第五个值的回调函数。</param>
  714. /// <returns>由元组组成的数组。</returns>
  715. /// <exception cref="ArgumentNullException"></exception>
  716. /// <exception cref="SqlException"></exception>
  717. public static Tuple<T1, T2, T3, T4, T5>[] Query<T1, T2, T3, T4, T5>(this IDbAdo dbClient, string sql, object parameters,
  718. Func<object, T1> getValue1,
  719. Func<object, T2> getValue2,
  720. Func<object, T3> getValue3,
  721. Func<object, T4> getValue4,
  722. Func<object, T5> getValue5)
  723. {
  724. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  725. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  726. if (getValue1 == null) throw new ArgumentNullException(nameof(getValue1));
  727. if (getValue2 == null) throw new ArgumentNullException(nameof(getValue2));
  728. if (getValue3 == null) throw new ArgumentNullException(nameof(getValue3));
  729. if (getValue4 == null) throw new ArgumentNullException(nameof(getValue4));
  730. if (getValue5 == null) throw new ArgumentNullException(nameof(getValue5));
  731. using (var query = dbClient.Query(sql, parameters))
  732. {
  733. if (!query.Success) throw new SqlException(query, sql);
  734. var tuples = new Tuple<T1, T2, T3, T4, T5>[query.Rows];
  735. for (var i = 0; i < query.Rows; i++)
  736. {
  737. var v1 = getValue1(query.Value(i, 0));
  738. var v2 = getValue2(query.Value(i, 1));
  739. var v3 = getValue3(query.Value(i, 2));
  740. var v4 = getValue4(query.Value(i, 3));
  741. var v5 = getValue5(query.Value(i, 4));
  742. tuples[i] = new Tuple<T1, T2, T3, T4, T5>(v1, v2, v3, v4, v5);
  743. }
  744. return tuples;
  745. }
  746. }
  747. /// <summary>执行查询,将结果输出为元组数组。</summary>
  748. /// <param name="dbClient">数据库连接。</param>
  749. /// <param name="sql">SQL 语句。</param>
  750. /// <param name="parameters">SQL 参数。</param>
  751. /// <param name="getValue1">获取第一个值的回调函数。</param>
  752. /// <param name="getValue2">获取第二个值的回调函数。</param>
  753. /// <param name="getValue3">获取第三个值的回调函数。</param>
  754. /// <param name="getValue4">获取第四个值的回调函数。</param>
  755. /// <param name="getValue5">获取第五个值的回调函数。</param>
  756. /// <param name="getValue6">获取第六个值的回调函数。</param>
  757. /// <returns>由元组组成的数组。</returns>
  758. /// <exception cref="ArgumentNullException"></exception>
  759. /// <exception cref="SqlException"></exception>
  760. public static Tuple<T1, T2, T3, T4, T5, T6>[] Query<T1, T2, T3, T4, T5, T6>(this IDbAdo dbClient, string sql, object parameters,
  761. Func<object, T1> getValue1,
  762. Func<object, T2> getValue2,
  763. Func<object, T3> getValue3,
  764. Func<object, T4> getValue4,
  765. Func<object, T5> getValue5,
  766. Func<object, T6> getValue6)
  767. {
  768. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  769. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  770. if (getValue1 == null) throw new ArgumentNullException(nameof(getValue1));
  771. if (getValue2 == null) throw new ArgumentNullException(nameof(getValue2));
  772. if (getValue3 == null) throw new ArgumentNullException(nameof(getValue3));
  773. if (getValue4 == null) throw new ArgumentNullException(nameof(getValue4));
  774. if (getValue5 == null) throw new ArgumentNullException(nameof(getValue5));
  775. if (getValue6 == null) throw new ArgumentNullException(nameof(getValue6));
  776. using (var query = dbClient.Query(sql, parameters))
  777. {
  778. if (!query.Success) throw new SqlException(query, sql);
  779. var tuples = new Tuple<T1, T2, T3, T4, T5, T6>[query.Rows];
  780. for (var i = 0; i < query.Rows; i++)
  781. {
  782. var v1 = getValue1(query.Value(i, 0));
  783. var v2 = getValue2(query.Value(i, 1));
  784. var v3 = getValue3(query.Value(i, 2));
  785. var v4 = getValue4(query.Value(i, 3));
  786. var v5 = getValue5(query.Value(i, 4));
  787. var v6 = getValue6(query.Value(i, 5));
  788. tuples[i] = new Tuple<T1, T2, T3, T4, T5, T6>(v1, v2, v3, v4, v5, v6);
  789. }
  790. return tuples;
  791. }
  792. }
  793. /// <summary>执行查询,将结果输出为元组数组。</summary>
  794. /// <param name="dbClient">数据库连接。</param>
  795. /// <param name="sql">SQL 语句。</param>
  796. /// <param name="parameters">SQL 参数。</param>
  797. /// <param name="getValue1">获取第一个值的回调函数。</param>
  798. /// <param name="getValue2">获取第二个值的回调函数。</param>
  799. /// <param name="getValue3">获取第三个值的回调函数。</param>
  800. /// <param name="getValue4">获取第四个值的回调函数。</param>
  801. /// <param name="getValue5">获取第五个值的回调函数。</param>
  802. /// <param name="getValue6">获取第六个值的回调函数。</param>
  803. /// <param name="getValue7">获取第七个值的回调函数。</param>
  804. /// <returns>由元组组成的数组。</returns>
  805. /// <exception cref="ArgumentNullException"></exception>
  806. /// <exception cref="SqlException"></exception>
  807. public static Tuple<T1, T2, T3, T4, T5, T6, T7>[] Query<T1, T2, T3, T4, T5, T6, T7>(this IDbAdo dbClient, string sql, object parameters,
  808. Func<object, T1> getValue1,
  809. Func<object, T2> getValue2,
  810. Func<object, T3> getValue3,
  811. Func<object, T4> getValue4,
  812. Func<object, T5> getValue5,
  813. Func<object, T6> getValue6,
  814. Func<object, T7> getValue7)
  815. {
  816. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  817. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  818. if (getValue1 == null) throw new ArgumentNullException(nameof(getValue1));
  819. if (getValue2 == null) throw new ArgumentNullException(nameof(getValue2));
  820. if (getValue3 == null) throw new ArgumentNullException(nameof(getValue3));
  821. if (getValue4 == null) throw new ArgumentNullException(nameof(getValue4));
  822. if (getValue5 == null) throw new ArgumentNullException(nameof(getValue5));
  823. if (getValue6 == null) throw new ArgumentNullException(nameof(getValue6));
  824. if (getValue7 == null) throw new ArgumentNullException(nameof(getValue7));
  825. using (var query = dbClient.Query(sql, parameters))
  826. {
  827. if (!query.Success) throw new SqlException(query, sql);
  828. var tuples = new Tuple<T1, T2, T3, T4, T5, T6, T7>[query.Rows];
  829. for (var i = 0; i < query.Rows; i++)
  830. {
  831. var v1 = getValue1(query.Value(i, 0));
  832. var v2 = getValue2(query.Value(i, 1));
  833. var v3 = getValue3(query.Value(i, 2));
  834. var v4 = getValue4(query.Value(i, 3));
  835. var v5 = getValue5(query.Value(i, 4));
  836. var v6 = getValue6(query.Value(i, 5));
  837. var v7 = getValue7(query.Value(i, 6));
  838. tuples[i] = new Tuple<T1, T2, T3, T4, T5, T6, T7>(v1, v2, v3, v4, v5, v6, v7);
  839. }
  840. return tuples;
  841. }
  842. }
  843. /// <summary>执行查询,将结果输出为元组数组。</summary>
  844. /// <param name="dbClient">数据库连接。</param>
  845. /// <param name="sql">SQL 语句。</param>
  846. /// <param name="parameters">SQL 参数。</param>
  847. /// <param name="getValue1">获取第一个值的回调函数。</param>
  848. /// <param name="getValue2">获取第二个值的回调函数。</param>
  849. /// <param name="getValue3">获取第三个值的回调函数。</param>
  850. /// <param name="getValue4">获取第四个值的回调函数。</param>
  851. /// <param name="getValue5">获取第五个值的回调函数。</param>
  852. /// <param name="getValue6">获取第六个值的回调函数。</param>
  853. /// <param name="getValue7">获取第七个值的回调函数。</param>
  854. /// <param name="getValue8">获取第八个值的回调函数。</param>
  855. /// <returns>由元组组成的数组。</returns>
  856. /// <exception cref="ArgumentNullException"></exception>
  857. /// <exception cref="SqlException"></exception>
  858. public static Tuple<T1, T2, T3, T4, T5, T6, T7, T8>[] Query<T1, T2, T3, T4, T5, T6, T7, T8>(this IDbAdo dbClient, string sql, object parameters,
  859. Func<object, T1> getValue1,
  860. Func<object, T2> getValue2,
  861. Func<object, T3> getValue3,
  862. Func<object, T4> getValue4,
  863. Func<object, T5> getValue5,
  864. Func<object, T6> getValue6,
  865. Func<object, T7> getValue7,
  866. Func<object, T8> getValue8)
  867. {
  868. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  869. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  870. if (getValue1 == null) throw new ArgumentNullException(nameof(getValue1));
  871. if (getValue2 == null) throw new ArgumentNullException(nameof(getValue2));
  872. if (getValue3 == null) throw new ArgumentNullException(nameof(getValue3));
  873. if (getValue4 == null) throw new ArgumentNullException(nameof(getValue4));
  874. if (getValue5 == null) throw new ArgumentNullException(nameof(getValue5));
  875. if (getValue6 == null) throw new ArgumentNullException(nameof(getValue6));
  876. if (getValue7 == null) throw new ArgumentNullException(nameof(getValue7));
  877. if (getValue8 == null) throw new ArgumentNullException(nameof(getValue8));
  878. using (var query = dbClient.Query(sql, parameters))
  879. {
  880. if (!query.Success) throw new SqlException(query, sql);
  881. var tuples = new Tuple<T1, T2, T3, T4, T5, T6, T7, T8>[query.Rows];
  882. for (var i = 0; i < query.Rows; i++)
  883. {
  884. var v1 = getValue1(query.Value(i, 0));
  885. var v2 = getValue2(query.Value(i, 1));
  886. var v3 = getValue3(query.Value(i, 2));
  887. var v4 = getValue4(query.Value(i, 3));
  888. var v5 = getValue5(query.Value(i, 4));
  889. var v6 = getValue6(query.Value(i, 5));
  890. var v7 = getValue7(query.Value(i, 6));
  891. var v8 = getValue8(query.Value(i, 7));
  892. tuples[i] = new Tuple<T1, T2, T3, T4, T5, T6, T7, T8>(v1, v2, v3, v4, v5, v6, v7, v8);
  893. }
  894. return tuples;
  895. }
  896. }
  897. #endif
  898. #endregion
  899. #region Execute
  900. /// <summary>执行 SQL 语句,并加入参数。</summary>
  901. /// <exception cref="ArgumentNullException"></exception>
  902. public static IExecute Execute(this IDbAdo dbClient, string sql, IEnumerable<KeyValuePair<string, object>> parameters, bool autoTransaction = false)
  903. {
  904. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  905. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  906. var ps = Parameters(dbClient, sql, parameters);
  907. return dbClient.Execute(sql, ps, autoTransaction);
  908. }
  909. /// <summary>执行 SQL 语句,并加入参数。</summary>
  910. /// <param name="dbClient">数据库连接。</param>
  911. /// <param name="sql">SQL 语句。</param>
  912. /// <param name="parameters">参数容器,每个属性表示一个 SQL 参数。此方法将会自动补足参数名称的 @ 前缀。</param>
  913. /// <param name="autoTransaction">自动使用事务。</param>
  914. /// <exception cref="ArgumentNullException"></exception>
  915. public static IExecute Execute(this IDbAdo dbClient, string sql, object parameters = null, bool autoTransaction = false)
  916. {
  917. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  918. if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
  919. if (parameters is IEnumerable<KeyValuePair<string, object>> kvps)
  920. {
  921. var ps = Parameters(dbClient, sql, kvps);
  922. return dbClient.Execute(sql, ps, autoTransaction);
  923. }
  924. {
  925. var ps = ParametersByProperites(dbClient, sql, parameters);
  926. return dbClient.Execute(sql, ps, autoTransaction);
  927. }
  928. }
  929. /// <summary>执行 SQL 语句,获取影响的行数。</summary>
  930. /// <param name="connection">数据库连接。</param>
  931. /// <param name="transaction">事务。</param>
  932. /// <param name="sql">SQL 语句。</param>
  933. /// <param name="parameters">参数。</param>
  934. /// <param name="timeout">超时秒数。</param>
  935. /// <returns>行数。</returns>
  936. public static int Execute(this IDbConnection connection, IDbTransaction transaction, string sql, IEnumerable<IDbDataParameter> parameters = null, int timeout = 3600)
  937. {
  938. if (connection == null) throw new ArgumentNullException(nameof(connection));
  939. if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql));
  940. if (connection.State != ConnectionState.Open) connection.Open();
  941. using (var command = connection.CreateCommand())
  942. {
  943. if (transaction != null) command.Transaction = transaction;
  944. if (timeout > 0) command.CommandTimeout = timeout;
  945. command.CommandText = sql;
  946. if (parameters != null)
  947. {
  948. foreach (var parameter in parameters)
  949. {
  950. if (parameter == null) continue;
  951. command.Parameters.Add(parameter);
  952. }
  953. }
  954. var rows = command.ExecuteNonQuery();
  955. return rows;
  956. }
  957. }
  958. #endregion
  959. #region Transaction
  960. /// <summary>启动事务,执行指定的过程并在完成后提交事务。若过程被异常打断,则回滚事务。</summary>
  961. /// <exception cref="ArgumentNullException"></exception>
  962. /// <exception cref="SqlException"></exception>
  963. public static void InTransaction(this IDbAdo source, Action action)
  964. {
  965. // 检查参数。
  966. if (source == null) throw new ArgumentNullException(nameof(source), "数据源无效。");
  967. if (action == null) throw new ArgumentNullException(nameof(action), "没有指定要在事物中执行的程序。");
  968. InTransaction<object>(source, () =>
  969. {
  970. action.Invoke();
  971. return null;
  972. });
  973. }
  974. /// <summary>启动事务,执行指定的过程并在完成后提交事务。若过程被异常打断,则回滚事务。</summary>
  975. /// <exception cref="ArgumentNullException"></exception>
  976. /// <exception cref="SqlException"></exception>
  977. public static T InTransaction<T>(this IDbAdo source, Func<T> func)
  978. {
  979. // 检查参数。
  980. if (source == null) throw new ArgumentNullException(nameof(source), "数据源无效。");
  981. if (func == null) throw new ArgumentNullException(nameof(func), "没有指定要在事物中执行的程序。");
  982. // 已经存在事务。
  983. if (source.Transaction != null) return func.Invoke();
  984. // 启动事务。
  985. var begin = source.Begin();
  986. if (begin.NotEmpty()) throw new SqlException("无法启动事务:" + begin);
  987. var result = default(T);
  988. var success = false;
  989. try
  990. {
  991. // 在事务内运行。
  992. result = func.Invoke();
  993. success = true;
  994. }
  995. finally
  996. {
  997. if (success)
  998. {
  999. // 执行成功,提交事务。
  1000. var commit = source.Commit();
  1001. if (!string.IsNullOrEmpty(commit)) throw new SqlException(commit);
  1002. }
  1003. else
  1004. {
  1005. // 执行失败,回滚事务。
  1006. try { source.Rollback(); } catch { }
  1007. }
  1008. }
  1009. return result;
  1010. }
  1011. #endregion
  1012. #region Parameter
  1013. /// <exception cref="ArgumentNullException"></exception>
  1014. static List<IDataParameter> ParametersByProperites(IDbAdo dbClient, string sql, object parameters)
  1015. {
  1016. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  1017. if (parameters == null) return null;
  1018. var lsql = sql.Lower();
  1019. var type = parameters.GetType();
  1020. var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
  1021. var count = properties.Length;
  1022. var dict = new Dictionary<string, object>(count);
  1023. for (var i = 0; i < count; i++)
  1024. {
  1025. var property = properties[i];
  1026. // 属性必须能够获取值。
  1027. var getter = property.GetGetMethod();
  1028. if (getter == null) continue;
  1029. // 属性值必须有效。
  1030. var name = property.Name;
  1031. if (name.IsEmpty()) continue;
  1032. // 属性不可重复。
  1033. if (!name.StartsWith("@")) name = "@" + name;
  1034. if (dict.ContainsKey(name)) continue;
  1035. // SQL 语句中必须包含此参数。
  1036. var lname = name.Lower();
  1037. if (!lsql.Contains(lname)) continue;
  1038. // 加入字典。
  1039. var value = getter.Invoke(parameters, null);
  1040. dict.Add(name, value);
  1041. }
  1042. if (dict.Count < 1) return null;
  1043. var ps = new List<IDataParameter>();
  1044. foreach (var kvp in dict)
  1045. {
  1046. var p = dbClient.Parameter(kvp.Key, kvp.Value);
  1047. ps.Add(p);
  1048. }
  1049. return ps;
  1050. }
  1051. /// <exception cref="ArgumentNullException"></exception>
  1052. static List<IDataParameter> Parameters(IDbAdo dbClient, string sql, IEnumerable<KeyValuePair<string, object>> parameters)
  1053. {
  1054. if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
  1055. if (parameters == null) return null;
  1056. var lsql = sql.Lower();
  1057. var names = new List<string>(20);
  1058. var ps = new List<IDataParameter>(20);
  1059. foreach (var kvp in parameters)
  1060. {
  1061. var name = kvp.Key;
  1062. if (name.IsEmpty()) continue;
  1063. // 属性不可重复。
  1064. if (!name.StartsWith("@")) name = "@" + name;
  1065. if (names.Contains(name)) continue;
  1066. // SQL 语句中必须包含此参数。
  1067. var lname = name.Lower();
  1068. if (!lsql.Contains(lname)) continue;
  1069. var p = dbClient.Parameter(name, kvp.Value);
  1070. ps.Add(p);
  1071. names.Add(name);
  1072. }
  1073. return ps;
  1074. }
  1075. #endregion
  1076. #region SQL
  1077. /// <summary>对文本转义,符合 SQL 安全性。可根据字段类型限制 UTF-8 字节数,默认为 0 时不限制字节数。</summary>
  1078. public static string Escape(this string text, int bytes = 0)
  1079. {
  1080. if (text.IsEmpty()) return "";
  1081. var t = text ?? "";
  1082. t = t.Replace("\\", "\\\\");
  1083. t = t.Replace("'", "\\'");
  1084. t = t.Replace("\n", "\\n");
  1085. t = t.Replace("\r", "\\r");
  1086. t = t.Replace("\b", "\\b");
  1087. t = t.Replace("\t", "\\t");
  1088. t = t.Replace("\f", "\\f");
  1089. if (bytes > 5)
  1090. {
  1091. if (t.Bytes(Encoding.UTF8).Length > bytes)
  1092. {
  1093. while (true)
  1094. {
  1095. t = t.Substring(0, t.Length - 1);
  1096. if (t.Bytes(Encoding.UTF8).Length <= (bytes - 4)) break;
  1097. }
  1098. t = t + " ...";
  1099. }
  1100. }
  1101. return t;
  1102. }
  1103. /// <summary>限定名称文本,只允许包含字母、数字和下划线。</summary>
  1104. public static string SafeName(this string name) => TextUtility.Restrict(name, "0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
  1105. #endregion
  1106. #region 数据模型 -> DataTable
  1107. /// <summary>将多个实体元素转换为 DataTable。</summary>
  1108. /// <typeparam name="T">实体元素的类型。</typeparam>
  1109. /// <param name="items">实体元素。</param>
  1110. /// <param name="tableName">设置 <see cref="DataTable"/> 的名称。</param>
  1111. /// <exception cref="ArgumentNullException"></exception>
  1112. /// <exception cref="DuplicateNameException"></exception>
  1113. /// <exception cref="InvalidExpressionException"></exception>
  1114. public static DataTable DataTable<T>(this IEnumerable<T> items, string tableName = null)
  1115. {
  1116. if (items == null) throw new ArgumentNullException(nameof(items));
  1117. // 解析表结构。
  1118. var it = typeof(T);
  1119. var ts = TableStructure.Parse(it, true, true);
  1120. if (ts == null || ts.Columns == null || ts.Columns.Length < 1)
  1121. {
  1122. foreach (var item in items)
  1123. {
  1124. if (item == null) continue;
  1125. var itemType = item.GetType();
  1126. ts = TableStructure.Parse(itemType, true, true);
  1127. if (ts == null) throw new TypeLoadException($"无法解析 {itemType.FullName} 的结构。");
  1128. it = itemType;
  1129. break;
  1130. }
  1131. if (ts == null) throw new TypeLoadException($"无法解析 {it.FullName} 的结构。");
  1132. }
  1133. var cas = ts.Columns;
  1134. var width = cas.Length;
  1135. if (width < 1) throw new TypeLoadException($"类型 {it.FullName} 的结构中没有列。");
  1136. // 初始化列。
  1137. var table = new DataTable();
  1138. var pis = new PropertyInfo[width];
  1139. var fts = new Type[width];
  1140. for (var i = 0; i < width; i++)
  1141. {
  1142. var ca = cas[i];
  1143. var pi = ca.Property;
  1144. var pt = pi.PropertyType;
  1145. pis[i] = pi;
  1146. var ft = pt;
  1147. if (pt.IsGenericType && pt.GetGenericTypeDefinition() == typeof(Nullable<>))
  1148. {
  1149. pt.GetGenericArguments();
  1150. ft = Nullable.GetUnderlyingType(pt);
  1151. }
  1152. fts[i] = ft;
  1153. var column = new DataColumn(ca.Field, ft);
  1154. column.AllowDBNull = true;
  1155. table.Columns.Add(column);
  1156. }
  1157. // 添加行。
  1158. foreach (var item in items)
  1159. {
  1160. if (item == null) continue;
  1161. var values = new ArrayBuilder<object>(width);
  1162. for (var i = 0; i < width; i++)
  1163. {
  1164. var value = pis[i].GetValue(item, null);
  1165. if (value is DateTime dt)
  1166. {
  1167. if (dt.Year < 1753)
  1168. {
  1169. values.Add(DBNull.Value);
  1170. continue;
  1171. }
  1172. }
  1173. values.Add(value);
  1174. }
  1175. table.Rows.Add(values.Export());
  1176. }
  1177. if (tableName.NotEmpty()) table.TableName = tableName;
  1178. else if (ts.TableName.NotEmpty()) table.TableName = ts.TableName;
  1179. return table;
  1180. }
  1181. #endregion
  1182. #region DataTable 序列化
  1183. /// <summary>转换 <see cref="System.Data.DataTable"/> 到 <see cref="ObjectSet{T}"/> 数组,每行记录为一个 ObjectSet 对象。</summary>
  1184. /// <returns>当参数 table 无效时返回 0 长度的 <see cref="ObjectSet{T}"/> 数组。</returns>
  1185. public static ObjectSet[] ObjectSet(this DataTable table)
  1186. {
  1187. if (table == null) return new ObjectSet[0];
  1188. var columns = table.Columns.Count;
  1189. var fields = new string[columns];
  1190. for (var c = 0; c < columns; c++) fields[c] = table.Columns[c].ColumnName;
  1191. var rows = table.Rows.Count;
  1192. var dicts = new Dictionary<string, object>[rows];
  1193. for (var r = 0; r < table.Rows.Count; r++)
  1194. {
  1195. var dict = new Dictionary<string, object>(columns);
  1196. for (var c = 0; c < columns; c++)
  1197. {
  1198. var field = fields[c];
  1199. if (string.IsNullOrEmpty(field)) continue;
  1200. if (dict.ContainsKey(field)) continue;
  1201. var v = table.Rows[r][c];
  1202. if (v.IsNull()) v = null;
  1203. dict.Add(field, v);
  1204. }
  1205. dicts[r] = dict;
  1206. }
  1207. var oss = new ObjectSet[rows];
  1208. for (var i = 0; i < rows; i++) oss[i] = new ObjectSet(dicts[i]);
  1209. return oss;
  1210. }
  1211. /// <summary>转换为 Json 对象。</summary>
  1212. public static Json ToJson(this DataTable table, Func<DateTime, string> dateTimeFormatter = null)
  1213. {
  1214. if (table == null) return null;
  1215. var columns = ToJson(table.Columns);
  1216. var rows = ToJson(table.Rows, dateTimeFormatter);
  1217. var jsonObject = Json.NewObject();
  1218. jsonObject.SetProperty("columns", columns);
  1219. jsonObject.SetProperty("rows", rows);
  1220. return jsonObject;
  1221. }
  1222. /// <summary>转换为 Json 对象。</summary>
  1223. public static Json ToJson(this DataColumnCollection columns)
  1224. {
  1225. if (columns == null) return null;
  1226. var json = Json.NewArray();
  1227. var count = columns.Count;
  1228. for (var c = 0; c < count; c++)
  1229. {
  1230. var dc = columns[c];
  1231. var column = Json.NewObject();
  1232. column.SetProperty("name", dc.ColumnName);
  1233. column.SetProperty("type", dc.DataType.FullName);
  1234. json.AddItem(column);
  1235. }
  1236. return json;
  1237. }
  1238. /// <summary>转换为 Json 对象。</summary>
  1239. public static Json ToJson(this DataRowCollection rows, Func<DateTime, object> dateTimeFormatter = null)
  1240. {
  1241. if (rows == null) return null;
  1242. var json = Json.NewArray();
  1243. var count = rows.Count;
  1244. for (var r = 0; r < count; r++)
  1245. {
  1246. json.AddItem(ToJson(rows[r], dateTimeFormatter));
  1247. }
  1248. return json;
  1249. }
  1250. /// <summary>转换为 Json 对象。</summary>
  1251. public static Json ToJson(this DataRow row, Func<DateTime, object> dateTimeFormatter = null)
  1252. {
  1253. if (row == null) return null;
  1254. var cells = row.ItemArray;
  1255. var count = cells.Length;
  1256. var json = Json.NewArray();
  1257. for (var c = 0; c < count; c++)
  1258. {
  1259. var value = cells[c];
  1260. if (value == null || value.Equals(DBNull.Value))
  1261. {
  1262. json.AddItem();
  1263. continue;
  1264. }
  1265. if (value is DateTime vDateTime)
  1266. {
  1267. if (dateTimeFormatter == null)
  1268. {
  1269. json.AddItem(Json.SerializeDateTime(vDateTime));
  1270. continue;
  1271. }
  1272. else
  1273. {
  1274. value = dateTimeFormatter.Invoke(vDateTime);
  1275. if (value == null || value.Equals(DBNull.Value))
  1276. {
  1277. json.AddItem();
  1278. continue;
  1279. }
  1280. }
  1281. }
  1282. if (value is string @string) json.AddItem(@string);
  1283. else if (value is byte @byte) json.AddItem(@byte);
  1284. else if (value is short @short) json.AddItem(@short);
  1285. else if (value is int @int) json.AddItem(@int);
  1286. else if (value is long @long) json.AddItem(@long);
  1287. else if (value is float @float) json.AddItem(@float);
  1288. else if (value is double @double) json.AddItem(@double);
  1289. else if (value is decimal @decimal) json.AddItem(@decimal);
  1290. else if (value is bool @bool) json.AddItem(@bool);
  1291. else if (value is byte[] bytes) json.AddItem(bytes.Base64());
  1292. else json.AddItem(TextUtility.Text(value));
  1293. }
  1294. return json;
  1295. }
  1296. /// <summary>转换 <see cref="DataTable"/> 为 CSV 文本,不存在表时返回 NULL 值。可指定是否包含表头。</summary>
  1297. public static string Csv(DataTable table, bool withHead = false)
  1298. {
  1299. if (table == null) return null;
  1300. var columns = table.Columns.Count;
  1301. if (columns < 1) return "";
  1302. var sb = new StringBuilder();
  1303. if (withHead)
  1304. {
  1305. for (var c = 0; c < columns; c++)
  1306. {
  1307. var v = table.Columns[c].ColumnName;
  1308. CsvCell(sb, c, v);
  1309. }
  1310. }
  1311. var rows = table.Rows.Count;
  1312. for (var r = 0; r < rows; r++)
  1313. {
  1314. var row = table.Rows[r];
  1315. if (withHead || r > 0) sb.Append("\r\n");
  1316. for (var c = 0; c < columns; c++) CsvCell(sb, c, row[c]);
  1317. }
  1318. return sb.ToString();
  1319. }
  1320. private static void CsvCell(StringBuilder sb, int c, object v)
  1321. {
  1322. if (c > 0) sb.Append(",");
  1323. if (v == null || v.Equals(DBNull.Value)) return;
  1324. if (v is bool @bool)
  1325. {
  1326. sb.Append(@bool ? "TRUE" : "FALSE");
  1327. return;
  1328. }
  1329. if (v is DateTime @datetime)
  1330. {
  1331. sb.Append(@datetime.Lucid());
  1332. return;
  1333. }
  1334. if (v is byte || v is sbyte || v is short || v is ushort || v is int || v is uint || v is long || v is ulong || v is float || v is double || v is decimal)
  1335. {
  1336. sb.Append(v.ToString());
  1337. return;
  1338. }
  1339. if (v is char)
  1340. {
  1341. sb.Append((char)v);
  1342. return;
  1343. }
  1344. var s = (v is string @string) ? @string : v.ToString();
  1345. var length = s.Length;
  1346. if (length < 1) return;
  1347. var quote = false;
  1348. var comma = false;
  1349. var newline = false;
  1350. for (var i = 0; i < length; i++)
  1351. {
  1352. var @char = s[i];
  1353. if (@char == '\"') quote = true;
  1354. else if (@char == ',') comma = true;
  1355. else if (@char == '\r') newline = false;
  1356. else if (@char == '\n') newline = false;
  1357. }
  1358. if (quote || comma || newline)
  1359. {
  1360. sb.Append("\"");
  1361. s = s.Replace("\"", "\"\"");
  1362. sb.Append(s);
  1363. sb.Append("\"");
  1364. }
  1365. else sb.Append(s);
  1366. }
  1367. #endregion
  1368. #region DataTable 快捷操作
  1369. /// <summary>获取默认表中指定单元格的内容。</summary>
  1370. /// <param name="table">数据表。</param>
  1371. /// <param name="rowIndex">行索引,从 0 开始。</param>
  1372. /// <param name="columnIndex">列索引,从 0 开始。</param>
  1373. public static object Value(this DataTable table, int rowIndex, int columnIndex)
  1374. {
  1375. if (table != null)
  1376. {
  1377. if (rowIndex >= 0 && rowIndex < table.Rows.Count)
  1378. {
  1379. if (columnIndex >= 0 && columnIndex < table.Columns.Count)
  1380. {
  1381. var value = table.Rows[rowIndex][columnIndex];
  1382. if (value == null || value.Equals(DBNull.Value)) return null;
  1383. return value;
  1384. }
  1385. }
  1386. }
  1387. return null;
  1388. }
  1389. /// <summary>获取默认表中指定单元的内容。</summary>
  1390. /// <param name="table">数据表。</param>
  1391. /// <param name="rowIndex">行索引,从 0 开始。</param>
  1392. /// <param name="columnName">列名称/字段名称,此名称不区分大小写。</param>
  1393. public static object Value(this DataTable table, int rowIndex, string columnName)
  1394. {
  1395. if (table != null && !string.IsNullOrEmpty(columnName))
  1396. {
  1397. if ((rowIndex < table.Rows.Count) && (rowIndex >= 0))
  1398. {
  1399. try
  1400. {
  1401. var value = table.Rows[rowIndex][columnName];
  1402. if (value == null || value.Equals(DBNull.Value)) return null;
  1403. return value;
  1404. }
  1405. catch { }
  1406. }
  1407. }
  1408. return null;
  1409. }
  1410. /// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
  1411. public static Class<DateTime> DateTime(this DataTable table, int row, int column) => table == null ? null : ClockUtility.DateTime(table.Value(row, column));
  1412. /// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
  1413. public static Class<DateTime> DateTime(this DataTable table, int row, string column) => table == null ? null : ClockUtility.DateTime(table.Value(row, column));
  1414. /// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
  1415. public static Int32 Int32(this DataTable table, int row, int column) => table == null ? 0 : NumberUtility.Int32(table.Value(row, column));
  1416. /// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
  1417. public static Int32 Int32(this DataTable table, int row, string column) => table == null ? 0 : NumberUtility.Int32(table.Value(row, column));
  1418. /// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
  1419. public static Int64 Int64(this DataTable table, int row, int column) => table == null ? 0L : NumberUtility.Int64(table.Value(row, column));
  1420. /// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
  1421. public static Int64 Int64(this DataTable table, int row, string column) => table == null ? 0L : NumberUtility.Int64(table.Value(row, column));
  1422. /// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
  1423. public static Decimal Decimal(this DataTable table, int row, int column) => table == null ? 0M : NumberUtility.Decimal(table.Value(row, column));
  1424. /// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
  1425. public static Decimal Decimal(this DataTable table, int row, string column) => table == null ? 0M : NumberUtility.Decimal(table.Value(row, column));
  1426. /// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>>
  1427. public static Double Double(this DataTable table, int row, int column) => table == null ? 0D : NumberUtility.Double(table.Value(row, column));
  1428. /// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>>
  1429. public static Double Double(this DataTable table, int row, string column) => table == null ? 0D : NumberUtility.Double(table.Value(row, column));
  1430. /// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
  1431. public static string Text(this DataTable table, int row, int column) => table == null ? null : TextUtility.Text(table.Value(row, column));
  1432. /// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
  1433. public static string Text(this DataTable table, int row, string column) => table == null ? null : TextUtility.Text(table.Value(row, column));
  1434. #endregion
  1435. #region Dynamic
  1436. #if !NET20
  1437. /// <summary>转换 ObjectSet 数组为 dynamic 数组。</summary>
  1438. public static dynamic[] Dynamic(this ObjectSet[] oss)
  1439. {
  1440. if (oss == null) return new dynamic[0];
  1441. var eos = oss.Expando();
  1442. var ds = new dynamic[eos.Length];
  1443. eos.CopyTo(ds, 0);
  1444. return ds;
  1445. }
  1446. #endif
  1447. #endregion
  1448. #region 表达式计算
  1449. /// <summary>计算文本表达式。</summary>
  1450. public static object Compute(string expression)
  1451. {
  1452. using (var table = new DataTable())
  1453. {
  1454. var result = table.Compute(expression, null);
  1455. if (result.IsNull()) return null;
  1456. return result;
  1457. }
  1458. }
  1459. #endregion
  1460. }
  1461. }