|
|
using Apewer.Internals;using Newtonsoft.Json.Linq;using System;using System.Collections.Generic;using System.Data;
using static Apewer.TextUtility;
namespace Apewer.Source{
/// <summary>System.Data.DataTable 装箱查询。</summary>
public sealed 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;
/// <summary>创建实例,默认状态为失败。</summary>
public Query(bool success = false, string message = null) { _success = false; _message = message; }
/// <summary>创建实例,Exception 为 NULL 时成功,非 NULL 时失败。</summary>
public Query(Exception exception) { _success = exception == null; _message = RuntimeUtility.Message(exception); }
/// <summary>创建实例,包装一个 DataTable 对象,数据表为 NULL 时失败,非 NULL 时成功。</summary>
public Query(DataTable table) { _table = table; _success = table != null; _message = table == null ? "未获取有效的数据表。" : null; }
/// <summary>创建实例,包装一个 DataTable 对象。</summary>
public Query(DataTable table, bool success, string message = null) { _table = table; _success = success; _message = message; }
/// <summary>创建实例,包装多个 DataTable 对象。</summary>
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]; }
/// <summary>从 <see cref="Query"/> 到 <see cref="Boolean"/> 的隐式转换,判断 <see cref="Query"/> 执行成功且包含表。</summary>
public static implicit operator bool(Query query) => query != null && query.Success && query.Table != null;
/// <summary>从 <see cref="DataTable"/> 到 <see cref="Query"/> 的隐式转换。</summary>
public static implicit operator Query(DataTable table) => new Query(table, table != null, null);
/// <summary>从 <see cref="Query"/> 到 <see cref="DataTable"/> 的隐式转换。</summary>
public static implicit operator DataTable(Query query) => query?.Table;
#region Property
/// <summary>语句执行成功。</summary>
public bool Success { get => _success; }
/// <summary>消息。</summary>
public string Message { get => _message; }
/// <summary>所有结果表。</summary>
public DataTable[] Tables { get => _tables; }
/// <summary>获取默认结果表。如果设置默认结果表,会丢失设置前的所有结果表。</summary>
public DataTable Table { get => _table; }
/// <summary>默认表中的数据总行数。</summary>
public int Rows { get => _table == null ? 0 : _table.Rows.Count; }
/// <summary>默认表中的数据总列数。</summary>
public int Columns { get => _table == null ? 0 : _table.Columns.Count; }
#endregion
#region Value
/// <summary>获取默认表中第 0 行、第 0 列的单元格内容。</summary>
public object Value() { if (_disposed) return null; return Value(0, 0); }
/// <summary>获取默认表中指定行中第 0 列的内容。</summary>
/// <param name="rowIndex">行索引,从 0 开始。</param>
public object Value(int rowIndex) { if (_disposed) return null; return Value(rowIndex, 0); }
/// <summary>获取默认表中第 0 行指定列的内容。</summary>
/// <param name="columnName">列名称/字段名称,此名称不区分大小写。</param>
public object Value(string columnName) { if (_disposed) return null; return Value(0, columnName); }
/// <summary>获取默认表中指定单元格的内容。</summary>
/// <param name="rowIndex">行索引,从 0 开始。</param>
/// <param name="columnIndex">列索引,从 0 开始。</param>
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) { var value = _table.Rows[rowIndex][columnIndex]; if (value == null || value.Equals(DBNull.Value)) return null; return value; } } } return null; }
/// <summary>获取默认表中指定单元的内容。</summary>
/// <param name="rowIndex">行索引,从 0 开始。</param>
/// <param name="columnName">列名称/字段名称,此名称不区分大小写。</param>
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 { var value = Table.Rows[rowIndex][columnName]; if (value == null || value.Equals(DBNull.Value)) return null; return value; } catch { } } } return null; }
/// <summary>搜索默认表。</summary>
/// <param name="conditionColumn">搜索条件:列名。</param>
/// <param name="conditionValue">搜索条件:列值。</param>
/// <param name="resultColumn">搜索结果。</param>
private 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 { var cValue = Table.Rows[i][conditionColumn]; if (cValue == null || cValue.Equals(DBNull.Value)) cValue = null; if (cValue.ToString() == conditionValue) return Table.Rows[i][resultColumn]; } catch { } } } return null; }
/// <summary>搜索默认表。</summary>
/// <param name="conditionColumn">搜索条件:列名。</param>
/// <param name="conditionValue">搜索条件:列值。</param>
/// <param name="resultColumn">搜索结果的列名。</param>
private 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 { var cValue = Table.Rows[i][conditionColumn]; if (cValue == null || cValue.Equals(DBNull.Value)) cValue = null; if (cValue.ToString() == conditionValue) return Table.Rows[i][resultColumn]; } catch { } } } } return null; }
#endregion
#region Method
/// <summary>搜索默认表。</summary>
/// <param name="conditionColumn">搜索条件:列名。</param>
/// <param name="conditionValue">搜索条件:列值。</param>
/// <param name="resultColumn">搜索结果。</param>
private string Text(int conditionColumn, string conditionValue, int resultColumn) { var value = Value(conditionColumn, conditionValue, resultColumn); return Text(value); }
/// <summary>释放系统资源。</summary>
public 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);
}
/// <summary>当不指定格式化程序时,自动根据类型选择预置的格式化程序。</summary>
private static Func<object, T> GetValueFormatter<T>() { var type = typeof(T); if (type.Equals(typeof(string))) return TextFormatter<T>; else return ForceFormatter<T>; }
/// <summary>获取指定列的所有值,无效值不加入结果。</summary>
public T[] ReadColumn<T>(int column = 0, Func<object, T> formatter = null) => SourceUtility.Column(this, (r) => (formatter ?? GetValueFormatter<T>()).Invoke(Value(r, column)));
/// <summary>获取指定列的所有值,无效值不加入结果。</summary>
/// <exception cref="ArgumentNullException"></exception>
public T[] ReadColumn<T>(string column, Func<object, T> formatter = null) => SourceUtility.Column(this, (r) => (formatter ?? GetValueFormatter<T>()).Invoke(Value(r, column)));
/// <summary>获取指定列的所有值,无效值不加入结果。</summary>
public string[] ReadColumn(int column = 0) => SourceUtility.Column(this, (r) => this.Text(r, column));
/// <summary>获取指定列的所有值,无效值不加入结果。</summary>
/// <exception cref="ArgumentNullException"></exception>
public string[] ReadColumn(string column) => SourceUtility.Column(this, (r) => this.Text(r, column));
#endregion
#region 模型化、IToJson
/// <summary>转换为 Json 对象。</summary>
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; }
/// <summary>转换为模型数组。</summary>
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<T>(object input) => (T)input;
private static T TextFormatter<T>(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) return null; if (value is string str) return str; try { return value.ToString() ?? ""; } catch { } return ""; }
internal static Class<DateTime> DateTime(object value) { if (value is DateTime dt) return dt; DateTime result; var parsed = System.DateTime.TryParse(value.ToString(), out result); return parsed ? new Class<DateTime>(result) : null; }
#endregion
}
}
|