Browse Source

Apewer-6.4.2

dev
王厅 4 years ago
parent
commit
2571159fe4
  1. 24
      Apewer.Source/Source/MySql.cs
  2. 18
      Apewer.Source/Source/SqlClient.cs
  3. 18
      Apewer.Source/Source/Sqlite.cs
  4. 10
      Apewer/ArrayBuilder.cs
  5. 100
      Apewer/BytesUtility.cs
  6. 2
      Apewer/Externals/Compression/Fixed.cs
  7. 17
      Apewer/IndependentAttribute.cs
  8. 21
      Apewer/NetworkUtility.cs
  9. 29
      Apewer/NumberUtility.cs
  10. 20
      Apewer/RuntimeUtility.cs
  11. 39
      Apewer/Source/Extensions.cs
  12. 10
      Apewer/Source/IDbClientOrm.cs
  13. 132
      Apewer/Source/OrmHelper.cs
  14. 28
      Apewer/Source/Record.cs
  15. 5
      Apewer/Source/TableStructure.cs
  16. 2
      Apewer/StorageUtility.cs
  17. 104
      Apewer/TextUtility.cs
  18. 44
      Apewer/Web/StaticController.cs
  19. 3
      Apewer/_Common.props
  20. 41
      Apewer/_Extensions.cs
  21. 3
      ChangeLog.md

24
Apewer.Source/Source/MySql.cs

