using System; using System.Collections.Generic; using System.Text; using static Apewer.NumberUtility; namespace Apewer.Source { /// <summary>ORM 帮助程序。</summary> public static class OrmHelper { #region As /// <summary>转换模型类型。</summary> public static TDst[] As<TSrc, TDst>(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; } /// <summary>转换模型类型。</summary> public static Result<TDst> As<TSrc, TDst>(this Result<TSrc> input) where TDst : class { if (input == null) return null; if (!input.HasValue) return new Result<TDst>(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<TDst>($"无法将记录从转换 {src} 到 {dst}。"); } return new Result<TDst>(value); } /// <summary>转换模型类型。</summary> public static Result<TDst[]> As<TSrc, TDst>(this Result<TSrc[]> input) where TDst : class { if (input == null) return null; if (!input.HasValue) return new Result<TDst[]>(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<TDst[]>(output); } #endregion #region IQuery -> IRecord /// <summary>读取所有行,生成列表。</summary> public static T[] Fill<T>(this IQuery query) where T : class, new() => As<object, T>(Fill(query, typeof(T))); /// <summary>读取所有行填充到 T,组成 T[]。</summary> 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; } /// <summary>获取指定列的所有值,无效值不加入结果。</summary> public static T[] Column<T>(this IQuery query, Func<int, T> 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; } /// <summary>将 Query 的行,填充到模型实体。</summary> /// <remarks>填充失败时返回 NULL 值。</remarks> /// <exception cref="Exception"></exception> 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) { // 必须有 setter 访问器。 var setter = property.GetSetMethod(); if (setter == null) continue; // 在表结构中检查,是否包含此属性,并获取 ColumnAttribute。 var attribute = null as ColumnAttribute; for (var j = 0; j < columns.Length; j++) { if (columns[j].PropertyName == property.Name) { attribute = columns[j]; break; } } if (attribute == null) continue; // 读取值。 var value = query.Value(r, attribute.Field); if (value == null) continue; if (value.Equals(DBNull.Value)) continue; // 根据属性类型设置值。 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(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) }); else if (pt.Equals(typeof(Nullable<DateTime>))) setter.Invoke(record, new object[] { new Nullable<DateTime>((DateTime)value) }); else if (pt.Equals(typeof(Nullable<byte>))) setter.Invoke(record, new object[] { new Nullable<byte>(Byte(value) ) }); else if (pt.Equals(typeof(Nullable<sbyte>))) setter.Invoke(record, new object[] { new Nullable<sbyte>(SByte(value) ) }); else if (pt.Equals(typeof(Nullable<short>))) setter.Invoke(record, new object[] { new Nullable<short>(Int16(value) ) }); else if (pt.Equals(typeof(Nullable<ushort>))) setter.Invoke(record, new object[] { new Nullable<int>(UInt16(value) ) }); else if (pt.Equals(typeof(Nullable<int>))) setter.Invoke(record, new object[] { new Nullable<int>(Int32(value) ) }); else if (pt.Equals(typeof(Nullable<uint>))) setter.Invoke(record, new object[] { new Nullable<uint>(UInt32(value) ) }); else if (pt.Equals(typeof(Nullable<long>))) setter.Invoke(record, new object[] { new Nullable<long>(Int64(value) ) }); else if (pt.Equals(typeof(Nullable<ulong>))) setter.Invoke(record, new object[] { new Nullable<ulong>(UInt64(value) ) }); else if (pt.Equals(typeof(Nullable<float>))) setter.Invoke(record, new object[] { new Nullable<float>(Single(value) ) }); else if (pt.Equals(typeof(Nullable<double>))) setter.Invoke(record, new object[] { new Nullable<double>(Double(value) ) }); else if (pt.Equals(typeof(Nullable<decimal>))) setter.Invoke(record, new object[] { new Nullable<decimal>(Decimal(value) ) }); else { try { setter.Invoke(record, new object[] { query.Value(r, attribute.Field) }); } catch { } } } return record; } #endregion #region IOrm /// <summary>查询记录。</summary> /// <param name="database">数据库对象。</param> /// <param name="model">记录模型。</param> /// <param name="sql">SQL 语句。</param> public static Result<object[]> Query(IDbClientAdo database, Type model, string sql) { if (database == null) return new Result<object[]>("数据库无效。"); if (model == null) return new Result<object[]>("模型类型无效。"); if (string.IsNullOrEmpty(sql)) return new Result<object[]>("SQL 语句无效。"); using (var query = database.Query(sql)) { if (query == null) return new Result<object[]>("查询实例无效。"); if (query.Table == null) { if (!string.IsNullOrEmpty(query.Message)) return new Result<object[]>(query.Message); return new Result<object[]>("查询实例不包含数据表。"); } try { var array = Fill(query, model); return new Result<object[]>(array); } catch (Exception ex) { return new Result<object[]>(ex); } } } // /// <summary>查询记录。</summary> // /// <typeparam name="T">记录模型。</typeparam> // /// <param name="database">数据库对象。</param> // /// <param name="sql">SQL 语句。</param> // public static Result<T[]> Query<T>(IDbClientAdo database, string sql) where T : class, new() => As<object, T>(Query(database, typeof(T), sql)); /// <summary>查询记录。</summary> /// <param name="database">数据库对象。</param> /// <param name="model">记录模型。</param> /// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param> public static Result<object[]> Query(IDbClientAdo database, Type model, Func<string, string> sqlGetter) { if (sqlGetter == null) return new Result<object[]>("SQL 语句获取函数无效。"); try { var tableName = TableStructure.Parse(model).Name; if (string.IsNullOrEmpty(tableName)) return new Result<object[]>("表名无效。"); return Query(database, model, sqlGetter(tableName)); } catch (Exception ex) { return new Result<object[]>(ex); } } /// <summary>查询记录。</summary> /// <typeparam name="T">记录模型。</typeparam> /// <param name="database">数据库对象。</param> /// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param> public static Result<T[]> Query<T>(IDbClientAdo database, Func<string, string> sqlGetter) where T : class, new() => As<object, T>(Query(database, typeof(T), sqlGetter)); /// <summary>获取具有指定主键的记录。</summary> /// <param name="database">数据库对象。</param> /// <param name="model">记录模型。</param> /// <param name="key">主键。</param> /// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名和主键值。</param> public static Result<object> Get(IDbClientAdo database, Type model, string key, Func<string, string, string> sqlGetter) { if (sqlGetter == null) return new Result<object>("SQL 语句获取函数无效。"); var safetyKey = TextUtility.SafeKey(key); if (string.IsNullOrEmpty(safetyKey)) return new Result<object>("主键无效。"); 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<object>("表名无效。"); var sql = sqlGetter(tableName, safetyKey); query = database.Query(sql); if (query.Table == null) return new Result<object>("没有获取到记录。"); record = Row(query, 0, model, ts); } catch (Exception ex) { RuntimeUtility.Dispose(query); return new Result<object>(ex); } RuntimeUtility.Dispose(query); if (record == null) return new Result<object>("没有获取到记录。"); return new Result<object>(record); } /// <summary>获取具有指定主键的记录。</summary> /// <typeparam name="T">记录模型。</typeparam> /// <param name="database">数据库对象。</param> /// <param name="key">主键。</param> /// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名和主键值。</param> public static Result<T> Get<T>(IDbClientAdo database, string key, Func<string, string, string> sqlGetter) where T : class, IRecord, new() => As<object, T>(Get(database, typeof(T), key, sqlGetter)); /// <summary>获取主键。</summary> /// <param name="database">数据库对象。</param> /// <param name="model">记录模型。</param> /// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param> public static Result<string[]> Keys(IDbClientAdo database, Type model, Func<string, string> sqlGetter) { if (database == null) return new Result<string[]>("数据库无效。"); if (model == null) return new Result<string[]>("模型类型无效。"); if (sqlGetter == null) return new Result<string[]>("SQL 语句获取函数无效。"); var tableStructure = null as TableStructure; try { tableStructure = TableStructure.Parse(model); } catch (Exception ex) { return new Result<string[]>(ex); } var tableName = tableStructure.Name; if (string.IsNullOrEmpty(tableName)) return new Result<string[]>("表名无效。"); // var keyName = null as string; // foreach (var column in tableStructure.Columns) // { // if (column.Key == "Key") // { // keyName = column.Value.Field; // break; // } // } // if (string.IsNullOrEmpty(keyName)) return Result<List<string>>.Error("主键字段无效。"); // var sql = sqlGetter(tableName, keyName); var sql = sqlGetter(tableName); var query = null as IQuery; try { query = database.Query(sql); if (query == null) return new Result<string[]>("查询实例无效。"); var list = new List<string>(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<string[]>(array); } catch (Exception ex) { RuntimeUtility.Dispose(query); return new Result<string[]>(ex); } } /// <summary>获取主键。</summary> /// <typeparam name="T">记录模型。</typeparam> /// <param name="database">数据库对象。</param> /// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param> public static Result<string[]> Keys<T>(IDbClientAdo database, Func<string, string> sqlGetter) where T : IRecord { return Keys(database, typeof(T), sqlGetter); } #endregion #region Record /// <summary>修复记录属性。</summary> public static void FixProperties(object record) { if (record == null) return; if (record is IRecord key) { if (string.IsNullOrEmpty(key.Key)) key.ResetKey(); } var now = DateTime.Now; if (record is IRecordMoment moment) { if (string.IsNullOrEmpty(moment.Created)) moment.Created = now.Lucid(); if (string.IsNullOrEmpty(moment.Updated)) moment.Updated = now.Lucid(); } if (record is IRecordStamp stamp) { var utc = ClockUtility.UtcStamp; if (stamp.Created == 0L) stamp.Created = now.Stamp(); if (stamp.Updated == 0L) stamp.Updated = now.Stamp(); } } /// <summary>设置 Updated 属性。</summary> /// <returns>TRUE:设置成功;FALSE:设置失败。</returns> public static bool SetUpdated(object record) { if (record == null) return false; var now = DateTime.Now; var setted = false; if (record is IRecordMoment moment) { moment.Updated = now.Lucid(); setted = true; } if (record is IRecordStamp stamp) { stamp.Updated = now.Stamp(); setted = true; } return setted; } #endregion } }