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.

445 lines
18 KiB

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>(IQuery query) where T : class, new() => As<object, T>(Fill(query, typeof(T)));
/// <summary>读取所有行填充到 T,组成 T[]。</summary>
public static object[] Fill(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>(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 IRecord 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 pt = property.PropertyType;
if (pt.Equals(typeof(object)) || pt.Equals(typeof(Nullable<DateTime>)))
{
setter.Invoke(record, new object[] { query.Value(r, attribute.Field) });
}
else if (pt.Equals(typeof(DateTime)))
{
var value = query.Value(r, attribute.Field);
if (value != null) setter.Invoke(record, new object[] { query.Value(r, attribute.Field) });
}
else if (pt.Equals(typeof(byte[])))
{
setter.Invoke(record, new object[] { (byte[])query.Value(r, attribute.Field) });
}
else if (pt.Equals(typeof(byte)))
{
setter.Invoke(record, new object[] { Byte(query.Text(r, attribute.Field)) });
}
else if (pt.Equals(typeof(sbyte)))
{
setter.Invoke(record, new object[] { SByte(query.Text(r, attribute.Field)) });
}
else if (pt.Equals(typeof(short)))
{
setter.Invoke(record, new object[] { Int16(query.Text(r, attribute.Field)) });
}
else if (pt.Equals(typeof(ushort)))
{
setter.Invoke(record, new object[] { UInt16(query.Text(r, attribute.Field)) });
}
else if (pt.Equals(typeof(int)))
{
setter.Invoke(record, new object[] { Int32(query.Text(r, attribute.Field)) });
}
else if (pt.Equals(typeof(uint)))
{
setter.Invoke(record, new object[] { UInt32(query.Text(r, attribute.Field)) });
}
else if (pt.Equals(typeof(long)))
{
setter.Invoke(record, new object[] { Int64(query.Text(r, attribute.Field)) });
}
else if (pt.Equals(typeof(ulong)))
{
setter.Invoke(record, new object[] { UInt64(query.Text(r, attribute.Field)) });
}
else if (pt.Equals(typeof(float)))
{
setter.Invoke(record, new object[] { Single(query.Text(r, attribute.Field)) });
}
else if (pt.Equals(typeof(double)))
{
setter.Invoke(record, new object[] { Double(query.Text(r, attribute.Field)) });
}
else if (pt.Equals(typeof(decimal)))
{
setter.Invoke(record, new object[] { Decimal(query.Text(r, attribute.Field)) });
}
else if (pt.Equals(typeof(string)))
{
setter.Invoke(record, new object[] { query.Text(r, attribute.Field) });
}
else
{
try
{
setter.Invoke(record, new object[] { query.Value(r, attribute.Field) });
}
catch { }
}
}
return record as IRecord;
}
#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) as Query)
{
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<IRecord> Get(IDbClientAdo database, Type model, string key, Func<string, string, string> sqlGetter)
{
if (sqlGetter == null) return new Result<IRecord>("SQL 语句获取函数无效。");
var safetyKey = TextUtility.SafeKey(key);
if (string.IsNullOrEmpty(safetyKey)) return new Result<IRecord>("主键无效。");
var query = null as IQuery;
var record = null as IRecord;
try
{
var ts = TableStructure.Parse(model);
var tableName = ts.Name;
if (string.IsNullOrEmpty(tableName)) return new Result<IRecord>("表名无效。");
var sql = sqlGetter(tableName, safetyKey);
query = database.Query(sql);
if (query.Table == null) return new Result<IRecord>("没有获取到记录。");
record = Row(query, 0, model, ts);
}
catch (Exception ex)
{
RuntimeUtility.Dispose(query);
return new Result<IRecord>(ex);
}
RuntimeUtility.Dispose(query);
if (record == null) return new Result<IRecord>("没有获取到记录。");
return new Result<IRecord>(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<IRecord, 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) key.ResetKey();
if (record is IRecordMoment moment)
{
var now = ClockUtility.LucidNow;
if (string.IsNullOrEmpty(moment.Created)) moment.Created = now;
if (string.IsNullOrEmpty(moment.Updated)) moment.Updated = now;
}
if (record is IRecordStamp stamp)
{
var utc = ClockUtility.UtcStamp;
if (stamp.Created == 0L) stamp.Created = utc;
if (stamp.Updated == 0L) stamp.Updated = utc;
}
}
/// <summary>设置 Updated 属性。</summary>
/// <returns>TRUE:设置成功;FALSE:设置失败。</returns>
public static bool SetUpdated(object record)
{
if (record == null) return false;
if (record is IRecordMoment moment)
{
var now = ClockUtility.LucidNow;
moment.Updated = now;
return true;
}
if (record is IRecordStamp stamp)
{
var utc = ClockUtility.UtcStamp;
stamp.Updated = utc;
return true;
}
return false;
}
#endregion
}
}