@ -10,6 +10,8 @@ using System.Net;
using System.Text;
using System.Transactions;
using static Apewer.Source.OrmHelper;
namespace Apewer.Source
{
@ -468,16 +470,16 @@ namespace Apewer.Source
public string Initialize(Type model) => Initialize(model, out string sql);
/// <summary></summary>
public string Initialize<T>() where T : class, IRecord, new() => Initialize(typeof(T));
public string Initialize<T>() where T : class, new() => Initialize(typeof(T));
/// <summary></summary>
public string Initialize(Record model) => (model == null) ? "参数无效。" : Initialize(model.GetType());
/// <summary>插入记录。返回错误信息。</summary>
public string Insert(IRecord record)
public string Insert(object record)
{
if (record == null) return "参数无效。";
record.FixProperties();
OrmHelper.FixProperties(record);
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
@ -494,8 +496,8 @@ namespace Apewer.Source
public string Update(IRecord record)
{
if (record == null) return "参数无效。";
record.FixProperties();
record.SetUpdated();
FixProperties(record);
SetUpdated(record);
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
@ -509,13 +511,13 @@ namespace Apewer.Source
}
/// <summary></summary>
public Result<IRecord[]> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
public Result<object[]> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
/// <summary></summary>
public Result<T[]> Query<T>(string sql) where T : class, IRecord, new() => OrmHelper.Query<T>(this, sql);
public Result<T[]> Query<T>(string sql) where T : class, new() => OrmHelper.Query<T>(this, sql);
/// <summary>获取所有记录。Flag 为 0 时将忽略 Flag 条件。</summary>
public Result<IRecord[]> Query(Type model, long flag = 0) => OrmHelper.Query(this, model, (tn) =>
public Result<object[]> Query(Type model, long flag = 0) => OrmHelper.Query(this, model, (tn) =>
{
if (flag == 0) return $"select * from `{tn}`; ";
return $"select * from `{tn}` where `_flag`={flag}; ";
@ -532,10 +534,10 @@ namespace Apewer.Source
/// <param name="model">填充的记录模型。</param>
/// <param name="skip">要跳过的记录数,可用最小值为 0。</param>
/// <param name="count">要获取的记录数,可用最小值为 1。</param>
public Result<IRecord[]> Query(Type model, int skip, int count)
public Result<object[]> Query(Type model, int skip, int count)
{
if (skip < 0) return new Result<IRecord[]>("参数 skip 超出了范围。");
if (count < 1) return new Result<IRecord[]>("参数 count 超出了范围。");
if (skip < 0) return new Result<object[]>("参数 skip 超出了范围。");
if (count < 1) return new Result<object[]>("参数 count 超出了范围。");
return OrmHelper.Query(this, model, (tn) => $"select * from `{tn}` limit {skip}, {count}; ");
}

18
Apewer.Source/Source/SqlClient.cs

@ -10,6 +10,8 @@ using System.Data.SqlClient;
using System.Net;
using System.Text;
using static Apewer.Source.OrmHelper;
#if NETFRAMEWORK
using System.Data.Sql;
#else
@ -356,7 +358,7 @@ namespace Apewer.Source
}
/// <summary>创建表,当表不存在时创建表,当现存表中缺少模型中属性对应的列时增加列。成功时返回空字符串,发生异常时返回异常信息。</summary>
public string Initialize<T>() where T : class, IRecord, new() => Initialize(typeof(T));
public string Initialize<T>() where T : class, new() => Initialize(typeof(T));
/// <summary>创建表,当表不存在时创建表,当现存表中缺少模型中属性对应的列时增加列。成功时返回空字符串,发生异常时返回异常信息。</summary>
public string Initialize(Type model)
@ -440,10 +442,10 @@ namespace Apewer.Source
}
/// <summary>插入记录。返回错误信息。</summary>
public string Insert(IRecord record)
public string Insert(object record)
{
if (record == null) return "参数无效。";
record.FixProperties();
FixProperties(record);
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
@ -460,8 +462,8 @@ namespace Apewer.Source
public string Update(IRecord record)
{
if (record == null) return "参数无效。";
record.FixProperties();
record.SetUpdated();
FixProperties(record);
SetUpdated(record);
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
@ -475,13 +477,13 @@ namespace Apewer.Source
}
/// <summary>获取按指定语句查询到的所有记录。</summary>
public Result<IRecord[]> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
public Result<object[]> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
/// <summary>获取按指定语句查询到的所有记录。</summary>
public Result<T[]> Query<T>(string sql) where T : class, IRecord, new() => OrmHelper.Query<T>(this, sql);
public Result<T[]> Query<T>(string sql) where T : class, new() => OrmHelper.Query<T>(this, sql);
/// <summary>获取记录。</summary>
public Result<IRecord[]> Query(Type model, long flag = 0) => OrmHelper.Query(this, model, (tn) =>
public Result<object[]> Query(Type model, long flag = 0) => OrmHelper.Query(this, model, (tn) =>
{
if (flag == 0) return $"select * from [{tn}]; ";
return $"select * from [{tn}] where _flag={flag}; ";

18
Apewer.Source/Source/Sqlite.cs

@ -8,6 +8,8 @@ using System.Data.SQLite;
using System.Text;
//using Mono.Data.Sqlite;
using static Apewer.Source.OrmHelper;
namespace Apewer.Source
{
@ -363,7 +365,7 @@ namespace Apewer.Source
public string Initialize(Record model) => model == null ? "参数无效。" : Initialize(model.GetType());
/// <summary>创建表,不修改已存在表。成功时返回空字符串,发生异常时返回异常信息。</summary>
public string Initialize<T>() where T : class, IRecord, new() => Initialize(typeof(T));
public string Initialize<T>() where T : class, new() => Initialize(typeof(T));
/// <summary>创建表,不修改已存在表。当现存表中缺少模型中属性对应的列时增加列。成功时返回空字符串,发生异常时返回异常信息。</summary>
public string Initialize(Type model)
@ -412,10 +414,10 @@ namespace Apewer.Source
}
/// <summary>插入记录。返回错误信息。</summary>
public string Insert(IRecord record)
public string Insert(object record)
{
if (record == null) return "参数无效。";
record.FixProperties();
OrmHelper.FixProperties(record);
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
@ -432,8 +434,8 @@ namespace Apewer.Source
public string Update(IRecord record)
{
if (record == null) return "参数无效。";
record.FixProperties();
record.SetUpdated();
FixProperties(record);
SetUpdated(record);
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
@ -447,13 +449,13 @@ namespace Apewer.Source
}
/// <summary>获取按指定语句查询到的所有记录。</summary>
public Result<IRecord[]> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
public Result<object[]> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
/// <summary>获取按指定语句查询到的所有记录。</summary>
public Result<T[]> Query<T>(string sql) where T : class, IRecord, new() => OrmHelper.Query<T>(this, sql);
public Result<T[]> Query<T>(string sql) where T : class, new() => OrmHelper.Query<T>(this, sql);
/// <summary>查询多条记录。</summary>
public Result<IRecord[]> Query(Type model, long flag = 0) => OrmHelper.Query(this, model, (tn) =>
public Result<object[]> Query(Type model, long flag = 0) => OrmHelper.Query(this, model, (tn) =>
{
if (flag == 0) return $"select * from [{tn}]; ";
return $"select * from [{tn}] where _flag={flag}; ";

10
Apewer/ArrayBuilder.cs

@ -63,12 +63,12 @@ namespace Apewer
/// <summary>添加元素。</summary>
public void Add(T item)
{
if (_count >= _capacity)
if (_capacity - _count < 1)
{
var capacity = _capacity + _step;
var array = new T[capacity];
Array.Copy(_array, array, _count);
_array = array;
_capacity += _step;
var temp = new T[_capacity];
Array.Copy(_array, temp, _count);
_array = temp;
}
_array[_count] = item;
_count++;

100
Apewer/BytesUtility.cs

@ -257,106 +257,6 @@ namespace Apewer
catch { return Empty; }
}
/// <summary>检查字节数组包含 UTF-8 BOM 头。</summary>
public static bool ContainsBOM(byte[] bytes)
{
if (bytes == null) return false;
if (bytes.LongLength < 3L) return false;
return bytes[0L] == 0xEF && bytes[1L] == 0xBB && bytes[2L] == 0xBF;
}
/// <summary>检查字节数组是 UTF-8 文本。可指定检查的最大字节长度。</summary>
/// <param name="bytes">要检查的字节数组。</param>
/// <param name="offset">已检查的偏移量。</param>
/// <param name="checkLength">检查的最大字节长度。</param>
public static bool IsUTF8(byte[] bytes, Class<int> offset, int checkLength = 1048576)
{
return IsUTF8(bytes, offset, checkLength);
}
/// <summary>检查字节数组是 UTF-8 文本,默认最多检测 1MB 数据。</summary>
/// <param name="bytes">要检查的字节数组。</param>
/// <param name="checkLength">检查的最大字节长度,指定为 0 将不限制检查长度。</param>
/// <param name="offset">已检查的偏移量,用于调试。</param>
public static bool IsUTF8(byte[] bytes, int checkLength = 1048576, Class<int> offset = null)
{
// UTF8在Unicode的基础上制定了这样一套规则:
// 1.对于单字节字符,比特位的最高位为0;
// 2.对于多字节字符,第一个字节的比特位中,最高位有n个1,剩下的n - 1个字节的比特位中,最高位都是10。
// 好了,我知道你一定看不懂,那就先来看看下面例子后,再去看上面定义吧。
// 比如一个字符(“A”),它在UTF8中的编码为(用二进制表示):01000001。由于比特位的最高位是0,表示它是单字节,它只需要1个字节就可以表示。
// 再比如一个字符(“判”),它在UTF8中的编码为(用二进制表示):11100101 10001000 10100100。由于在第一个字节中,比特位最高位有3个1,说明这个字符总共需要3个字节来表示,且后3 - 1 = 2位字节中,比特位的最高位为10。
if (bytes == null) return false;
var length = bytes.LongLength;
// 检查 BOM 头。
if (ContainsBOM(bytes)) return true;
var hasOffset = offset != null;
var append = 0;
if (hasOffset) offset.Value = 0;
for (int i = 0; i < length; i++)
{
if (checkLength > 0 && i >= checkLength) break;
var b = bytes[i];
if (hasOffset) offset.Value = i;
// 追加字节最高位为 0。
if (append > 0)
{
if (b >> 6 != 2) return false;
append -= 1;
continue;
}
// ASCII 字符。
if (b < 128) continue;
// 2 字节 UTF-8。
if (b >= 0xC0 && b <= 0xDF)
{
append = 1;
continue;
}
// 3 字节 UTF-8 字符。
if (b >= 0xE0 && b <= 0xEF)
{
append = 2;
continue;
}
// 4 字节 UTF-8 字符。
if (b >= 0xF0 && b <= 0xF7)
{
append = 3;
continue;
}
// 5 字节 UTF-8 字符。
if (b >= 0xF8 && b <= 0xFB)
{
append = 4;
continue;
}
// 6 字节 UTF-8 字符。
if (b >= 0xFC && b <= 0xFD)
{
append = 5;
continue;
}
// 未知字节,非 UTF-8 定义。
return false;
}
return true;
}
#endregion
#region 压缩、解压。

2
Apewer/Externals/Compression/Fixed.cs

@ -15,7 +15,7 @@ namespace Externals.Compression
// 识别参考数据,判断解压的编码。
if (bytes != null || bytes.LongLength > 0L)
{
var isUTF8 = BytesUtility.IsUTF8(bytes);
var isUTF8 = TextUtility.IsUTF8(bytes);
if (isUTF8) return Encoding.UTF8;
// 返回默认编码。

17
Apewer/IndependentAttribute.cs

@ -7,6 +7,21 @@ namespace Apewer
/// <summary>无依赖特性。</summary>
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)]
public sealed class IndependentAttribute : Attribute { }
public sealed class IndependentAttribute : Attribute
{
string _remark = null;
/// <summary>无依赖特性。</summary>
public IndependentAttribute(string remark) => _remark = remark;
/// <summary>备注。</summary>
public string Remark
{
get { return _remark; }
set { if (!string.IsNullOrEmpty(value)) _remark = value; }
}
}
}

21
Apewer/NetworkUtility.cs

@ -142,26 +142,13 @@ namespace Apewer
/// <summary>获取本地计算机的计算机名。</summary>
public static string LocalHost
{
get
{
var hn = Dns.GetHostName();
return string.IsNullOrEmpty(hn) ? "" : hn;
}
get => Dns.GetHostName() ?? "";
}
/// <summary>本地计算机的 IP 地址。</summary>
public static List<string> LocalIP
/// <summary>本地计算机的所有 IP 地址。</summary>
public static IPAddress[] LocalIP
{
get
{
var list = new List<string>();
var he = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in he.AddressList)
{
list.Add(ip.ToString());
}
return list;
}
get => Dns.GetHostEntry(Dns.GetHostName()).AddressList;
}
/// <summary>判断 IPv4 地址格式是否正确。</summary>

29
Apewer/NumberUtility.cs

@ -256,8 +256,11 @@ namespace Apewer
return new string(chars, 0, output);
}
private static T GetNumber<T>(string text, Func<string, T> convert, Func<T, double, T> percent = null)
private static T GetNumber<T>(object @object, Func<string, T> convert, Func<T, double, T> percent = null)
{
if (@object == null) return default(T);
if (@object is T) return (T)@object;
var text = (@object is string) ? (string)@object : "";
var trim = Trim(text);
if (trim == null) return default;
@ -292,40 +295,40 @@ namespace Apewer
}
/// <summary>获取单精度浮点对象。</summary>
public static float Float(string text) => GetNumber(text, Convert.ToSingle, (v, d) => v / Convert.ToSingle(d));
public static float Float(object number) => GetNumber(number, Convert.ToSingle, (v, d) => v / Convert.ToSingle(d));
/// <summary>获取单精度浮点对象。</summary>
public static float Single(string text) => GetNumber(text, Convert.ToSingle, (v, d) => v / Convert.ToSingle(d));
public static float Single(object number) => GetNumber(number, Convert.ToSingle, (v, d) => v / Convert.ToSingle(d));
/// <summary>获取双精度浮点对象。</summary>
public static double Double(string text) => GetNumber(text, Convert.ToDouble, (v, d) => v / d);
public static double Double(object number) => GetNumber(number, Convert.ToDouble, (v, d) => v / d);
/// <summary>获取 Decimal 对象。</summary>
public static decimal Decimal(string text) => GetNumber(text, DecimalAsFloat, (v, d) => v / Convert.ToDecimal(d));
public static decimal Decimal(object number) => GetNumber(number, DecimalAsFloat, (v, d) => v / Convert.ToDecimal(d));
/// <summary>获取 Byte 对象。</summary>
public static byte Byte(string text) => GetNumber(text, Convert.ToByte);
public static byte Byte(object number) => GetNumber(number, Convert.ToByte);
/// <summary>获取 SByte 对象。</summary>
public static sbyte SByte(string text) => GetNumber(text, Convert.ToSByte);
public static sbyte SByte(object number) => GetNumber(number, Convert.ToSByte);
/// <summary>获取 Int16 对象。</summary>
public static short Int16(string text) => GetNumber(text, Convert.ToInt16);
public static short Int16(object number) => GetNumber(number, Convert.ToInt16);
/// <summary>获取 UInt16 对象。</summary>
public static ushort UInt16(string text) => GetNumber(text, Convert.ToUInt16);
public static ushort UInt16(object number) => GetNumber(number, Convert.ToUInt16);
/// <summary>获取 Int32 对象。</summary>
public static int Int32(string text) => GetNumber(text, Convert.ToInt32);
public static int Int32(object number) => GetNumber(number, Convert.ToInt32);
/// <summary>获取 UInt32 对象。</summary>
public static uint UInt32(string text) => GetNumber(text, Convert.ToUInt32);
public static uint UInt32(object number) => GetNumber(number, Convert.ToUInt32);
/// <summary>获取 Int64 对象。</summary>
public static long Int64(string text) => GetNumber(text, Convert.ToInt64);
public static long Int64(object number) => GetNumber(number, Convert.ToInt64);
/// <summary>获取 UInt64 对象。</summary>
public static ulong UInt64(string text) => GetNumber(text, Convert.ToUInt64);
public static ulong UInt64(object number) => GetNumber(number, Convert.ToUInt64);
#endregion

20
Apewer/RuntimeUtility.cs

@ -603,6 +603,26 @@ namespace Apewer
private static Class<string> _AppPath = null;
private static Class<string> _DataPath = null;
private static Class<bool> _InIIS = null;
/// <summary>当前应用程序由 IIS 托管。</summary>
public static bool InIIS()
{
#if NETFRAMEWORK
if (_InIIS != null) return _InIIS.Value;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
if (assembly.FullName.StartsWith("System.Web,"))
{
_InIIS = new Class<bool>(true);
return true;
}
}
_InIIS = new Class<bool>(false);
#endif
return false;
}
/// <summary>获取当前应用程序所在目录的路径。</summary>
/// <remarks>

39
Apewer/Source/Extensions.cs

@ -1,39 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Source
{
/// <summary></summary>
public static class Extensions
{
/// <summary>设置 Updated 属性。</summary>
/// <returns>TRUE:设置成功;FALSE:设置失败。</returns>
public static bool SetUpdated(this IRecord record)
{
if (record == null) return false;
var moment = record as IRecordMoment;
if (moment != null)
{
var now = ClockUtility.LucidNow;
moment.Updated = now;
return true;
}
var stamp = record as IRecordStamp;
if (stamp != null)
{
var utc = ClockUtility.UtcStamp;
stamp.Updated = utc;
return true;
}
return false;
}
}
}

10
Apewer/Source/IDbClientOrm.cs

@ -16,12 +16,12 @@ namespace Apewer.Source
/// <summary>初始化指定类型,以创建表或增加字段。</summary>
/// <returns>错误信息。当成功时候返回空字符串。</returns>
public string Initialize<T>() where T : class, IRecord, new();
public string Initialize<T>() where T : class, new();
/// <summary>插入记录。</summary>
/// <param name="record">要插入的记录实体。</param>
/// <returns>错误信息。当成功时候返回空字符串。</returns>
public string Insert(IRecord record);
public string Insert(object record);
/// <summary>更新记录。</summary>
/// <param name="record">要插入的记录实体。</param>
@ -51,16 +51,16 @@ namespace Apewer.Source
/// <summary>使用指定语句查询,获取查询结果。</summary>
/// <param name="model">目标记录的类型。</param>
/// <param name="sql">要执行的 SQL 语句。</param>
public Result<IRecord[]> Query(Type model, string sql);
public Result<object[]> Query(Type model, string sql);
/// <summary>使用指定语句查询,获取查询结果。</summary>
/// <param name="sql">要执行的 SQL 语句。</param>
public Result<T[]> Query<T>(string sql) where T : class, IRecord, new();
public Result<T[]> Query<T>(string sql) where T : class, new();
/// <summary>查询所有记录。</summary>
/// <param name="model">目标记录的类型。</param>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<IRecord[]> Query(Type model, long flag = 0);
public Result<object[]> Query(Type model, long flag = 0);
/// <summary>查询所有记录。</summary>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>

132
Apewer/Source/OrmHelper.cs

@ -13,37 +13,45 @@ namespace Apewer.Source
#region As
private static T[] As<T>(IRecord[] input) where T : IRecord
/// <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 T[count];
var output = new TDst[count];
for (var i = 0; i < count; i++)
{
var record = input[i];
if (record == null) continue;
var t = (T)record;
output[i] = t;
var item = input[i];
if (item == null) continue;
output[i] = item as TDst; // 此处可能抛出异常。
}
return output;
}
private static Result<T> As<T>(Result<IRecord> input) where T : class, IRecord, new()
/// <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<T>(input.Code, input.Message);
var value = input.Value as T;
return new Result<T>(value);
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);
}
private static Result<T[]> As<T>(Result<IRecord[]> input) where T : class, IRecord, new()
/// <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<T[]>(input.Code, input.Message);
if (!input.HasValue) return new Result<TDst[]>(input.Code, input.Message);
var count = input.Value.Length;
var output = new T[count];
for (var i = 0; i < count; i++) output[i] = input.Value[i] as T;
return new Result<T[]>(output);
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
@ -51,16 +59,18 @@ namespace Apewer.Source
#region IQuery -> IRecord
/// <summary>读取所有行,生成列表。</summary>
public static T[] Fill<T>(IQuery query) where T : class, IRecord, new() => As<T>(Fill(query, typeof(T)));
public static T[] Fill<T>(IQuery query) where T : class, new() => As<object, T>(Fill(query, typeof(T)));
/// <summary>读取所有行填充到 T,组成 T[]。</summary>
public static IRecord[] Fill(IQuery query, Type model)
public static object[] Fill(IQuery query, Type model)
{
if (query == null) return new IRecord[0];
if (model == null) return new IRecord[0];
if (query == null) return new object[0];
if (model == null) return new object[0];
var ts = TableStructure.Parse(model);
var output = new IRecord[query.Rows];
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;
}
@ -94,6 +104,7 @@ namespace Apewer.Source
/// <summary>将 Query 的行,填充到模型实体。</summary>
/// <remarks>填充失败时返回 NULL 值。</remarks>
/// <exception cref="Exception"></exception>
public static IRecord Row(IQuery query, int rowIndex, Type model, TableStructure structure)
{
// 检查参数。
@ -210,27 +221,27 @@ namespace Apewer.Source
/// <param name="database">数据库对象。</param>
/// <param name="model">记录模型。</param>
/// <param name="sql">SQL 语句。</param>
public static Result<IRecord[]> Query(IDbClientAdo database, Type model, string sql)
public static Result<object[]> Query(IDbClientAdo database, Type model, string sql)
{
if (database == null) return new Result<IRecord[]>("数据库无效。");
if (model == null) return new Result<IRecord[]>("模型类型无效。");
if (string.IsNullOrEmpty(sql)) return new Result<IRecord[]>("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<IRecord[]>("查询实例无效。");
if (query == null) return new Result<object[]>("查询实例无效。");
if (query.Table == null)
{
if (!string.IsNullOrEmpty(query.Message)) return new Result<IRecord[]>(query.Message);
return new Result<IRecord[]>("查询实例不包含数据表。");
if (!string.IsNullOrEmpty(query.Message)) return new Result<object[]>(query.Message);
return new Result<object[]>("查询实例不包含数据表。");
}
try
{
var array = Fill(query, model);
return new Result<IRecord[]>(array);
return new Result<object[]>(array);
}
catch (Exception ex)
{
return new Result<IRecord[]>(ex);
return new Result<object[]>(ex);
}
}
}
@ -239,24 +250,24 @@ namespace Apewer.Source
/// <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, IRecord, new() => As<T>(Query(database, typeof(T), sql));
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<IRecord[]> Query(IDbClientAdo database, Type model, Func<string, string> sqlGetter)
public static Result<object[]> Query(IDbClientAdo database, Type model, Func<string, string> sqlGetter)
{
if (sqlGetter == null) return new Result<IRecord[]>("SQL 语句获取函数无效。");
if (sqlGetter == null) return new Result<object[]>("SQL 语句获取函数无效。");
try
{
var tableName = TableStructure.Parse(model).Name;
if (string.IsNullOrEmpty(tableName)) return new Result<IRecord[]>("表名无效。");
if (string.IsNullOrEmpty(tableName)) return new Result<object[]>("表名无效。");
return Query(database, model, sqlGetter(tableName));
}
catch (Exception ex)
{
return new Result<IRecord[]>(ex);
return new Result<object[]>(ex);
}
}
@ -264,7 +275,7 @@ namespace Apewer.Source
/// <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, IRecord, new() => As<T>(Query(database, typeof(T), sqlGetter));
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>
@ -306,7 +317,7 @@ namespace Apewer.Source
/// <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<T>(Get(database, typeof(T), key, sqlGetter));
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>
@ -380,6 +391,55 @@ namespace Apewer.Source
#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
}
}

28
Apewer/Source/Record.cs

@ -37,34 +37,6 @@ namespace Apewer.Source
#region static
internal static void FixProperties(IRecord record)
{
if (record == null) return;
// if (record.Flag == 0) record.Flag = 1;
if (string.IsNullOrEmpty(record.Key))
{
var canResetKey = record as Record;
if (canResetKey != null) canResetKey.ResetKey();
}
var moment = record as IRecordMoment;
if (moment != null)
{
var now = ClockUtility.LucidNow;
if (string.IsNullOrEmpty(moment.Created)) moment.Created = now;
if (string.IsNullOrEmpty(moment.Updated)) moment.Updated = now;
}
var stamp = record as IRecordStamp;
if (stamp != null)
{
var utc = ClockUtility.UtcStamp;
if (stamp.Created == 0L) stamp.Created = utc;
if (stamp.Updated == 0L) stamp.Updated = utc;
}
}
/// <summary>枚举带有 Table 特性的 <typeparamref name="T"/> 派生类型。</summary>
public static List<Type> EnumerateTableTypes<T>() where T : IRecord => EnumerateTableTypes(typeof(T));

5
Apewer/Source/TableStructure.cs

@ -161,7 +161,7 @@ namespace Apewer.Source
return lower;
}
static IDataParameter CreateParameter(IRecord record, ColumnAttribute ca, Func<Parameter, IDataParameter> callback)
static IDataParameter CreateParameter(object record, ColumnAttribute ca, Func<Parameter, IDataParameter> callback)
{
var property = ca.Property;
if (property == null) return null;
@ -207,10 +207,9 @@ namespace Apewer.Source
}
/// <summary>生成 IDataParameter 列表,用于 Insert 和 Update 方法。</summary>
public IDataParameter[] CreateParameters(IRecord record, Func<Parameter, IDataParameter> callback, params string[] excludeds)
public IDataParameter[] CreateParameters(object record, Func<Parameter, IDataParameter> callback, params string[] excludeds)
{
if (record == null || callback == null) return null;
record.FixProperties();
var list = new List<IDataParameter>(_columns.Length);
foreach (var ca in Columns)

2
Apewer/StorageUtility.cs

@ -484,7 +484,7 @@ namespace Apewer
{
var head = new byte[3];
stream.Read(head, 0, 3);
if (BytesUtility.ContainsBOM(head))
if (TextUtility.ContainsBOM(head))
{
var capacity = length - 3;
result = new byte[capacity];

104
Apewer/TextUtility.cs

@ -957,6 +957,110 @@ namespace Apewer
return trim ? Trim(middle, trimBlank) : middle;
}
#region encoding
/// <summary>检查字节数组包含 UTF-8 BOM 头。</summary>
public static bool ContainsBOM(byte[] bytes)
{
if (bytes == null) return false;
if (bytes.LongLength < 3L) return false;
return bytes[0L] == 0xEF && bytes[1L] == 0xBB && bytes[2L] == 0xBF;
}
/// <summary>检查字节数组是 UTF-8 文本。可指定检查的最大字节长度。</summary>
/// <param name="bytes">要检查的字节数组。</param>
/// <param name="offset">已检查的偏移量。</param>
/// <param name="checkLength">检查的最大字节长度。</param>
public static bool IsUTF8(byte[] bytes, Class<int> offset, int checkLength = 1048576)
{
return IsUTF8(bytes, offset, checkLength);
}
/// <summary>检查字节数组是 UTF-8 文本,默认最多检测 1MB 数据。</summary>
/// <param name="bytes">要检查的字节数组。</param>
/// <param name="checkLength">检查的最大字节长度,指定为 0 将不限制检查长度。</param>
/// <param name="offset">已检查的偏移量,用于调试。</param>
public static bool IsUTF8(byte[] bytes, int checkLength = 1048576, Class<int> offset = null)
{
// UTF8在Unicode的基础上制定了这样一套规则:
// 1.对于单字节字符,比特位的最高位为0;
// 2.对于多字节字符,第一个字节的比特位中,最高位有n个1,剩下的n - 1个字节的比特位中,最高位都是10。
// 好了,我知道你一定看不懂,那就先来看看下面例子后,再去看上面定义吧。
// 比如一个字符(“A”),它在UTF8中的编码为(用二进制表示):01000001。由于比特位的最高位是0,表示它是单字节,它只需要1个字节就可以表示。
// 再比如一个字符(“判”),它在UTF8中的编码为(用二进制表示):11100101 10001000 10100100。由于在第一个字节中,比特位最高位有3个1,说明这个字符总共需要3个字节来表示,且后3 - 1 = 2位字节中,比特位的最高位为10。
if (bytes == null) return false;
var length = bytes.LongLength;
// 检查 BOM 头。
if (ContainsBOM(bytes)) return true;
var hasOffset = offset != null;
var append = 0;
if (hasOffset) offset.Value = 0;
for (int i = 0; i < length; i++)
{
if (checkLength > 0 && i >= checkLength) break;
var b = bytes[i];
if (hasOffset) offset.Value = i;
// 追加字节最高位为 0。
if (append > 0)
{
if (b >> 6 != 2) return false;
append -= 1;
continue;
}
// ASCII 字符。
if (b < 128) continue;
// 2 字节 UTF-8。
if (b >= 0xC0 && b <= 0xDF)
{
append = 1;
continue;
}
// 3 字节 UTF-8 字符。
if (b >= 0xE0 && b <= 0xEF)
{
append = 2;
continue;
}
// 4 字节 UTF-8 字符。
if (b >= 0xF0 && b <= 0xF7)
{
append = 3;
continue;
}
// 5 字节 UTF-8 字符。
if (b >= 0xF8 && b <= 0xFB)
{
append = 4;
continue;
}
// 6 字节 UTF-8 字符。
if (b >= 0xFC && b <= 0xFD)
{
append = 5;
continue;
}
// 未知字节,非 UTF-8 定义。
return false;
}
return true;
}
#endregion
}
}

44
Apewer/Web/StaticController.cs

@ -20,6 +20,8 @@ namespace Apewer.Web
List<string> PathSegments;
Class<string> _root = null;
/// <summary></summary>
public StaticController() : base((c) => { ((StaticController)c).Initialize(); return false; }) { }
@ -65,9 +67,49 @@ namespace Apewer.Web
/// <summary>获取此静态站点的目录。</summary>
protected virtual string Root()
{
if (_root) return _root.Value;
var app = RuntimeUtility.ApplicationPath;
var paths = StorageUtility.GetSubFiles(app);
foreach (var path in paths)
{
var split = path.Split('/', '\\');
var lower = split[split.Length - 1];
switch (lower)
{
case "index.html":
case "index.htm":
case "default.html":
case "default.htm":
case "favicon.ico":
_root = new Class<string>(app);
return app;
}
}
var www = StorageUtility.CombinePath(app, "www");
return System.IO.Directory.Exists(www) ? www : app;
if (System.IO.Directory.Exists(www))
{
_root = new Class<string>(www);
return www;
}
var web = StorageUtility.CombinePath(app, "web");
if (System.IO.Directory.Exists(web))
{
_root = new Class<string>(web);
return www;
}
var @static = StorageUtility.CombinePath(app, "static");
if (System.IO.Directory.Exists(@static))
{
_root = new Class<string>(@static);
return www;
}
_root = new Class<string>(app);
return app;
}
/// <summary>从扩展名获取内容类型。</summary>

3
Apewer/_Common.props

@ -14,12 +14,13 @@
<!-- 程序集信息 -->
<PropertyGroup>
<Product>Apewer Libraries</Product>
<Version>6.4.1</Version>
<Version>6.4.2</Version>
</PropertyGroup>
<!-- NuGet -->
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageOutputPath>..\</PackageOutputPath>
<IsPackable>true</IsPackable>
</PropertyGroup>

41
Apewer/_Extensions.cs

@ -52,22 +52,22 @@ public static class Extensions
#region Number
/// <summary></summary>
/// <summary>判断此值为零。</summary>
public static bool IsZero(this decimal @this) => @this.Equals(0M);
/// <summary></summary>
/// <summary>判断此值为零。</summary>
public static bool IsZero(this double @this) => @this.Equals(0D);
/// <summary></summary>
/// <summary>判断此值为零。</summary>
public static bool IsZero(this float @this) => @this.Equals(0F);
/// <summary></summary>
/// <summary>判断此值非零。</summary>
public static bool NotZero(this decimal @this) => !@this.Equals(0M);
/// <summary></summary>
/// <summary>判断此值非零。</summary>
public static bool NotZero(this double @this) => !@this.Equals(0D);
/// <summary></summary>
/// <summary>判断此值非零。</summary>
public static bool NotZero(this float @this) => !@this.Equals(0F);
/// <summary>约束值范围,若源值不在范围中,则修改为接近的值。</summary>
@ -77,17 +77,23 @@ public static class Extensions
#region String、StringBuilder
/// <summary></summary>
public static Int32 Int32(this string @this) => NumberUtility.Int32(@this);
/// <summary>获取 Byte 对象。</summary>
public static byte Byte(this object @this) => NumberUtility.Byte(@this);
/// <summary></summary>
public static Int64 Int64(this string @this) => NumberUtility.Int64(@this);
/// <summary>获取 Int32 对象。</summary>
public static int Int32(this object @this) => NumberUtility.Int32(@this);
/// <summary></summary>
public static Decimal Decimal(this string @this) => NumberUtility.Decimal(@this);
/// <summary>获取 Int64 对象。</summary>
public static long Int64(this object @this) => NumberUtility.Int64(@this);
/// <summary></summary>
public static Double Double(this string @this) => NumberUtility.Double(@this);
/// <summary>获取 Decimal 对象。</summary>
public static decimal Decimal(this object @this) => NumberUtility.Decimal(@this);
/// <summary>获取单精度浮点对象。</summary>
public static float Float(this object @this) => NumberUtility.Float(@this);
/// <summary>获取双精度浮点对象。</summary>
public static double Double(this object @this) => NumberUtility.Double(@this);
/// <summary>将文本转换为字节数组,默认使用 UTF-8。</summary>
public static byte[] Bytes(this string @this, Encoding encoding = null) => TextUtility.Bytes(@this, encoding);
@ -196,10 +202,10 @@ public static class Extensions
public static byte[] Append(this byte[] @this, params byte[] bytes) => BytesUtility.Merge(@this, bytes);
/// <summary>检查字节数组是 UTF-8 文本,默认最多检测 1MB 数据(指定为 0 将不限制检查长度)。</summary>
public static bool IsUTF8(this byte[] @this, int checkLength = 1048576) => BytesUtility.IsUTF8(@this, checkLength, null);
public static bool IsUTF8(this byte[] @this, int checkLength = 1048576) => TextUtility.IsUTF8(@this, checkLength, null);
/// <summary>检查字节数组包含 UTF-8 BOM 头。</summary>
public static bool ContainsBOM(this byte[] @this) => BytesUtility.ContainsBOM(@this);
public static bool ContainsBOM(this byte[] @this) => TextUtility.ContainsBOM(@this);
#endregion
@ -422,9 +428,6 @@ public static class Extensions
#region Source
/// <summary>修补基本属性。</summary>
public static void FixProperties(this IRecord @this) => Record.FixProperties(@this);
/// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
public static Class<DateTime> DateTime(this IQuery @this, int row = 0, int column = 0) => @this == null ? null : Query.DateTime(@this.Value(row, column));

3
ChangeLog.md

@ -1,6 +1,9 @@

### 最新提交
### 6.4.2
- ArrayBuilder:修正 512 长度无法扩展的问题。
### 6.4.1
- Clock:增加 long.DateTime() 方法;
- Json:引用的 List 现改为数组;

Loading…
Cancel
Save