using Apewer.Internals; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Data; using static Apewer.TextUtility; namespace Apewer.Source { /// System.Data.DataTable 装箱查询。 public class Query : IQuery, IDisposable, IToJson { private bool _disposed = false; private bool _success = false; private string _message = null; private DataTable _table = null; private DataTable[] _tables = null; /// 创建实例,默认状态为失败。 public Query(bool success = false, string message = null) { _success = false; _message = message; } /// 创建实例,Exception 为 NULL 时成功,非 NULL 时失败。 public Query(Exception exception) { _success = exception == null; _message = RuntimeUtility.Message(exception); } /// 创建实例,包装一个 DataTable 对象,数据表为 NULL 时失败,非 NULL 时成功。 public Query(DataTable table) { _table = table; _success = table != null; _message = table == null ? "未获取有效的数据表。" : null; } /// 创建实例,包装一个 DataTable 对象。 public Query(DataTable table, bool success, string message = null) { _table = table; _success = success; _message = message; } /// 创建实例,包装多个 DataTable 对象。 public Query(DataTable[] tables, bool success = true, string message = null) { _tables = tables; _success = success; _message = message; if (tables != null && tables.Length > 0) _table = tables[0]; } #region Property /// 语句执行成功。 public bool Success { get => _success; } /// 消息。 public string Message { get => _message; } /// 所有结果表。 public DataTable[] Tables { get => _tables; } /// 获取默认结果表。如果设置默认结果表,会丢失设置前的所有结果表。 public DataTable Table { get => _table; } /// 默认表中的数据总行数。 public int Rows { get => _table == null ? 0 : _table.Rows.Count; } /// 默认表中的数据总列数。 public int Columns { get => _table == null ? 0 : _table.Columns.Count; } #endregion #region Value /// 获取默认表中第 0 行、第 0 列的单元格内容。 public object Value() { if (_disposed) return null; return Value(0, 0); } /// 获取默认表中指定行中第 0 列的内容。 /// 行索引,从 0 开始。 public object Value(int rowIndex) { if (_disposed) return null; return Value(rowIndex, 0); } /// 获取默认表中第 0 行指定列的内容。 /// 列名称。 public object Value(string columnName) { if (_disposed) return null; return Value(0, columnName); } /// 获取默认表中指定单元格的内容。 /// 行索引,从 0 开始。 /// 列索引,从 0 开始。 public object Value(int rowIndex, int columnIndex) { if (_disposed) return null; if (_table != null) { if (rowIndex >= 0 && rowIndex < _table.Rows.Count) { if (columnIndex >= 0 && columnIndex < _table.Columns.Count) { return _table.Rows[rowIndex][columnIndex]; } } } return null; } /// 获取默认表中指定单元的内容。 /// 行索引,从 0 开始。 /// 列名称。 public object Value(int rowIndex, string columnName) { if (_disposed) return null; if ((Table != null) && !string.IsNullOrEmpty(columnName)) { if ((rowIndex < Table.Rows.Count) && (rowIndex >= 0)) { try { return Table.Rows[rowIndex][columnName]; } catch { } } } return null; } /// 搜索默认表。 /// 搜索条件:列名。 /// 搜索条件:列值。 /// 搜索结果。 public object Value(string conditionColumn, string conditionValue, string resultColumn) { if (_disposed) return null; if ((Table != null) && (!string.IsNullOrEmpty(conditionColumn)) && (conditionValue != null) && (!string.IsNullOrEmpty(resultColumn))) { for (int i = 0; i < Table.Rows.Count; i++) { try { if (Table.Rows[i][conditionColumn].ToString() == conditionValue) return Table.Rows[i][resultColumn]; } catch { } } } return null; } /// 搜索默认表。 /// 搜索条件:列名。 /// 搜索条件:列值。 /// 搜索结果的列名。 public object Value(int conditionColumn, string conditionValue, int resultColumn) { if (_disposed) return null; if ((Table != null) && (conditionColumn >= 0) && (conditionValue != null) && (resultColumn >= 0)) { if ((conditionColumn < Table.Columns.Count) && (resultColumn < Table.Columns.Count)) { for (int i = 0; i < Table.Rows.Count; i++) { try { if (Table.Rows[i][conditionColumn].ToString() == conditionValue) return Table.Rows[i][resultColumn]; } catch { } } } } return null; } #endregion #region Method /// 搜索默认表。 /// 搜索条件:列名。 /// 搜索条件:列值。 /// 搜索结果。 public string Text(int conditionColumn, string conditionValue, int resultColumn) { var value = Value(conditionColumn, conditionValue, resultColumn); return Text(value); } /// 释放系统资源。 public virtual void Dispose() { if (_disposed) return; if (_tables != null) { foreach (var table in _tables) RuntimeUtility.Dispose(table); _tables = null; } RuntimeUtility.Dispose(_table); _table = null; _tables = null; _disposed = true; // GC.SuppressFinalize(this); } /// 当不指定格式化程序时,自动根据类型选择预置的格式化程序。 private static Func GetValueFormatter() { var type = typeof(T); if (type.Equals(typeof(string))) return TextFormatter; else return ForceFormatter; } /// 获取指定列的所有值,无效值不加入结果。 public T[] ReadColumn(int column = 0, Func formatter = null) => SourceUtility.Column(this, (r) => (formatter ?? GetValueFormatter()).Invoke(Value(r, column))); /// 获取指定列的所有值,无效值不加入结果。 /// public T[] ReadColumn(string column, Func formatter = null) => SourceUtility.Column(this, (r) => (formatter ?? GetValueFormatter()).Invoke(Value(r, column))); /// 获取指定列的所有值,无效值不加入结果。 public string[] ReadColumn(int column = 0) => SourceUtility.Column(this, (r) => this.Text(r, column)); /// 获取指定列的所有值,无效值不加入结果。 /// public string[] ReadColumn(string column) => SourceUtility.Column(this, (r) => this.Text(r, column)); #endregion #region 模型化、IToJson /// 转换为 Json 对象。 public Json ToJson() { var columns = Json.NewArray(); var rows = Json.NewArray(); var table = _table; if (!_disposed && table != null) { var columnsCount = table.Columns.Count; for (var c = 0; c < columnsCount; c++) { var dc = table.Columns[c]; var column = Json.NewObject(); column.SetProperty("name", dc.ColumnName); column.SetProperty("type", dc.DataType.FullName); columns.AddItem(column); } var rowsCount = table.Rows.Count; for (var r = 0; r < rowsCount; r++) { var row = Json.NewArray(); for (var c = 0; c < columnsCount; c++) { var v = Value(r, c); if (v == null) row.AddItem(); else if (v.Equals(DBNull.Value)) row.AddItem(); else if (v is byte vByte) row.AddItem(vByte); else if (v is short vInt16) row.AddItem(vInt16); else if (v is int vInt32) row.AddItem(vInt32); else if (v is long vInt64) row.AddItem(vInt64); else if (v is float vSingle) row.AddItem(vSingle); else if (v is double vDouble) row.AddItem(vDouble); else if (v is decimal vDecimal) row.AddItem(vDecimal); else if (v is bool vBoolean) row.AddItem(vBoolean); else if (v is byte[] vBytes) row.AddItem(vBytes.Base64()); else if (v is DateTime vDateTime) row.AddItem(vDateTime.Lucid()); else row.AddItem(v.ToString()); } rows.AddItem(row); } } var jsonObject = Json.NewObject(); jsonObject.SetProperty("success", _success); jsonObject.SetProperty("message", _message); jsonObject.SetProperty("columns", columns); jsonObject.SetProperty("rows", rows); return jsonObject; } /// 转换为模型数组。 public ObjectSet[] ToArray() { var oss = null as ObjectSet[]; var table = _table; if (!_disposed && table != null) { var width = table.Columns.Count; var names = new string[width]; for (var c = 0; c < width; c++) { var name = table.Columns[c].ColumnName; if (name.IsEmpty()) continue; if (names.Contains(name)) continue; names[c] = name; } var unnamed = 1; for (var c = 0; c < width; c++) { if (names[c] != null) continue; while (true) { var name = "unnamed_" + unnamed.ToString(); unnamed++; if (names.Contains(name)) continue; names[c] = name; break; } } var height = table.Rows.Count; oss = new ObjectSet[height]; for (var r = 0; r < height; r++) { var os = new ObjectSet(); var dict = os.Origin; for (var c = 0; c < width; c++) { var v = Value(r, c); dict.Add(names[c], v.IsNull() ? null : v); } oss[r] = os; } } return oss; } #endregion #region Static private static T ForceFormatter(object input) => (T)input; private static T TextFormatter(object input) => (T)(Text(input) as object); private static ObjectDisposedException DisposedException { get { return new ObjectDisposedException(typeof(Query).FullName); } } #endregion #region Extension internal static string Text(object value) { if (value != null && !value.Equals(DBNull.Value)) { if (value is string str) return str; try { return value.ToString() ?? ""; } catch { } } return ""; } internal static Class DateTime(object value) { if (value == null) return null; if (value is DateTime) return null; DateTime result; var parsed = System.DateTime.TryParse(value.ToString(), out result); return parsed ? new Class(result) : null; } #endregion } }