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.

1305 lines
55 KiB

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