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.
1197 lines
51 KiB
1197 lines
51 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
|
|
namespace Apewer.Source
|
|
{
|
|
|
|
/// <summary>ORM 帮助程序。</summary>
|
|
public static class SourceUtility
|
|
{
|
|
|
|
#region ORM
|
|
|
|
/// <summary>读取所有行,生成列表。</summary>
|
|
public static T[] Fill<T>(this IQuery query) where T : class, new()
|
|
{
|
|
var objects = Fill(query, typeof(T));
|
|
var array = CollectionUtility.As<object, T>(objects);
|
|
return array;
|
|
}
|
|
|
|
/// <summary>读取所有行填充到 T,组成 T[]。</summary>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="ArgumentException"></exception>
|
|
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);
|
|
}
|
|
|
|
/// <summary>将 Query 的行,填充到模型实体。</summary>
|
|
/// <remarks>填充失败时返回 NULL 值。</remarks>
|
|
/// <exception cref="Exception"></exception>
|
|
public static object FillRow(IQuery query, int rowIndex, Type model, TableStructure structure) => FillRow(query?.Table, rowIndex, model, structure);
|
|
|
|
/// <summary>将 Query 的行,填充到模型实体。</summary>
|
|
/// <remarks>填充失败时返回 NULL 值。</remarks>
|
|
/// <exception cref="Exception"></exception>
|
|
public static object FillRow(DataTable table, int rowIndex, Type model, TableStructure structure)
|
|
{
|
|
// 检查参数。
|
|
if (table == null || model == null || structure == null) return null;
|
|
if (rowIndex < 0 || rowIndex >= table.Rows.Count) 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 = table.Rows[r][field];
|
|
if (value != null && value.Equals(DBNull.Value)) value = null;
|
|
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[] { NumberUtility.Boolean(value) });
|
|
else if (pt.Equals(typeof(byte))) setter.Invoke(record, new object[] { NumberUtility.Byte(value) });
|
|
else if (pt.Equals(typeof(sbyte))) setter.Invoke(record, new object[] { NumberUtility.SByte(value) });
|
|
else if (pt.Equals(typeof(short))) setter.Invoke(record, new object[] { NumberUtility.Int16(value) });
|
|
else if (pt.Equals(typeof(ushort))) setter.Invoke(record, new object[] { NumberUtility.UInt16(value) });
|
|
else if (pt.Equals(typeof(int))) setter.Invoke(record, new object[] { NumberUtility.Int32(value) });
|
|
else if (pt.Equals(typeof(uint))) setter.Invoke(record, new object[] { NumberUtility.UInt32(value) });
|
|
else if (pt.Equals(typeof(long))) setter.Invoke(record, new object[] { NumberUtility.Int64(value) });
|
|
else if (pt.Equals(typeof(ulong))) setter.Invoke(record, new object[] { NumberUtility.UInt64(value) });
|
|
else if (pt.Equals(typeof(float))) setter.Invoke(record, new object[] { NumberUtility.Single(value) });
|
|
else if (pt.Equals(typeof(double))) setter.Invoke(record, new object[] { NumberUtility.Double(value) });
|
|
else if (pt.Equals(typeof(decimal))) setter.Invoke(record, new object[] { NumberUtility.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>(NumberUtility.Boolean(value)) });
|
|
else if (pt.Equals(typeof(Nullable<byte>))) setter.Invoke(record, new object[] { new Nullable<byte>(NumberUtility.Byte(value)) });
|
|
else if (pt.Equals(typeof(Nullable<sbyte>))) setter.Invoke(record, new object[] { new Nullable<sbyte>(NumberUtility.SByte(value)) });
|
|
else if (pt.Equals(typeof(Nullable<short>))) setter.Invoke(record, new object[] { new Nullable<short>(NumberUtility.Int16(value)) });
|
|
else if (pt.Equals(typeof(Nullable<ushort>))) setter.Invoke(record, new object[] { new Nullable<int>(NumberUtility.UInt16(value)) });
|
|
else if (pt.Equals(typeof(Nullable<int>))) setter.Invoke(record, new object[] { new Nullable<int>(NumberUtility.Int32(value)) });
|
|
else if (pt.Equals(typeof(Nullable<uint>))) setter.Invoke(record, new object[] { new Nullable<uint>(NumberUtility.UInt32(value)) });
|
|
else if (pt.Equals(typeof(Nullable<long>))) setter.Invoke(record, new object[] { new Nullable<long>(NumberUtility.Int64(value)) });
|
|
else if (pt.Equals(typeof(Nullable<ulong>))) setter.Invoke(record, new object[] { new Nullable<ulong>(NumberUtility.UInt64(value)) });
|
|
else if (pt.Equals(typeof(Nullable<float>))) setter.Invoke(record, new object[] { new Nullable<float>(NumberUtility.Single(value)) });
|
|
else if (pt.Equals(typeof(Nullable<double>))) setter.Invoke(record, new object[] { new Nullable<double>(NumberUtility.Double(value)) });
|
|
else if (pt.Equals(typeof(Nullable<decimal>))) setter.Invoke(record, new object[] { new Nullable<decimal>(NumberUtility.Decimal(value)) });
|
|
#endif
|
|
|
|
else
|
|
{
|
|
try
|
|
{
|
|
setter.Invoke(record, new object[] { value });
|
|
return true;
|
|
}
|
|
catch { }
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>解析 DataTable,填充没行到到指定的类型中,形成数组。</summary>
|
|
/// <param name="table">将要读取的表。</param>
|
|
/// <param name="compatible">当类型不同时,尝试转换以兼容。</param>
|
|
/// <returns>由指定类型组成的数组。</returns>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="ArgumentException"></exception>
|
|
public static T[] Fill<T>(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;
|
|
}
|
|
|
|
/// <summary>解析 DataTable,填充没行到到指定的类型中,形成数组。</summary>
|
|
/// <param name="table">将要读取的表。</param>
|
|
/// <param name="model">要填充的目标类型,必须是可实例化的引用类型。</param>
|
|
/// <param name="compatible">当类型不同时,尝试转换以兼容。</param>
|
|
/// <returns>由指定类型组成的数组。</returns>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="ArgumentException"></exception>
|
|
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 cas = ts.Fillable;
|
|
var dc = 0;
|
|
var dfs = new string[cas.Length];
|
|
var dts = new ColumnAttribute[cas.Length];
|
|
for (var i = 0; i < cas.Length; i++)
|
|
{
|
|
var ca = cas[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, NumberUtility.Boolean(value), null);
|
|
else if (pt.Equals(typeof(byte))) prop.SetValue(record, NumberUtility.Byte(value), null);
|
|
else if (pt.Equals(typeof(sbyte))) prop.SetValue(record, NumberUtility.SByte(value), null);
|
|
else if (pt.Equals(typeof(short))) prop.SetValue(record, NumberUtility.Int16(value), null);
|
|
else if (pt.Equals(typeof(ushort))) prop.SetValue(record, NumberUtility.UInt16(value), null);
|
|
else if (pt.Equals(typeof(int))) prop.SetValue(record, NumberUtility.Int32(value), null);
|
|
else if (pt.Equals(typeof(uint))) prop.SetValue(record, NumberUtility.UInt32(value), null);
|
|
else if (pt.Equals(typeof(long))) prop.SetValue(record, NumberUtility.Int64(value), null);
|
|
else if (pt.Equals(typeof(ulong))) prop.SetValue(record, NumberUtility.UInt64(value), null);
|
|
else if (pt.Equals(typeof(float))) prop.SetValue(record, NumberUtility.Single(value), null);
|
|
else if (pt.Equals(typeof(double))) prop.SetValue(record, NumberUtility.Double(value), null);
|
|
else if (pt.Equals(typeof(decimal))) prop.SetValue(record, NumberUtility.Decimal(value), null);
|
|
|
|
else if (pt.Equals(typeof(Nullable<DateTime>))) prop.SetValue(record, new Nullable<DateTime>((DateTime)value), null);
|
|
else if (pt.Equals(typeof(Nullable<bool>))) prop.SetValue(record, new Nullable<bool>(NumberUtility.Boolean(value)), null);
|
|
else if (pt.Equals(typeof(Nullable<byte>))) prop.SetValue(record, new Nullable<byte>(NumberUtility.Byte(value)), null);
|
|
else if (pt.Equals(typeof(Nullable<sbyte>))) prop.SetValue(record, new Nullable<sbyte>(NumberUtility.SByte(value)), null);
|
|
else if (pt.Equals(typeof(Nullable<short>))) prop.SetValue(record, new Nullable<short>(NumberUtility.Int16(value)), null);
|
|
else if (pt.Equals(typeof(Nullable<ushort>))) prop.SetValue(record, new Nullable<int>(NumberUtility.UInt16(value)), null);
|
|
else if (pt.Equals(typeof(Nullable<int>))) prop.SetValue(record, new Nullable<int>(NumberUtility.Int32(value)), null);
|
|
else if (pt.Equals(typeof(Nullable<uint>))) prop.SetValue(record, new Nullable<uint>(NumberUtility.UInt32(value)), null);
|
|
else if (pt.Equals(typeof(Nullable<long>))) prop.SetValue(record, new Nullable<long>(NumberUtility.Int64(value)), null);
|
|
else if (pt.Equals(typeof(Nullable<ulong>))) prop.SetValue(record, new Nullable<ulong>(NumberUtility.UInt64(value)), null);
|
|
else if (pt.Equals(typeof(Nullable<float>))) prop.SetValue(record, new Nullable<float>(NumberUtility.Single(value)), null);
|
|
else if (pt.Equals(typeof(Nullable<double>))) prop.SetValue(record, new Nullable<double>(NumberUtility.Double(value)), null);
|
|
else if (pt.Equals(typeof(Nullable<decimal>))) prop.SetValue(record, new Nullable<decimal>(NumberUtility.Decimal(value)), null);
|
|
|
|
else
|
|
{
|
|
try
|
|
{
|
|
prop.SetValue(record, value, null);
|
|
return true;
|
|
}
|
|
catch { }
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#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 Query
|
|
|
|
/// <summary>简单查询:取结果中第 0 列所有单元格的文本形式,可指定查询后关闭服务器连接,返回结果中不包含无效文本。</summary>
|
|
/// <param name="source">数据库客户端。</param>
|
|
/// <param name="sql">用于查询的 SQL 语句。</param>
|
|
/// <exception cref="SqlException"></exception>
|
|
public static string[] Column(this IDbAdo source, string sql)
|
|
{
|
|
if (source == null) return new string[0];
|
|
|
|
var pool = null as string[];
|
|
var rows = 0;
|
|
var count = 0;
|
|
using (var query = source.Query(sql))
|
|
{
|
|
if (!query.Success) throw new SqlException(query, sql);
|
|
|
|
rows = query.Rows;
|
|
if (rows < 1) return new string[0];
|
|
|
|
pool = new string[rows];
|
|
for (int i = 0; i < rows; i++)
|
|
{
|
|
var cell = TextUtility.Trim(query.Text(i, 0));
|
|
if (string.IsNullOrEmpty(cell)) continue;
|
|
|
|
pool[count] = cell;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if (count < 1) return new string[0];
|
|
if (count == rows) return pool;
|
|
|
|
var array = new string[count];
|
|
Array.Copy(pool, 0, array, 0, count);
|
|
return array;
|
|
}
|
|
|
|
/// <summary>简单查询:取结果中第 0 行、第 0 列单元格中的文本,可指定查询后关闭服务器连接。</summary>
|
|
/// <param name="dbClient">数据库客户端。</param>
|
|
/// <param name="sql">用于查询的 SQL 语句。</param>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="SqlException"></exception>
|
|
public static string Cell(this IDbAdo dbClient, string sql)
|
|
{
|
|
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
|
|
if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
|
|
|
|
using (var query = dbClient.Query(sql))
|
|
{
|
|
if (!query.Success) throw new SqlException(query, sql);
|
|
var value = TextUtility.Trim(query.Text(0, 0));
|
|
return value;
|
|
}
|
|
}
|
|
|
|
/// <summary>查询。</summary>
|
|
/// <param name="dbClient">数据库连接。</param>
|
|
/// <param name="sql">SQL 语句。</param>
|
|
/// <param name="parameters">SQL 参数。</param>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
public static IQuery Query(this IDbAdo dbClient, string sql, IEnumerable<KeyValuePair<string, object>> parameters)
|
|
{
|
|
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
|
|
if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
|
|
|
|
var ps = Parameters(dbClient, sql, parameters);
|
|
return dbClient.Query(sql, ps);
|
|
}
|
|
|
|
/// <summary>查询。</summary>
|
|
/// <param name="dbClient">数据库连接。</param>
|
|
/// <param name="sql">SQL 语句。</param>
|
|
/// <param name="parameters">参数容器,每个属性表示一个 SQL 参数。此方法将会自动补足参数名称的 @ 前缀。</param>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
public static IQuery Query(this IDbAdo dbClient, string sql, object parameters = null)
|
|
{
|
|
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
|
|
if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
|
|
|
|
if (parameters is IEnumerable<KeyValuePair<string, object>> kvps)
|
|
{
|
|
var ps = Parameters(dbClient, sql, kvps);
|
|
return dbClient.Query(sql, ps);
|
|
}
|
|
|
|
{
|
|
var ps = ParametersByProperites(dbClient, sql, parameters);
|
|
return dbClient.Query(sql, ps);
|
|
}
|
|
}
|
|
|
|
/// <summary>执行 SELECT 语句,获取查询结果。</summary>
|
|
/// <param name="connection">数据库连接。</param>
|
|
/// <param name="transaction">事务。</param>
|
|
/// <param name="sql">SQL 语句。</param>
|
|
/// <param name="parameters">参数。</param>
|
|
/// <param name="timeout">超时秒数。</param>
|
|
/// <returns>查询结果。</returns>
|
|
public static DataTable Query(this IDbConnection connection, IDbTransaction transaction, string sql, IEnumerable<IDbDataParameter> parameters = null, int timeout = 3600)
|
|
{
|
|
if (connection == null) throw new ArgumentNullException(nameof(connection));
|
|
if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql));
|
|
|
|
if (connection.State != ConnectionState.Open) connection.Open();
|
|
using (var command = connection.CreateCommand())
|
|
{
|
|
if (transaction != null) command.Transaction = transaction;
|
|
if (timeout > 0) command.CommandTimeout = timeout;
|
|
command.CommandText = sql;
|
|
if (parameters != null)
|
|
{
|
|
foreach (var parameter in parameters)
|
|
{
|
|
if (parameter == null) continue;
|
|
command.Parameters.Add(parameter);
|
|
}
|
|
}
|
|
|
|
using (var reader = command.ExecuteReader())
|
|
{
|
|
var table = new DataTable();
|
|
table.Load(reader);
|
|
return table;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Execute
|
|
|
|
/// <summary>执行 SQL 语句,并加入参数。</summary>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
public static IExecute Execute(this IDbAdo dbClient, string sql, IEnumerable<KeyValuePair<string, object>> parameters, bool autoTransaction = false)
|
|
{
|
|
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
|
|
if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
|
|
|
|
var ps = Parameters(dbClient, sql, parameters);
|
|
return dbClient.Execute(sql, ps, autoTransaction);
|
|
}
|
|
|
|
/// <summary>执行 SQL 语句,并加入参数。</summary>
|
|
/// <param name="dbClient">数据库连接。</param>
|
|
/// <param name="sql">SQL 语句。</param>
|
|
/// <param name="parameters">参数容器,每个属性表示一个 SQL 参数。此方法将会自动补足参数名称的 @ 前缀。</param>
|
|
/// <param name="autoTransaction">自动使用事务。</param>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
public static IExecute Execute(this IDbAdo dbClient, string sql, object parameters = null, bool autoTransaction = false)
|
|
{
|
|
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
|
|
if (sql.IsEmpty()) throw new ArgumentNullException(nameof(sql));
|
|
|
|
if (parameters is IEnumerable<KeyValuePair<string, object>> kvps)
|
|
{
|
|
var ps = Parameters(dbClient, sql, kvps);
|
|
return dbClient.Execute(sql, ps, autoTransaction);
|
|
}
|
|
|
|
{
|
|
var ps = ParametersByProperites(dbClient, sql, parameters);
|
|
return dbClient.Execute(sql, ps, autoTransaction);
|
|
}
|
|
}
|
|
|
|
/// <summary>执行 SQL 语句,获取影响的行数。</summary>
|
|
/// <param name="connection">数据库连接。</param>
|
|
/// <param name="transaction">事务。</param>
|
|
/// <param name="sql">SQL 语句。</param>
|
|
/// <param name="parameters">参数。</param>
|
|
/// <param name="timeout">超时秒数。</param>
|
|
/// <returns>行数。</returns>
|
|
public static int Execute(this IDbConnection connection, IDbTransaction transaction, string sql, IEnumerable<IDbDataParameter> parameters = null, int timeout = 3600)
|
|
{
|
|
if (connection == null) throw new ArgumentNullException(nameof(connection));
|
|
if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql));
|
|
|
|
if (connection.State != ConnectionState.Open) connection.Open();
|
|
using (var command = connection.CreateCommand())
|
|
{
|
|
if (transaction != null) command.Transaction = transaction;
|
|
if (timeout > 0) command.CommandTimeout = timeout;
|
|
command.CommandText = sql;
|
|
if (parameters != null)
|
|
{
|
|
foreach (var parameter in parameters)
|
|
{
|
|
if (parameter == null) continue;
|
|
command.Parameters.Add(parameter);
|
|
}
|
|
}
|
|
|
|
var rows = command.ExecuteNonQuery();
|
|
return rows;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Transaction
|
|
|
|
/// <summary>启动事务,执行指定的过程并在完成后提交事务。若过程被异常打断,则回滚事务。</summary>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="SqlException"></exception>
|
|
public static void InTransaction(this IDbAdo source, Action action)
|
|
{
|
|
// 检查参数。
|
|
if (source == null) throw new ArgumentNullException(nameof(source), "数据源无效。");
|
|
if (action == null) throw new ArgumentNullException(nameof(action), "没有指定要在事物中执行的程序。");
|
|
|
|
InTransaction<object>(source, () =>
|
|
{
|
|
action.Invoke();
|
|
return null;
|
|
});
|
|
}
|
|
|
|
/// <summary>启动事务,执行指定的过程并在完成后提交事务。若过程被异常打断,则回滚事务。</summary>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="SqlException"></exception>
|
|
public static T InTransaction<T>(this IDbAdo source, Func<T> func)
|
|
{
|
|
// 检查参数。
|
|
if (source == null) throw new ArgumentNullException(nameof(source), "数据源无效。");
|
|
if (func == null) throw new ArgumentNullException(nameof(func), "没有指定要在事物中执行的程序。");
|
|
|
|
// 已经存在事务。
|
|
if (source.Transaction != null) return func.Invoke();
|
|
|
|
// 启动事务。
|
|
var begin = source.Begin();
|
|
if (begin.NotEmpty()) throw new SqlException("无法启动事务:" + begin);
|
|
|
|
var result = default(T);
|
|
var success = false;
|
|
try
|
|
{
|
|
// 在事务内运行。
|
|
result = func.Invoke();
|
|
success = true;
|
|
}
|
|
finally
|
|
{
|
|
if (success)
|
|
{
|
|
// 执行成功,提交事务。
|
|
var commit = source.Commit();
|
|
if (!string.IsNullOrEmpty(commit)) throw new SqlException(commit);
|
|
}
|
|
else
|
|
{
|
|
// 执行失败,回滚事务。
|
|
try { source.Rollback(); } catch { }
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Parameter
|
|
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
static List<IDataParameter> ParametersByProperites(IDbAdo dbClient, string sql, object parameters)
|
|
{
|
|
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
|
|
if (parameters == null) return null;
|
|
|
|
var lsql = sql.Lower();
|
|
|
|
var type = parameters.GetType();
|
|
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
|
|
var count = properties.Length;
|
|
var dict = new Dictionary<string, object>(count);
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var property = properties[i];
|
|
|
|
// 属性必须能够获取值。
|
|
var getter = property.GetGetMethod();
|
|
if (getter == null) continue;
|
|
|
|
// 属性值必须有效。
|
|
var name = property.Name;
|
|
if (name.IsEmpty()) continue;
|
|
|
|
// 属性不可重复。
|
|
if (!name.StartsWith("@")) name = "@" + name;
|
|
if (dict.ContainsKey(name)) continue;
|
|
|
|
// SQL 语句中必须包含此参数。
|
|
var lname = name.Lower();
|
|
if (!lsql.Contains(lname)) continue;
|
|
|
|
// 加入字典。
|
|
var value = getter.Invoke(parameters, null);
|
|
dict.Add(name, value);
|
|
}
|
|
if (dict.Count < 1) return null;
|
|
|
|
var ps = new List<IDataParameter>();
|
|
foreach (var kvp in dict)
|
|
{
|
|
var p = dbClient.Parameter(kvp.Key, kvp.Value);
|
|
ps.Add(p);
|
|
}
|
|
return ps;
|
|
}
|
|
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
static List<IDataParameter> Parameters(IDbAdo dbClient, string sql, IEnumerable<KeyValuePair<string, object>> parameters)
|
|
{
|
|
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
|
|
if (parameters == null) return null;
|
|
|
|
var lsql = sql.Lower();
|
|
var names = new List<string>(20);
|
|
var ps = new List<IDataParameter>(20);
|
|
foreach (var kvp in parameters)
|
|
{
|
|
var name = kvp.Key;
|
|
if (name.IsEmpty()) continue;
|
|
|
|
// 属性不可重复。
|
|
if (!name.StartsWith("@")) name = "@" + name;
|
|
if (names.Contains(name)) continue;
|
|
|
|
// SQL 语句中必须包含此参数。
|
|
var lname = name.Lower();
|
|
if (!lsql.Contains(lname)) continue;
|
|
|
|
var p = dbClient.Parameter(name, kvp.Value);
|
|
ps.Add(p);
|
|
names.Add(name);
|
|
}
|
|
return ps;
|
|
}
|
|
|
|
#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;
|
|
}
|
|
|
|
/// <summary>限定名称文本,只允许包含字母、数字和下划线。</summary>
|
|
public static string SafeName(this string name) => TextUtility.Restrict(name, "0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
|
|
|
|
#endregion
|
|
|
|
#region 数据模型 -> DataTable
|
|
|
|
/// <summary>将多个实体元素转换为 DataTable。</summary>
|
|
/// <typeparam name="T">实体元素的类型。</typeparam>
|
|
/// <param name="items">实体元素。</param>
|
|
/// <param name="tableName">设置 <see cref="DataTable"/> 的名称。</param>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
/// <exception cref="DuplicateNameException"></exception>
|
|
/// <exception cref="InvalidExpressionException"></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, 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<object>(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;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DataTable 序列化
|
|
|
|
/// <summary>转换 <see cref="System.Data.DataTable"/> 到 <see cref="ObjectSet{T}"/> 数组,每行记录为一个 ObjectSet 对象。</summary>
|
|
/// <returns>当参数 table 无效时返回 0 长度的 <see cref="ObjectSet{T}"/> 数组。</returns>
|
|
public 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<string, object>[rows];
|
|
for (var r = 0; r < table.Rows.Count; r++)
|
|
{
|
|
var dict = new Dictionary<string, object>(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;
|
|
}
|
|
|
|
/// <summary>转换为 Json 对象。</summary>
|
|
public static Json ToJson(this DataTable table, Func<DateTime, string> dateTimeFormatter = null)
|
|
{
|
|
if (table == null) return null;
|
|
|
|
var columns = ToJson(table.Columns);
|
|
var rows = ToJson(table.Rows, dateTimeFormatter);
|
|
|
|
var jsonObject = Json.NewObject();
|
|
jsonObject.SetProperty("columns", columns);
|
|
jsonObject.SetProperty("rows", rows);
|
|
return jsonObject;
|
|
}
|
|
|
|
/// <summary>转换为 Json 对象。</summary>
|
|
public static Json ToJson(this DataColumnCollection columns)
|
|
{
|
|
if (columns == null) return null;
|
|
|
|
var json = Json.NewArray();
|
|
var count = columns.Count;
|
|
for (var c = 0; c < count; c++)
|
|
{
|
|
var dc = columns[c];
|
|
var column = Json.NewObject();
|
|
column.SetProperty("name", dc.ColumnName);
|
|
column.SetProperty("type", dc.DataType.FullName);
|
|
json.AddItem(column);
|
|
}
|
|
return json;
|
|
}
|
|
|
|
/// <summary>转换为 Json 对象。</summary>
|
|
public static Json ToJson(this DataRowCollection rows, Func<DateTime, object> dateTimeFormatter = null)
|
|
{
|
|
if (rows == null) return null;
|
|
|
|
var json = Json.NewArray();
|
|
var count = rows.Count;
|
|
for (var r = 0; r < count; r++)
|
|
{
|
|
json.AddItem(ToJson(rows[r], dateTimeFormatter));
|
|
}
|
|
return json;
|
|
}
|
|
|
|
/// <summary>转换为 Json 对象。</summary>
|
|
public static Json ToJson(this DataRow row, Func<DateTime, object> dateTimeFormatter = null)
|
|
{
|
|
if (row == null) return null;
|
|
var cells = row.ItemArray;
|
|
var count = cells.Length;
|
|
|
|
var json = Json.NewArray();
|
|
for (var c = 0; c < count; c++)
|
|
{
|
|
var value = cells[c];
|
|
if (value == null || value.Equals(DBNull.Value))
|
|
{
|
|
json.AddItem();
|
|
continue;
|
|
}
|
|
|
|
if (value is DateTime vDateTime)
|
|
{
|
|
if (dateTimeFormatter == null)
|
|
{
|
|
json.AddItem(Json.SerializeDateTime(vDateTime));
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
value = dateTimeFormatter.Invoke(vDateTime);
|
|
if (value == null || value.Equals(DBNull.Value))
|
|
{
|
|
json.AddItem();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (value is string @string) json.AddItem(@string);
|
|
else if (value is byte @byte) json.AddItem(@byte);
|
|
else if (value is short @short) json.AddItem(@short);
|
|
else if (value is int @int) json.AddItem(@int);
|
|
else if (value is long @long) json.AddItem(@long);
|
|
else if (value is float @float) json.AddItem(@float);
|
|
else if (value is double @double) json.AddItem(@double);
|
|
else if (value is decimal @decimal) json.AddItem(@decimal);
|
|
else if (value is bool @bool) json.AddItem(@bool);
|
|
else if (value is byte[] bytes) json.AddItem(bytes.Base64());
|
|
else json.AddItem(TextUtility.Text(value));
|
|
}
|
|
return json;
|
|
}
|
|
|
|
/// <summary>转换 <see cref="DataTable"/> 为 CSV 文本,不存在表时返回 NULL 值。可指定是否包含表头。</summary>
|
|
public static string Csv(DataTable table, bool withHead = false)
|
|
{
|
|
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
|
|
|
|
#region DataTable 快捷操作
|
|
|
|
/// <summary>获取默认表中指定单元格的内容。</summary>
|
|
/// <param name="table">数据表。</param>
|
|
/// <param name="rowIndex">行索引,从 0 开始。</param>
|
|
/// <param name="columnIndex">列索引,从 0 开始。</param>
|
|
public static object Value(this DataTable table, int rowIndex, int columnIndex)
|
|
{
|
|
if (table != null)
|
|
{
|
|
if (rowIndex >= 0 && rowIndex < table.Rows.Count)
|
|
{
|
|
if (columnIndex >= 0 && columnIndex < table.Columns.Count)
|
|
{
|
|
var value = table.Rows[rowIndex][columnIndex];
|
|
if (value == null || value.Equals(DBNull.Value)) return null;
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>获取默认表中指定单元的内容。</summary>
|
|
/// <param name="table">数据表。</param>
|
|
/// <param name="rowIndex">行索引,从 0 开始。</param>
|
|
/// <param name="columnName">列名称/字段名称,此名称不区分大小写。</param>
|
|
public static object Value(this DataTable table, int rowIndex, string columnName)
|
|
{
|
|
if (table != null && !string.IsNullOrEmpty(columnName))
|
|
{
|
|
if ((rowIndex < table.Rows.Count) && (rowIndex >= 0))
|
|
{
|
|
try
|
|
{
|
|
var value = table.Rows[rowIndex][columnName];
|
|
if (value == null || value.Equals(DBNull.Value)) return null;
|
|
return value;
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
|
|
public static Class<DateTime> DateTime(this DataTable table, int row, int column) => table == null ? null : ClockUtility.DateTime(table.Value(row, column));
|
|
|
|
/// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
|
|
public static Class<DateTime> DateTime(this DataTable table, int row, string column) => table == null ? null : ClockUtility.DateTime(table.Value(row, column));
|
|
|
|
/// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
|
|
public static Int32 Int32(this DataTable table, int row, int column) => table == null ? 0 : NumberUtility.Int32(table.Value(row, column));
|
|
|
|
/// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
|
|
public static Int32 Int32(this DataTable table, int row, string column) => table == null ? 0 : NumberUtility.Int32(table.Value(row, column));
|
|
|
|
/// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
|
|
public static Int64 Int64(this DataTable table, int row, int column) => table == null ? 0L : NumberUtility.Int64(table.Value(row, column));
|
|
|
|
/// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
|
|
public static Int64 Int64(this DataTable table, int row, string column) => table == null ? 0L : NumberUtility.Int64(table.Value(row, column));
|
|
|
|
/// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
|
|
public static Decimal Decimal(this DataTable table, int row, int column) => table == null ? 0M : NumberUtility.Decimal(table.Value(row, column));
|
|
|
|
/// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
|
|
public static Decimal Decimal(this DataTable table, int row, string column) => table == null ? 0M : NumberUtility.Decimal(table.Value(row, column));
|
|
|
|
/// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>>
|
|
public static Double Double(this DataTable table, int row, int column) => table == null ? 0D : NumberUtility.Double(table.Value(row, column));
|
|
|
|
/// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>>
|
|
public static Double Double(this DataTable table, int row, string column) => table == null ? 0D : NumberUtility.Double(table.Value(row, column));
|
|
|
|
/// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
|
|
public static string Text(this DataTable table, int row, int column) => table == null ? null : TextUtility.Text(table.Value(row, column));
|
|
|
|
/// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
|
|
public static string Text(this DataTable table, int row, string column) => table == null ? null : TextUtility.Text(table.Value(row, column));
|
|
|
|
#endregion
|
|
|
|
#region Dynamic
|
|
|
|
#if NET40_OR_GREATER
|
|
|
|
/// <summary>转换 ObjectSet 数组为 dynamic 数组。</summary>
|
|
public static dynamic[] Dynamic(this ObjectSet[] oss)
|
|
{
|
|
if (oss == null) return new dynamic[0];
|
|
|
|
var eos = oss.Expando();
|
|
var ds = new dynamic[eos.Length];
|
|
eos.CopyTo(ds, 0);
|
|
return ds;
|
|
}
|
|
|
|
#endif
|
|
|
|
#endregion
|
|
|
|
#region 表达式计算
|
|
|
|
/// <summary>计算文本表达式。</summary>
|
|
public static object Compute(string expression)
|
|
{
|
|
using (var table = new DataTable())
|
|
{
|
|
var result = table.Compute(expression, null);
|
|
if (result.IsNull()) return null;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|
|
|