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.
591 lines
25 KiB
591 lines
25 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
|
|
using static Apewer.NumberUtility;
|
|
|
|
namespace Apewer.Source
|
|
{
|
|
|
|
/// <summary>ORM 帮助程序。</summary>
|
|
public static class SourceUtility
|
|
{
|
|
|
|
#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)
|
|
{
|
|
|
|
// 在表结构中检查,是否包含此属性,并获取 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<DateTime>))) setter.Invoke(record, new object[] { new Nullable<DateTime>((DateTime)value) });
|
|
else if (pt.Equals(typeof(Nullable<bool>))) setter.Invoke(record, new object[] { new Nullable<bool>(Boolean(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)) });
|
|
#endif
|
|
|
|
else
|
|
{
|
|
try
|
|
{
|
|
setter.Invoke(record, new object[] { value });
|
|
return true;
|
|
}
|
|
catch { }
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IOrm
|
|
|
|
/// <summary>查询记录。</summary>
|
|
/// <param name="database">数据库对象。</param>
|
|
/// <param name="model">记录模型。</param>
|
|
/// <param name="sql">SQL 语句。</param>
|
|
/// <param name="parameters">为 SQL 命令提供参数。</param>
|
|
public static Result<object[]> Query(IDbAdo database, Type model, string sql, IEnumerable<IDataParameter> parameters)
|
|
{
|
|
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, parameters))
|
|
{
|
|
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(IDbAdo 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), null);
|
|
}
|
|
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>(IDbAdo 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(IDbAdo 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>(IDbAdo 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(IDbAdo 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 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>(IDbAdo 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();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>设置 Updated 属性。</summary>
|
|
/// <returns>TRUE:设置成功;FALSE:设置失败。</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>枚举带有 Table 特性的 <typeparamref name="T"/> 派生类型。</summary>
|
|
public static Type[] EnumerateRecords<T>() where T : IRecord => EnumerateRecords(typeof(T));
|
|
|
|
/// <summary>枚举带有 Table 特性的派生类型。</summary>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
public static Type[] EnumerateRecords(Type baseType)
|
|
{
|
|
if (baseType == null) throw new ArgumentNullException(nameof(baseType));
|
|
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
|
var builder = new ArrayBuilder<Type>();
|
|
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<TableAttribute>(type, false)) return false;
|
|
if (type.Equals(@base)) return true;
|
|
if (RuntimeUtility.IsInherits(type, @base)) return true;
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DbClient
|
|
|
|
/// <summary>简单查询:取结果中第 0 列所有单元格的文本形式,可指定查询后关闭服务器连接,返回结果中不包含无效文本。</summary>
|
|
/// <param name="source">数据库客户端。</param>
|
|
/// <param name="sql">用于查询的 SQL 语句。</param>
|
|
/// <param name="close">查询后,关闭数据库链接。</param>
|
|
public static string[] SimpleColumn(this IDbAdo source, string sql, bool close = false)
|
|
{
|
|
if (source == null) return new string[0];
|
|
var ab = new ArrayBuilder<string>();
|
|
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();
|
|
}
|
|
|
|
/// <summary>简单查询:取结果中第 0 行、第 0 列单元格中的文本,可指定查询后关闭服务器连接。</summary>
|
|
/// <param name="source">数据库客户端。</param>
|
|
/// <param name="sql">用于查询的 SQL 语句。</param>
|
|
/// <param name="close">查询后,关闭数据库链接。</param>
|
|
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
|
|
|
|
/// <summary>对文本转义,符合 SQL 安全性。可根据字段类型限制 UTF-8 字节数,默认为 0 时不限制字节数。</summary>
|
|
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
|
|
|
|
/// <summary>将多个实体元素转换为 DataTable。</summary>
|
|
/// <typeparam name="T">实体元素的类型。</typeparam>
|
|
/// <param name="items">实体元素。</param>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
public static DataTable DataTable<T>(this IEnumerable<T> 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<object>(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
|
|
|
|
}
|
|
|
|
}
|
|
|