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 (query.Table == null) return new object[0];
if (model == null) return new object[0];
return Fill(query.Table, model);
}
/// 获取指定列的所有值,无效值不加入结果。
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 && ts.Table.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).TableName;
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.TableName;
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.TableName;
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;
}
/// 查询。
///
public static IQuery Query(this IDbClient dbClient, string sql, IEnumerable> parameters)
{
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
return dbClient.Query(sql, Parameters(dbClient, parameters));
}
/// 执行 SQL 语句,并加入参数。
///
public static IExecute Execute(this IDbClient dbClient, string sql, IEnumerable> parameters, bool autoTransaction = false)
{
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
return dbClient.Execute(sql, Parameters(dbClient, parameters), autoTransaction);
}
///
static List Parameters(IDbClient dbClient, IEnumerable> parameters)
{
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
var ps = new List();
if (parameters != null)
{
foreach (var parameter in parameters) ps.Add(dbClient.Parameter(parameter.Key, parameter.Value));
}
return ps;
}
#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;
}
/// 限定名称文本,只允许包含字母、数字和下划线。
public static string SafeName(this string name) => TextUtility.Restrict(name, "0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
#endregion
#region DataTable
/// 解析 DataTable,填充没行到到指定的类型中,形成数组。
/// 将要读取的表。
/// 当类型不同时,尝试转换以兼容。
/// 由指定类型组成的数组。
///
///
public static T[] Fill(this DataTable table, bool compatible = true)
{
if (table == null) throw new ArgumentNullException(nameof(table), $"参数 {table} 无效。");
var objects = Fill(table, typeof(T), compatible);
var count = objects.Length;
var array = new T[count];
for (var i = 0; i < count; i++) array[i] = (T)objects[i];
return array;
}
/// 解析 DataTable,填充没行到到指定的类型中,形成数组。
/// 将要读取的表。
/// 要填充的目标类型,必须是可实例化的引用类型。
/// 当类型不同时,尝试转换以兼容。
/// 由指定类型组成的数组。
///
///
public static object[] Fill(this DataTable table, Type model, bool compatible = true)
{
if (table == null) throw new ArgumentNullException(nameof(table), $"参数 {table} 无效。");
if (model == null) throw new ArgumentNullException(nameof(model), $"参数 {model} 无效。");
// 检查模型是否允许填充。
var ts = TableStructure.Parse(model, true, true);
if (ts == null) throw new ArgumentException($"无法填充到类型 {model.FullName} 中。");
// 检查行数。
var rows = table.Rows;
var rowsCount = rows.Count;
if (rowsCount < 1) return new object[0];
// 确定数组。
var array = new object[rowsCount];
for (var i = 0; i < rowsCount; i++) array[i] = Activator.CreateInstance(model, true);
// 检查列数。
var columns = table.Columns;
var columnsCount = columns.Count;
if (columnsCount < 1) return array;
// 解析表头,仅保留有名称的列。
var sc = 0;
var sfs = new string[columnsCount];
var sts = new Type[columnsCount];
var sis = new int[columnsCount];
for (var i = 0; i < columnsCount; i++)
{
var column = columns[i];
var key = column.ColumnName.Lower();
if (string.IsNullOrEmpty(key)) continue;
if (sfs.Contains(key)) continue;
sfs[sc] = key;
sts[sc] = column.DataType;
sis[sc] = i;
sc++;
}
if (sc < 1) return array;
// 解析模型列。
var dc = 0;
var dfs = new string[ts.Columns.Length];
var dts = new ColumnAttribute[ts.Columns.Length];
for (var i = 0; i < ts.Columns.Length; i++)
{
var ca = ts.Columns[i];
var key = ca.Field.Lower();
if (string.IsNullOrEmpty(key)) continue;
if (dfs.Contains(key)) continue;
dfs[dc] = key;
dts[dc] = ca;
dc++;
}
if (dc < 1) return array;
// 遍历、填充。
for (var r = 0; r < rowsCount; r++)
{
var record = array[r];
// 遍历 table 的列。
for (var s = 0; s < sc; s++)
{
var sf = sfs[s];
// 遍历 model 的列。
for (var d = 0; d < dc; d++)
{
var df = dfs[d];
if (df != sf) continue;
// 取值、填充。
var value = rows[r][sis[s]];
Fill(record, dts[d], sts[s], value, compatible);
break;
}
}
}
return array;
}
static bool Fill(object record, ColumnAttribute ca, Type st, object value, bool compatible)
{
// 如果是 NULL 则忽略填充。
if (value.IsNull()) return false;
// 获取属性的类型,必须与 table 中的类型相同。
var prop = ca.Property;
if (prop.PropertyType == st)
{
prop.SetValue(record, value, null);
return true;
}
// 类型不同且不需要兼容时,不填充。
if (!compatible) return false;
// 根据属性类型设置值。
var pt = prop.PropertyType;
if (pt.Equals(typeof(object))) prop.SetValue(record, value, null);
else if (pt.Equals(typeof(byte[]))) prop.SetValue(record, (byte[])value, null);
else if (pt.Equals(typeof(string))) prop.SetValue(record, value.ToString(), null);
else if (pt.Equals(typeof(DateTime))) prop.SetValue(record, value, null);
else if (pt.Equals(typeof(bool))) prop.SetValue(record, Boolean(value), null);
else if (pt.Equals(typeof(byte))) prop.SetValue(record, Byte(value), null);
else if (pt.Equals(typeof(sbyte))) prop.SetValue(record, SByte(value), null);
else if (pt.Equals(typeof(short))) prop.SetValue(record, Int16(value), null);
else if (pt.Equals(typeof(ushort))) prop.SetValue(record, UInt16(value), null);
else if (pt.Equals(typeof(int))) prop.SetValue(record, Int32(value), null);
else if (pt.Equals(typeof(uint))) prop.SetValue(record, UInt32(value), null);
else if (pt.Equals(typeof(long))) prop.SetValue(record, Int64(value), null);
else if (pt.Equals(typeof(ulong))) prop.SetValue(record, UInt64(value), null);
else if (pt.Equals(typeof(float))) prop.SetValue(record, Single(value), null);
else if (pt.Equals(typeof(double))) prop.SetValue(record, Double(value), null);
else if (pt.Equals(typeof(decimal))) prop.SetValue(record, Decimal(value), null);
else if (pt.Equals(typeof(Nullable))) prop.SetValue(record, new Nullable((DateTime)value), null);
else if (pt.Equals(typeof(Nullable))) prop.SetValue(record, new Nullable(Boolean(value)), null);
else if (pt.Equals(typeof(Nullable))) prop.SetValue(record, new Nullable(Byte(value)), null);
else if (pt.Equals(typeof(Nullable))) prop.SetValue(record, new Nullable(SByte(value)), null);
else if (pt.Equals(typeof(Nullable))) prop.SetValue(record, new Nullable(Int16(value)), null);
else if (pt.Equals(typeof(Nullable))) prop.SetValue(record, new Nullable(UInt16(value)), null);
else if (pt.Equals(typeof(Nullable))) prop.SetValue(record, new Nullable(Int32(value)), null);
else if (pt.Equals(typeof(Nullable))) prop.SetValue(record, new Nullable(UInt32(value)), null);
else if (pt.Equals(typeof(Nullable))) prop.SetValue(record, new Nullable(Int64(value)), null);
else if (pt.Equals(typeof(Nullable))) prop.SetValue(record, new Nullable(UInt64(value)), null);
else if (pt.Equals(typeof(Nullable))) prop.SetValue(record, new Nullable(Single(value)), null);
else if (pt.Equals(typeof(Nullable))) prop.SetValue(record, new Nullable(Double(value)), null);
else if (pt.Equals(typeof(Nullable))) prop.SetValue(record, new Nullable(Decimal(value)), null);
else
{
try
{
prop.SetValue(record, value, null);
return true;
}
catch { }
}
return false;
}
/// 将多个实体元素转换为 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, true, true);
if (ts == null || ts.Columns == null || ts.Columns.Length < 1)
{
foreach (var item in items)
{
if (item == null) continue;
var itemType = item.GetType();
ts = TableStructure.Parse(itemType, true, true);
if (ts == null) throw new TypeLoadException($"无法解析 {itemType.FullName} 的结构。");
it = itemType;
break;
}
if (ts == null) throw new TypeLoadException($"无法解析 {it.FullName} 的结构。");
}
var cas = ts.Columns;
var width = cas.Length;
if (width < 1) throw new TypeLoadException($"类型 {it.FullName} 的结构中没有列。");
// 初始化列。
var table = new DataTable();
var pis = new PropertyInfo[width];
var fts = new Type[width];
for (var i = 0; i < width; i++)
{
var ca = cas[i];
var pi = ca.Property;
var pt = pi.PropertyType;
pis[i] = pi;
var ft = pt;
if (pt.IsGenericType && pt.GetGenericTypeDefinition() == typeof(Nullable<>))
{
pt.GetGenericArguments();
ft = Nullable.GetUnderlyingType(pt);
}
fts[i] = ft;
var column = new DataColumn(ca.Field, ft);
column.AllowDBNull = true;
table.Columns.Add(column);
}
// 添加行。
foreach (var item in items)
{
if (item == null) continue;
var values = new ArrayBuilder(width);
for (var i = 0; i < width; i++)
{
var value = pis[i].GetValue(item, null);
if (value is DateTime dt)
{
if (dt.Year < 1753)
{
values.Add(DBNull.Value);
continue;
}
}
values.Add(value);
}
table.Rows.Add(values.Export());
}
if (tableName.NotEmpty()) table.TableName = tableName;
else if (ts.TableName.NotEmpty()) table.TableName = ts.TableName;
return table;
}
/// 转换 到 数组,每行记录为一个 ObjectSet 对象。
/// 当参数 table 无效时返回 0 长度的 数组。
internal static ObjectSet[] ObjectSet(this DataTable table)
{
if (table == null) return new ObjectSet[0];
var columns = table.Columns.Count;
var fields = new string[columns];
for (var c = 0; c < columns; c++) fields[c] = table.Columns[c].ColumnName;
var rows = table.Rows.Count;
var dicts = new Dictionary[rows];
for (var r = 0; r < table.Rows.Count; r++)
{
var dict = new Dictionary(columns);
for (var c = 0; c < columns; c++)
{
var field = fields[c];
if (string.IsNullOrEmpty(field)) continue;
if (dict.ContainsKey(field)) continue;
var v = table.Rows[r][c];
if (v.IsNull()) v = null;
dict.Add(field, v);
}
dicts[r] = dict;
}
var oss = new ObjectSet[rows];
for (var i = 0; i < rows; i++) oss[i] = new ObjectSet(dicts[i]);
return oss;
}
internal static string Csv(DataTable table, bool withHead)
{
if (table == null) return null;
var columns = table.Columns.Count;
if (columns < 1) return "";
var sb = new StringBuilder();
if (withHead)
{
for (var c = 0; c < columns; c++)
{
var v = table.Columns[c].ColumnName;
CsvCell(sb, c, v);
}
}
var rows = table.Rows.Count;
for (var r = 0; r < rows; r++)
{
var row = table.Rows[r];
if (withHead || r > 0) sb.Append("\r\n");
for (var c = 0; c < columns; c++) CsvCell(sb, c, row[c]);
}
return sb.ToString();
}
private static void CsvCell(StringBuilder sb, int c, object v)
{
if (c > 0) sb.Append(",");
if (v == null || v.Equals(DBNull.Value)) return;
if (v is bool @bool)
{
sb.Append(@bool ? "TRUE" : "FALSE");
return;
}
if (v is DateTime @datetime)
{
sb.Append(@datetime.Lucid());
return;
}
if (v is byte || v is sbyte || v is short || v is ushort || v is int || v is uint || v is long || v is ulong || v is float || v is double || v is decimal)
{
sb.Append(v.ToString());
return;
}
if (v is char)
{
sb.Append((char)v);
return;
}
var s = (v is string @string) ? @string : v.ToString();
var length = s.Length;
if (length < 1) return;
var quote = false;
var comma = false;
var newline = false;
for (var i = 0; i < length; i++)
{
var @char = s[i];
if (@char == '\"') quote = true;
else if (@char == ',') comma = true;
else if (@char == '\r') newline = false;
else if (@char == '\n') newline = false;
}
if (quote || comma || newline)
{
sb.Append("\"");
s = s.Replace("\"", "\"\"");
sb.Append(s);
sb.Append("\"");
}
else sb.Append(s);
}
#endregion
}
}