using System; using System.Collections.Generic; using System.Data; using System.Reflection; using System.Text; using static Apewer.NumberUtility; namespace Apewer.Source { /// ORM 帮助程序。 public static class SourceUtility { #region As /// 转换模型类型。 public static TDst[] As(this TSrc[] input) where TDst : class { if (input == null) return null; var count = input.Length; var output = new TDst[count]; for (var i = 0; i < count; i++) { var item = input[i]; if (item == null) continue; output[i] = item as TDst; // 此处可能抛出异常。 } return output; } /// 转换模型类型。 public static Result As(this Result input) where TDst : class { if (input == null) return null; if (!input.HasValue) return new Result(input.Code, input.Message); var value = input.Value as TDst; if (value == null) { var src = input.Value.GetType().FullName; var dst = typeof(TDst).FullName; return new Result($"无法将记录从转换 {src} 到 {dst}。"); } return new Result(value); } /// 转换模型类型。 public static Result As(this Result input) where TDst : class { if (input == null) return null; if (!input.HasValue) return new Result(input.Code, input.Message); var count = input.Value.Length; var output = new TDst[count]; for (var i = 0; i < count; i++) output[i] = input.Value[i] as TDst; return new Result(output); } #endregion #region IQuery -> IRecord /// 读取所有行,生成列表。 public static T[] Fill(this IQuery query) where T : class, new() => As(Fill(query, typeof(T))); /// 读取所有行填充到 T,组成 T[]。 public static object[] Fill(this IQuery query, Type model) { if (query == null) return new object[0]; if (model == null) return new object[0]; var ts = TableStructure.Parse(model); if (ts == null) return new object[0]; var output = new object[query.Rows]; for (int r = 0; r < query.Rows; r++) output[r] = Row(query, r, model, ts); return output; } /// 获取指定列的所有值,无效值不加入结果。 public static T[] Column(this IQuery query, Func filler) { if (query == null || filler == null) return new T[0]; var rows = query.Rows; var output = new T[rows]; var added = 0; for (int r = 0; r < rows; r++) { var value = filler(r); if (value == null) continue; if (value is string str) { if (str == "") continue; } output[added] = value; added++; } if (added < 1) return new T[0]; if (added == rows) return output; var output2 = new T[added]; Array.Copy(output, output2, added); return output2; } /// 将 Query 的行,填充到模型实体。 /// 填充失败时返回 NULL 值。 /// public static object Row(IQuery query, int rowIndex, Type model, TableStructure structure) { // 检查参数。 if (query == null || model == null || structure == null) return null; if (rowIndex < 0 || rowIndex >= query.Rows) return null; if (!RuntimeUtility.CanNew(model)) return null; // 变量别名。 var ts = structure; var r = rowIndex; var columns = ts.Columns; // 检查模型的属性,按属性从表中取相应的列。 var record = Activator.CreateInstance(model); var properties = model.GetProperties(); foreach (var property in properties) { // 在表结构中检查,是否包含此属性,并获取 ColumnAttribute 中的 Field。 var field = null as string; for (var j = 0; j < columns.Length; j++) { if (columns[j].PropertyName == property.Name) { field = columns[j].Field; break; } } if (field == null) { if (ts.Attribute != null && ts.AllProperties) continue; field = property.Name; } var value = query.Value(r, field); var setted = Set(record, property, value); } return record; } static bool Set(object record, PropertyInfo property, object value) { // 读取值。 if (value == null) return false; if (value.Equals(DBNull.Value)) return false; // 必须有 setter 访问器。 var setter = property.GetSetMethod(); if (setter == null) return false; // 根据属性类型设置值。 var pt = property.PropertyType; if (pt.Equals(typeof(object))) setter.Invoke(record, new object[] { value }); else if (pt.Equals(typeof(byte[]))) setter.Invoke(record, new object[] { (byte[])value }); else if (pt.Equals(typeof(string))) setter.Invoke(record, new object[] { value.ToString() }); else if (pt.Equals(typeof(DateTime))) setter.Invoke(record, new object[] { value }); else if (pt.Equals(typeof(bool))) setter.Invoke(record, new object[] { Boolean(value) }); else if (pt.Equals(typeof(byte))) setter.Invoke(record, new object[] { Byte(value) }); else if (pt.Equals(typeof(sbyte))) setter.Invoke(record, new object[] { SByte(value) }); else if (pt.Equals(typeof(short))) setter.Invoke(record, new object[] { Int16(value) }); else if (pt.Equals(typeof(ushort))) setter.Invoke(record, new object[] { UInt16(value) }); else if (pt.Equals(typeof(int))) setter.Invoke(record, new object[] { Int32(value) }); else if (pt.Equals(typeof(uint))) setter.Invoke(record, new object[] { UInt32(value) }); else if (pt.Equals(typeof(long))) setter.Invoke(record, new object[] { Int64(value) }); else if (pt.Equals(typeof(ulong))) setter.Invoke(record, new object[] { UInt64(value) }); else if (pt.Equals(typeof(float))) setter.Invoke(record, new object[] { Single(value) }); else if (pt.Equals(typeof(double))) setter.Invoke(record, new object[] { Double(value) }); else if (pt.Equals(typeof(decimal))) setter.Invoke(record, new object[] { Decimal(value) }); #if !NET20 else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable((DateTime)value) }); else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Boolean(value)) }); else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Byte(value)) }); else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(SByte(value)) }); else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Int16(value)) }); else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(UInt16(value)) }); else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Int32(value)) }); else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(UInt32(value)) }); else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Int64(value)) }); else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(UInt64(value)) }); else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Single(value)) }); else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Double(value)) }); else if (pt.Equals(typeof(Nullable))) setter.Invoke(record, new object[] { new Nullable(Decimal(value)) }); #endif else { try { setter.Invoke(record, new object[] { value }); return true; } catch { } } return false; } #endregion #region IOrm /// 查询记录。 /// 数据库对象。 /// 记录模型。 /// SQL 语句。 /// 为 SQL 命令提供参数。 public static Result Query(IDbAdo database, Type model, string sql, IEnumerable parameters) { if (database == null) return new Result("数据库无效。"); if (model == null) return new Result("模型类型无效。"); if (string.IsNullOrEmpty(sql)) return new Result("SQL 语句无效。"); using (var query = database.Query(sql, parameters)) { if (query == null) return new Result("查询实例无效。"); if (query.Table == null) { if (!string.IsNullOrEmpty(query.Message)) return new Result(query.Message); return new Result("查询实例不包含数据表。"); } try { var array = Fill(query, model); return new Result(array); } catch (Exception ex) { return new Result(ex); } } } // /// 查询记录。 // /// 记录模型。 // /// 数据库对象。 // /// SQL 语句。 // public static Result Query(IDbClientAdo database, string sql) where T : class, new() => As(Query(database, typeof(T), sql)); /// 查询记录。 /// 数据库对象。 /// 记录模型。 /// 生成 SQL 语句的函数,传入参数为表名。 public static Result Query(IDbAdo database, Type model, Func sqlGetter) { if (sqlGetter == null) return new Result("SQL 语句获取函数无效。"); try { var tableName = TableStructure.Parse(model).Name; if (string.IsNullOrEmpty(tableName)) return new Result("表名无效。"); return Query(database, model, sqlGetter(tableName), null); } catch (Exception ex) { return new Result(ex); } } /// 查询记录。 /// 记录模型。 /// 数据库对象。 /// 生成 SQL 语句的函数,传入参数为表名。 public static Result Query(IDbAdo database, Func sqlGetter) where T : class, new() => As(Query(database, typeof(T), sqlGetter)); /// 获取具有指定主键的记录。 /// 数据库对象。 /// 记录模型。 /// 主键。 /// 生成 SQL 语句的函数,传入参数为表名和主键值。 public static Result Get(IDbAdo database, Type model, string key, Func sqlGetter) { if (sqlGetter == null) return new Result("SQL 语句获取函数无效。"); var safetyKey = TextUtility.SafeKey(key); if (string.IsNullOrEmpty(safetyKey)) return new Result("主键无效。"); var query = null as IQuery; var record = null as object; try { record = Activator.CreateInstance(model); var ts = TableStructure.Parse(model); var tableName = ts.Name; if (string.IsNullOrEmpty(tableName)) return new Result("表名无效。"); var sql = sqlGetter(tableName, safetyKey); query = database.Query(sql); if (query.Table == null) return new Result("没有获取到记录。"); record = Row(query, 0, model, ts); } catch (Exception ex) { RuntimeUtility.Dispose(query); return new Result(ex); } RuntimeUtility.Dispose(query); if (record == null) return new Result("没有获取到记录。"); return new Result(record); } /// 获取具有指定主键的记录。 /// 记录模型。 /// 数据库对象。 /// 主键。 /// 生成 SQL 语句的函数,传入参数为表名和主键值。 public static Result Get(IDbAdo database, string key, Func sqlGetter) where T : class, IRecord, new() => As(Get(database, typeof(T), key, sqlGetter)); /// 获取主键。 /// 数据库对象。 /// 记录模型。 /// 生成 SQL 语句的函数,传入参数为表名。 public static Result Keys(IDbAdo database, Type model, Func sqlGetter) { if (database == null) return new Result("数据库无效。"); if (model == null) return new Result("模型类型无效。"); if (sqlGetter == null) return new Result("SQL 语句获取函数无效。"); var tableStructure = null as TableStructure; try { tableStructure = TableStructure.Parse(model); } catch (Exception ex) { return new Result(ex); } var tableName = tableStructure.Name; if (string.IsNullOrEmpty(tableName)) return new Result("表名无效。"); var sql = sqlGetter(tableName); var query = null as IQuery; try { query = database.Query(sql); if (query == null) return new Result("查询实例无效。"); var list = new List(query.Rows); for (var r = 0; r < query.Rows; r++) { var key = TextUtility.SafeKey(query.Text(r)); if (string.IsNullOrEmpty(key)) continue; list.Add(key); } query.Dispose(); list.Capacity = list.Count; var array = list.ToArray(); return new Result(array); } catch (Exception ex) { RuntimeUtility.Dispose(query); return new Result(ex); } } /// 获取主键。 /// 记录模型。 /// 数据库对象。 /// 生成 SQL 语句的函数,传入参数为表名。 public static Result Keys(IDbAdo database, Func sqlGetter) where T : IRecord { return Keys(database, typeof(T), sqlGetter); } #endregion #region Record /// 修复记录属性。 public static void FixProperties(object record) { if (record == null) return; if (record is IRecord key) { if (string.IsNullOrEmpty(key.Key)) key.ResetKey(); } if (record is IRecordMoment moment) { var now = moment.GenerateMoment(); if (string.IsNullOrEmpty(moment.Created)) moment.Created = now; if (string.IsNullOrEmpty(moment.Updated)) moment.Updated = now; } if (record is IRecordStamp stamp) { var now = stamp.GenerateStamp(); if (stamp.Created == 0L) stamp.Created = now; if (stamp.Updated == 0L) stamp.Updated = now; } } /// 设置 Updated 属性。 /// TRUE:设置成功;FALSE:设置失败。 public static bool SetUpdated(object record) { if (record == null) return false; var setted = false; if (record is IRecordMoment moment) { moment.Updated = moment.GenerateMoment(); setted = true; } if (record is IRecordStamp stamp) { stamp.Updated = stamp.GenerateStamp(); setted = true; } return setted; } /// 枚举带有 Table 特性的 派生类型。 public static Type[] EnumerateRecords() where T : IRecord => EnumerateRecords(typeof(T)); /// 枚举带有 Table 特性的派生类型。 /// public static Type[] EnumerateRecords(Type baseType) { if (baseType == null) throw new ArgumentNullException(nameof(baseType)); var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var builder = new ArrayBuilder(); foreach (var assembly in assemblies) { var types = RuntimeUtility.GetTypes(assembly); foreach (var type in types) { if (!EnumerateRecords(type, baseType)) continue; if (builder.Contains(type)) continue; builder.Add(type); } } return builder.Export(); } static bool EnumerateRecords(Type type, Type @base) { if (type == null || @base == null) return false; if (type.IsAbstract) return false; if (!RuntimeUtility.Contains(type, false)) return false; if (type.Equals(@base)) return true; if (RuntimeUtility.IsInherits(type, @base)) return true; return false; } #endregion #region DbClient /// 简单查询:取结果中第 0 列所有单元格的文本形式,可指定查询后关闭服务器连接,返回结果中不包含无效文本。 /// 数据库客户端。 /// 用于查询的 SQL 语句。 /// 查询后,关闭数据库链接。 public static string[] SimpleColumn(this IDbAdo source, string sql, bool close = false) { if (source == null) return new string[0]; var ab = new ArrayBuilder(); using (var query = source.Query(sql)) { var rows = query.Rows; if (rows > 0) { var added = 0; for (int i = 0; i < rows; i++) { var cell = TextUtility.Trim(query.Text(i)); if (string.IsNullOrEmpty(cell)) continue; ab.Add(cell); added++; } } } if (close) RuntimeUtility.Dispose(source); return ab.Export(); } /// 简单查询:取结果中第 0 行、第 0 列单元格中的文本,可指定查询后关闭服务器连接。 /// 数据库客户端。 /// 用于查询的 SQL 语句。 /// 查询后,关闭数据库链接。 public static string SimpleCell(this IDbAdo source, string sql, bool close = false) { if (source == null) return null; var value = null as string; using (var query = source.Query(sql)) value = TextUtility.Trim(query.Text()); if (close) RuntimeUtility.Dispose(source); return value; } #endregion #region SQL /// 对文本转义,符合 SQL 安全性。可根据字段类型限制 UTF-8 字节数,默认为 0 时不限制字节数。 public static string Escape(this string text, int bytes = 0) { if (text.IsEmpty()) return ""; var t = text ?? ""; t = t.Replace("\\", "\\\\"); t = t.Replace("'", "\\'"); t = t.Replace("\n", "\\n"); t = t.Replace("\r", "\\r"); t = t.Replace("\b", "\\b"); t = t.Replace("\t", "\\t"); t = t.Replace("\f", "\\f"); if (bytes > 5) { if (t.Bytes(Encoding.UTF8).Length > bytes) { while (true) { t = t.Substring(0, t.Length - 1); if (t.Bytes(Encoding.UTF8).Length <= (bytes - 4)) break; } t = t + " ..."; } } return t; } #endregion #region DataTable /// 将多个实体元素转换为 DataTable。 /// 实体元素的类型。 /// 实体元素。 /// public static DataTable DataTable(this IEnumerable items, string tableName = null) { if (items == null) throw new ArgumentNullException(nameof(items)); // 解析表结构。 var it = typeof(T); var ts = TableStructure.Parse(it); var cas = ts.Columns; var width = cas.Length; // 初始化列。 var table = new DataTable(); var gs = new MethodInfo[width]; var pts = new Type[width]; for (var i = 0; i < width; i++) { var ca = cas[i]; var p = ca.Property; var pt = p.PropertyType; var g = p.GetGetMethod(); if (g != null) { gs[i] = g; pts[i] = pt; table.Columns.Add(ca.Field, pt); } } // 添加行。 foreach (var item in items) { if (item == null) continue; var values = new ArrayBuilder(width); for (var i = 0; i < width; i++) { if (gs[i] == null) continue; var v = gs[i].Invoke(item, null); values.Add(v); } table.Rows.Add(values.Export()); } if (tableName.NotEmpty()) table.TableName = tableName; return table; } #endregion } }