diff --git a/Apewer.Source/Source/DbClient.cs b/Apewer.Source/Source/DbClient.cs
index b1a90e9..c9c7031 100644
--- a/Apewer.Source/Source/DbClient.cs
+++ b/Apewer.Source/Source/DbClient.cs
@@ -56,6 +56,7 @@ namespace Apewer.Source
{
Logger.Error(this, "Connect", ex.Message);
Close();
+ if (ThrowAdoException) throw;
return ex.Message;
}
}
@@ -63,7 +64,11 @@ namespace Apewer.Source
/// 更改已打开的数据库。
public string Change(string store)
{
- if (store.IsEmpty()) return "未指定数据名称。";
+ if (store.IsEmpty())
+ {
+ if (ThrowAdoException) throw new ArgumentNullException(nameof(store));
+ return "未指定数据名称。";
+ }
var connect = Connect();
if (connect.NotEmpty()) return connect;
@@ -76,6 +81,7 @@ namespace Apewer.Source
catch (Exception ex)
{
Logger.Error(this, "Change", ex.Message);
+ if (ThrowAdoException) throw;
return ex.Message();
}
}
@@ -109,6 +115,8 @@ namespace Apewer.Source
#region Transaction
+ const string TransactionNotFound = "事务不存在。";
+
private IDbTransaction _transaction = null;
private bool _autocommit = false;
@@ -117,9 +125,16 @@ namespace Apewer.Source
string Begin(bool commit, Class isolation)
{
- if (_transaction != null) return "已存在未完成的事务,无法再次启动。";
+ if (_transaction != null)
+ {
+ const string msg = "已存在未完成的事务,无法再次启动。";
+ if (ThrowAdoException) throw new SqlException(msg);
+ return msg;
+ }
+
var connect = Connect();
if (connect.NotEmpty()) return $"无法启动事务:连接失败。(${connect})";
+
try
{
_transaction = isolation ? _conn.BeginTransaction(isolation.Value) : _conn.BeginTransaction();
@@ -129,6 +144,7 @@ namespace Apewer.Source
catch (Exception ex)
{
Logger.Error(this, "Begin", ex.Message());
+ if (ThrowAdoException) throw;
return ex.Message();
}
}
@@ -163,7 +179,12 @@ namespace Apewer.Source
/// 提交事务。
public string Commit()
{
- if (_transaction == null) return "事务不存在。";
+ if (_transaction == null)
+ {
+ if (ThrowAdoException) throw new SqlException(TransactionNotFound);
+ return TransactionNotFound;
+ }
+
try
{
_transaction.Commit();
@@ -176,6 +197,7 @@ namespace Apewer.Source
RuntimeUtility.Dispose(_transaction);
_transaction = null;
Logger.Error(this, "Commit", ex.Message());
+ if (ThrowAdoException) throw;
return ex.Message();
}
}
@@ -183,7 +205,13 @@ namespace Apewer.Source
/// 从挂起状态回滚事务。
public string Rollback()
{
- if (_transaction == null) return "事务不存在。";
+ if (_transaction == null)
+ {
+ if (ThrowAdoException) throw new SqlException(TransactionNotFound);
+ return TransactionNotFound;
+ }
+
+
try
{
_transaction.Rollback();
@@ -196,6 +224,7 @@ namespace Apewer.Source
RuntimeUtility.Dispose(_transaction);
_transaction = null;
Logger.Error(this, "Rollback", ex.Message());
+ if (ThrowAdoException) throw;
return ex.Message();
}
}
@@ -204,6 +233,10 @@ namespace Apewer.Source
#region ADO
+ /// 允许 ADO 抛出异常,取代返回错误信息。
+ /// FALSE(默认值)
+ public virtual bool ThrowAdoException { get; set; }
+
/// 查询。
/// SQL 语句。
/// 为 SQL 语句提供的参数。
@@ -262,6 +295,7 @@ namespace Apewer.Source
catch (Exception exception)
{
Logger.Error(this, "Query", exception.Message, sql);
+ if (ThrowAdoException) throw exception;
return new Query(exception);
}
}
@@ -329,6 +363,7 @@ namespace Apewer.Source
{
Logger.Error(this, "Execute", exception.Message, sql);
if (tempTran) Rollback();
+ if (ThrowAdoException) throw exception;
return new Execute(exception);
}
}
diff --git a/Apewer.Source/Source/MySql.cs b/Apewer.Source/Source/MySql.cs
index 39cc02d..323ba21 100644
--- a/Apewer.Source/Source/MySql.cs
+++ b/Apewer.Source/Source/MySql.cs
@@ -51,6 +51,18 @@ namespace Apewer.Source
_connstr = cs;
}
+ /// 构建连接字符串以创建实例。
+ ///
+ public MySql(string address, int port, string store, string user, string pass, Timeout timeout = null) : base(timeout)
+ {
+ if (string.IsNullOrEmpty(address)) throw new ArgumentNullException(nameof(address));
+ if (port < 1 || port > 65535) throw new ArgumentOutOfRangeException(nameof(port));
+ if (string.IsNullOrEmpty(store)) store = "mysql";
+ if (string.IsNullOrEmpty(user)) user = "root";
+ var cs = $"server={address}; port={port}; database={store}; uid={user}; pwd={pass ?? ""}; ";
+ _connstr = cs;
+ }
+
///
protected override void ClearPool(bool all = false)
{
diff --git a/Apewer.Source/Source/SqlClient.cs b/Apewer.Source/Source/SqlClient.cs
index db2261a..d2a2299 100644
--- a/Apewer.Source/Source/SqlClient.cs
+++ b/Apewer.Source/Source/SqlClient.cs
@@ -420,7 +420,7 @@ COLLATE Chinese_PRC_CI_AS
if (!create.Success) return TextUtility.Merge("创建失败:", create.Message);
// 设置兼容性级别。
- var sql2 = $"alter database [{store}] set compatibility_level = 0";
+ var sql2 = $"alter database [{store}] set compatibility_level = 100";
source.Execute(sql2, null, false);
// 设置恢复默认为“简单”
diff --git a/Apewer.Source/Source/Sqlite.cs b/Apewer.Source/Source/Sqlite.cs
index 5405562..989f02b 100644
--- a/Apewer.Source/Source/Sqlite.cs
+++ b/Apewer.Source/Source/Sqlite.cs
@@ -50,6 +50,10 @@ namespace Apewer.Source
/// 连接数据库的密码,使用内存数据库时此参数将被忽略。
/// 超时设定。
///
+ ///
+ ///
+ ///
+ ///
public Sqlite(string path = null, string pass = null, Timeout timeout = null) : base(timeout)
{
// 使用内存。
@@ -61,7 +65,12 @@ namespace Apewer.Source
}
// 使用文件。
- if (!File.Exists(path)) throw new FileNotFoundException("文件不存在。", path);
+ if (!File.Exists(path))
+ {
+ var dir = Directory.GetParent(path).FullName;
+ if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
+ File.Create(path).Dispose();
+ }
_connstr = $"data source='{path}'; version=3; ";
_path = path;
if (!string.IsNullOrEmpty(pass))
@@ -322,31 +331,32 @@ namespace Apewer.Source
#region mirror
/// 保存当前数据库到文件,若文件已存在则将重写文件。
- public bool Save(string path, string pass = null)
+ public string Save(string path, string pass = null)
{
+ if (path.IsEmpty()) return "参数 path 无效。";
if (!StorageUtility.CreateFile(path, 0, true))
{
- Logger.Error(nameof(Sqlite), "Save", TextUtility.Merge("创建文件 ", path, " 失败。"));
- return false;
+ var msg = $"创建文件 {path} 失败。";
+ Logger.Error(nameof(Sqlite), "Save", msg);
+ return msg;
}
-
using (var destination = new Sqlite(path, pass)) return Save(destination);
}
/// 保存当前数据库到目标数据库。
- public bool Save(Sqlite destination) => string.IsNullOrEmpty(Backup(this, destination));
+ public string Save(Sqlite destination) => Backup(this, destination);
/// 加载文件到当前数据库。
- public bool Load(string path, string pass = null)
+ public string Load(string path, string pass = null)
{
using (var source = new Sqlite(path, pass)) return Load(source);
}
/// 加载源数据库到当前数据库。
- public bool Load(Sqlite source) => string.IsNullOrEmpty(Backup(source, this));
+ public string Load(Sqlite source) => Backup(source, this);
/// 备份数据库,返回错误信息。
- static string Backup(Sqlite source, Sqlite destination)
+ string Backup(Sqlite source, Sqlite destination)
{
if (source == null) return "备份失败:源无效。";
if (destination == null) return "备份失败:目标无效。";
@@ -355,22 +365,18 @@ namespace Apewer.Source
var dConnect = source.Connect();
if (dConnect.NotEmpty()) return dConnect;
- lock (source.Locker)
+ try
{
- lock (destination.Locker)
- {
- try
- {
- var src = (SQLiteConnection)source.Connection;
- var dst = (SQLiteConnection)destination.Connection;
- src.BackupDatabase(dst, "main", "main", -1, null, 0);
- return "";
- }
- catch (Exception ex)
- {
- return "SQLite Load Failed: " + ex.Message;
- }
- }
+ var src = (SQLiteConnection)source.Connection;
+ var dst = (SQLiteConnection)destination.Connection;
+ src.BackupDatabase(dst, "main", "main", -1, null, 0);
+ return "";
+ }
+ catch (Exception ex)
+ {
+ var msg = "备份失败:" + ex.Message;
+ Logger?.Error(this, msg);
+ return msg;
}
}
diff --git a/Apewer.Windows/Internals/RegHelper.cs b/Apewer.Windows/Internals/RegHelper.cs
new file mode 100644
index 0000000..d24d190
--- /dev/null
+++ b/Apewer.Windows/Internals/RegHelper.cs
@@ -0,0 +1,118 @@
+using Apewer;
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+
+namespace Apewer.Internals
+{
+
+ /// 注册表。
+ static class RegHelper
+ {
+
+ /// 用户登录后的启动项。
+ const string RunKey = @"Software\Microsoft\Windows\CurrentVersion\Run";
+
+ /// HKEY_CURRENT_USER
+ /// 当前用户的信息。
+ static RegistryKey CurrentUser { get => Registry.CurrentUser; }
+
+ /// HKEY_LOCAL_MACHINE
+ /// 系统信息,对所有用户生效,设置需要管理员权限。
+ static RegistryKey LocalMachine { get => Registry.LocalMachine; }
+
+ static RegistryKey OpenSubKey(RegistryKey root, string key, bool write = false)
+ {
+ var segs = key.Split('\\', '/');
+ var queue = new Queue(segs);
+ var rkey = root;
+ var check = write ? RegistryKeyPermissionCheck.ReadWriteSubTree : RegistryKeyPermissionCheck.ReadSubTree;
+ while (queue.Count > 0)
+ {
+ var name = queue.Dequeue();
+ var sub = rkey.OpenSubKey(name, check);
+ if (sub == null)
+ {
+ if (!write) return null;
+ rkey = rkey.CreateSubKey(name);
+ }
+ else
+ {
+ rkey = sub;
+ }
+ }
+ return rkey;
+ }
+
+ /// 获取字符串。
+ /// 注册表存储区。
+ /// 路径。
+ /// 名称。
+ /// 字符串的值。获取失败时返回 NULL 值。
+ static string Get(RegistryKey root, string key, string name)
+ {
+ try
+ {
+ var rkey = OpenSubKey(root, key, false);
+ if (rkey == null) return null;
+
+ var names = rkey.GetValueNames().ToList();
+ if (names.Contains(name))
+ {
+ var obj = rkey.GetValue(name, null);
+ var str = obj as string;
+ return str;
+ }
+ }
+ catch { }
+ return null;
+ }
+
+ /// 设置字符串,指定 value 为 NULL 可删除该值。
+ /// 注册表存储区。
+ /// 路径。
+ /// 名称。
+ /// 值。
+ /// 错误信息。设置成功时返回 NULL 值。
+ static string Set(RegistryKey root, string key, string name, string value)
+ {
+ try
+ {
+ var rkey = OpenSubKey(root, key, true);
+ if (rkey == null) return "无法打开子键。";
+
+ var apps = rkey.GetValueNames();
+ if (string.IsNullOrEmpty(value)) rkey.DeleteValue(name, true);
+ else rkey.SetValue(name, value, RegistryValueKind.String);
+ return null;
+ }
+ catch (Exception ex)
+ {
+ return ex.Message;
+ }
+ }
+
+ /// 已启用自动启动。
+ public static bool AutoRun
+ {
+ get
+ {
+ var exePath = Application.ExecutablePath;
+ var exeName = Path.GetFileNameWithoutExtension(exePath);
+ return Get(CurrentUser, RunKey, exeName) == exePath;
+ }
+ set
+ {
+ var exePath = Application.ExecutablePath;
+ var exeName = Path.GetFileNameWithoutExtension(exePath);
+ Set(CurrentUser, RunKey, exeName, value ? exePath : null);
+ }
+ }
+
+ }
+
+}
diff --git a/Apewer.Windows/Surface/FormsUtility.cs b/Apewer.Windows/Surface/FormsUtility.cs
index 38849e4..69e78b6 100644
--- a/Apewer.Windows/Surface/FormsUtility.cs
+++ b/Apewer.Windows/Surface/FormsUtility.cs
@@ -9,6 +9,7 @@ using System.Reflection;
using System.Diagnostics;
#if NETFX || NETCORE
+using Microsoft.Win32;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
@@ -829,14 +830,12 @@ namespace Apewer.Surface
private const string FontYahei = "Microsoft Yahei";
private const string FontSimsun = "Simsun";
+ private const string GuiFontRegKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\GRE_Initialize";
private static string _fontname = "";
/// 存在微软雅黑字体。
- public static bool MsyhExist
- {
- get { return File.Exists("c:\\windows\\fonts\\msyh.ttc"); }
- }
+ public static bool MsyhExist { get => File.Exists("c:\\windows\\fonts\\msyh.ttc"); }
/// 获取默认字体名称。
public static string DefaultFontName
@@ -853,8 +852,17 @@ namespace Apewer.Surface
}
}
+ /// 获取默认字体名称。
+ public static string GuiFontName { get => Registry.GetValue(GuiFontRegKey, "GUIFont.Facename", "Arial") as string; }
+
+ /// 获取默认字体大小。
+ public static float GuiFontSize { get => Convert.ToSingle(Registry.GetValue(GuiFontRegKey, "GUIFont.Height", 9F)); }
+
#if NETFX || NETCORE
+ /// 获取默认字体。
+ public static Font GuiFont { get => new Font(GuiFontName, GuiFontSize); }
+
/// 获取默认字体。
public static Font DefaultFont
{
diff --git a/Apewer.Windows/Surface/ImageUtility.cs b/Apewer.Windows/Surface/ImageUtility.cs
index 81b8798..fa48253 100644
--- a/Apewer.Windows/Surface/ImageUtility.cs
+++ b/Apewer.Windows/Surface/ImageUtility.cs
@@ -7,6 +7,7 @@ using Apewer.Models;
using System;
using System.Collections.Generic;
using System.Drawing;
+using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Text;
@@ -15,7 +16,7 @@ namespace Apewer.Surface
{
/// 图像实用工具。
- public sealed class ImageUtility
+ public static class ImageUtility
{
internal static byte[] EmptyBytes { get { return new byte[0]; } }
@@ -50,6 +51,58 @@ namespace Apewer.Surface
return SaveAsBytes(image, ImageFormat.Jpeg, dispose);
}
+ /// 调整图像尺寸,生成新图像。
+ /// 原图像。
+ /// 新图像的宽度,在缩放时此参数用于限定最大宽度。
+ /// 新图像的高度,在缩放时此参数用于限定最大高度。
+ /// 保持原比例进行缩放。
+ /// 新图像。
+ public static Bitmap Resize(this Image image, int width, int height, bool scale = false)
+ {
+ if (image == null) throw new ArgumentNullException(nameof(image));
+ if (width < 0) throw new ArgumentOutOfRangeException(nameof(width));
+ if (height < 0) throw new ArgumentOutOfRangeException(nameof(height));
+
+ var newWidth = width;
+ var newHeight = height;
+ if (scale)
+ {
+ newWidth = image.Width;
+ newHeight = image.Height;
+ var radio = Convert.ToDouble(newWidth) / Convert.ToDouble(newHeight);
+ if (newWidth > width)
+ {
+ newWidth = width;
+ newHeight = Convert.ToInt32(width / radio);
+ }
+ if (newHeight > height)
+ {
+ newWidth = Convert.ToInt32(height * radio);
+ newHeight = height;
+ }
+ }
+
+ var bitmap = new Bitmap(newWidth, newHeight);
+ try
+ {
+ using (var graphic = Graphics.FromImage(bitmap))
+ {
+ graphic.CompositingMode = CompositingMode.SourceOver;
+ graphic.CompositingQuality = CompositingQuality.HighQuality;
+ graphic.SmoothingMode = SmoothingMode.AntiAlias;
+ graphic.Clear(Color.Transparent);
+ graphic.DrawImage(image, 0, 0, width, height);
+ }
+
+ return bitmap;
+ }
+ catch (Exception ex)
+ {
+ RuntimeUtility.Dispose(bitmap);
+ throw ex;
+ }
+ }
+
/// 生成二维码图像,可指定色块边长像素值和纠错率,失败时返回 NULL 值。
public static Bitmap GenerateQrCode(string text, int block = 8)
{
diff --git a/Apewer/Apewer.props b/Apewer/Apewer.props
index 1d64731..edd434c 100644
--- a/Apewer/Apewer.props
+++ b/Apewer/Apewer.props
@@ -9,7 +9,7 @@
Apewer
Apewer Libraries
- 6.7.2
+ 6.7.3
diff --git a/Apewer/BytesUtility.cs b/Apewer/BytesUtility.cs
index 454d171..cbb86ee 100644
--- a/Apewer/BytesUtility.cs
+++ b/Apewer/BytesUtility.cs
@@ -776,6 +776,36 @@ namespace Apewer
#region AES
+ private static void Aes256(byte[] key, byte[] salt, Func create, Stream input, Stream output)
+ {
+ using (var rm = new RijndaelManaged())
+ {
+ rm.KeySize = 256;
+ rm.BlockSize = 128;
+ rm.Mode = CipherMode.ECB;
+ rm.Padding = PaddingMode.PKCS7;
+
+ var k = new Rfc2898DeriveBytes(SHA256(key), salt, 1);
+ rm.Key = k.GetBytes(32);
+ rm.IV = k.GetBytes(16);
+
+ using (var ct = create.Invoke(rm))
+ {
+ using (var cs = new CryptoStream(output, ct, CryptoStreamMode.Write))
+ {
+ Read(input, cs);
+ cs.Close();
+ }
+ }
+ }
+ }
+
+ /// 执行 AES 加密。
+ public static void Aes256Encrypt(Stream input, Stream output, byte[] key) => Aes256(key, key, rm => rm.CreateEncryptor(), input, output);
+
+ /// 执行 AES 解密。
+ public static void Aes256Decrypt(Stream input, Stream output, byte[] key) => Aes256(key, key, rm => rm.CreateDecryptor(), input, output);
+
private static RijndaelManaged Aes256Provider(byte[] key)
{
var k = key ?? Empty; // AesFill(key);
diff --git a/Apewer/Class.cs b/Apewer/Class.cs
index 0797b45..b3ea296 100644
--- a/Apewer/Class.cs
+++ b/Apewer/Class.cs
@@ -13,12 +13,6 @@ namespace Apewer
/// 装箱对象。
public T Value { get; set; }
- ///
- public bool IsNull { get { return Value == null; } }
-
- ///
- public bool HasValue { get { return Value != null; } }
-
/// 创建默认值。
public Class(T value = default, bool tryEquals = true, bool tryHashCode = true)
{
@@ -95,7 +89,6 @@ namespace Apewer
public int CompareTo(Class other)
{
if (Value == null) throw new MissingMemberException(typeof(T).FullName, nameof(Value));
- if (other == null || !other.HasValue) return 1;
if (Value is IComparable) return ((IComparable)Value).CompareTo(other.Value);
if (Value is IComparable) return ((IComparable)Value).CompareTo(other.Value);
@@ -119,7 +112,7 @@ namespace Apewer
var text = instance as Class;
if (text != null) return !string.IsNullOrEmpty(text.Value);
- return instance.HasValue;
+ return instance.NotNull();
}
/// 从 到 T 的隐式转换。
diff --git a/Apewer/ClockUtility.cs b/Apewer/ClockUtility.cs
index 15c46b2..e4fb138 100644
--- a/Apewer/ClockUtility.cs
+++ b/Apewer/ClockUtility.cs
@@ -5,6 +5,8 @@ using System.Diagnostics;
using System.Globalization;
using System.Text;
+using static System.DateTime;
+
namespace Apewer
{
@@ -12,6 +14,29 @@ namespace Apewer
public static class ClockUtility
{
+ #region DateTime
+
+ /// 尝试转换内容为 DateTime 实例。
+ public static Class DateTime(object value)
+ {
+ if (value is DateTime dt) return dt;
+ if (value.IsNull()) return null;
+
+ DateTime result;
+ try
+ {
+ var text = value.ToString();
+ var parsed = System.DateTime.TryParse(text, out result);
+ return parsed ? new Class(result) : null;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ #endregion
+
#region Fixed
private static DateTime _zero = new DateTime(0L, DateTimeKind.Unspecified);
@@ -28,10 +53,10 @@ namespace Apewer
public static DateTime UtcOrigin { get => _utc_origin; }
/// 获取一个 DateTime 对象,该对象设置为此计算机上的当前日期和时间,表示为本地时间。
- public static DateTime Now { get => DateTime.Now; }
+ public static DateTime Now { get => System.DateTime.Now; }
/// 获取一个 DateTime 对象,该对象设置为此计算机上的当前日期和时间,表示为协调通用时间 (UTC)。
- public static DateTime UtcNow { get => DateTime.UtcNow; }
+ public static DateTime UtcNow { get => System.DateTime.UtcNow; }
/// 创建一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000。
public static DateTime NewOrigin(DateTimeKind kind) => new DateTime(1970, 1, 1, 0, 0, 0, 0, kind);
@@ -94,7 +119,7 @@ namespace Apewer
var s = datetime as string;
if (string.IsNullOrEmpty(s)) return Zero;
DateTime value;
- var success = DateTime.TryParse(s, out value);
+ var success = TryParse(s, out value);
return success ? value : Zero;
}
else
@@ -166,11 +191,11 @@ namespace Apewer
}
DateTime dt;
- if (!DateTime.TryParse(str, out dt))
+ if (!TryParse(str, out dt))
{
- if (!str.Contains("-") && DateTime.TryParseExact(str, "yyyy-M-d", null, DateTimeStyles.None, out dt))
+ if (!str.Contains("-") && TryParseExact(str, "yyyy-M-d", null, DateTimeStyles.None, out dt))
{
- if (!str.Contains("/") && DateTime.TryParseExact(str, "yyyy/M/d", null, DateTimeStyles.None, out dt))
+ if (!str.Contains("/") && TryParseExact(str, "yyyy/M/d", null, DateTimeStyles.None, out dt))
{
return null;
}
@@ -192,7 +217,7 @@ namespace Apewer
public static string LucidUtc { get => Lucid(UtcNow); }
/// 表示当前本地日期的文本,显示为易于阅读的格式。
- public static string LucidDate { get { return Lucid(DateTime.Now, true, false, false, false); } }
+ public static string LucidDate { get { return Lucid(Now, true, false, false, false); } }
/// 表示当前本地时间的文本,显示为紧凑的格式。
public static string CompactNow { get => Compact(Now); }
@@ -201,7 +226,7 @@ namespace Apewer
public static string CompactUtc { get => Compact(UtcNow); }
/// 表示当前本地日期的文本,显示为紧凑的格式。
- public static string CompactDate { get { return Compact(DateTime.Now, true, false, false, false); } }
+ public static string CompactDate { get { return Compact(Now, true, false, false, false); } }
/// 转换 DateTime 对象到易于阅读的文本。
public static string Lucid(DateTime datetime, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true)
@@ -305,7 +330,7 @@ namespace Apewer
// 使用默认解析。
{
- if (DateTime.TryParse(text, out DateTime dt)) return dt;
+ if (TryParse(text, out DateTime dt)) return dt;
}
// 尝试解析 Lucid 格式。
@@ -332,7 +357,7 @@ namespace Apewer
day = NumberUtility.Int32(lucid.Substring(8, 2));
if (year < 1) return failed;
if (month < 1 || month > 12) return failed;
- if (day < 1 || day > DateTime.DaysInMonth(year, month)) return failed;
+ if (day < 1 || day > DaysInMonth(year, month)) return failed;
int hour = 0, minute = 0, second = 0, milli = 0;
if (lucid.Length >= 16)
diff --git a/Apewer/CollectionUtility.cs b/Apewer/CollectionUtility.cs
index d9903f5..29d530e 100644
--- a/Apewer/CollectionUtility.cs
+++ b/Apewer/CollectionUtility.cs
@@ -577,6 +577,232 @@ namespace Apewer
#endregion
+ #region Find
+
+ /// 根据条件筛选,将符合条件的元素组成新数组。
+ public static T[] FindAll(this T[] array, Predicate match) => System.Array.FindAll(array, match);
+
+ /// 根据条件筛选,找到第一个符合条件的元素。
+ public static T Find(this T[] array, Predicate match) => System.Array.Find(array, match);
+
+ #endregion
+
+ #region Map
+
+ /// 遍历集合,转换元素,生成新数组。
+ /// 输入的元素类型。
+ /// 输出的元素类型。
+ /// 要遍历的集合。
+ /// 转换程序。
+ /// 新数组的元素类型。
+ public static TOut[] Map(this IEnumerable collection, Func selector)
+ {
+ if (selector == null) throw new ArgumentNullException(nameof(selector));
+ if (collection == null) return new TOut[0];
+
+ if (collection is TIn[] array)
+ {
+ var count = array.Length;
+ var result = new TOut[count];
+ for (var i = 0; i < count; i++)
+ {
+ result[i] = selector.Invoke(array[i]);
+ }
+ return result;
+ }
+ else
+ {
+ var capacity = 0;
+ var count = 0;
+ var list = new List();
+ foreach (var item in collection)
+ {
+ if (capacity == count)
+ {
+ capacity += 1024;
+ list.Capacity += capacity;
+ }
+ list.Add(selector.Invoke(item));
+ count += 1;
+ }
+ return list.ToArray();
+ }
+ }
+
+ #endregion
+
+ #region ForEach
+
+ /// 遍历每个元素,执行回调。
+ /// 元素的类型。
+ /// 元素的集合。
+ /// 回调。参数为本次遍历的元素。
+ public static void ForEach(this IEnumerable collection, Action callback)
+ {
+ if (collection == null || callback == null) return;
+ ForEach(collection, (item, index) =>
+ {
+ callback.Invoke(item);
+ return true;
+ });
+ }
+
+ /// 遍历每个元素,执行回调。
+ /// 元素的类型。
+ /// 元素的集合。
+ /// 回调。参数为本次遍历的元素和遍历的索引值,索引值从 0 开始。
+ public static void ForEach(this IEnumerable collection, Action callback)
+ {
+ if (collection == null || callback == null) return;
+ ForEach(collection, (item, index) =>
+ {
+ callback.Invoke(item, index);
+ return true;
+ });
+ }
+
+ /// 遍历每个元素,执行回调。
+ /// 元素的类型。
+ /// 元素的集合。
+ /// 回调。参数为本次遍历的元素。返回 true 将继续遍历,返回 false 打断遍历。
+ public static void ForEach(this IEnumerable collection, Func callback)
+ {
+ if (collection == null || callback == null) return;
+ ForEach(collection, (item, index) =>
+ {
+ var result = callback.Invoke(item);
+ return result;
+ });
+ }
+
+ /// 遍历每个元素,执行回调。
+ /// 元素的类型。
+ /// 元素的集合。
+ /// 回调。参数为本次遍历的元素和遍历的索引值,索引值从 0 开始。返回 true 将继续遍历,返回 false 打断遍历。
+ public static void ForEach(this IEnumerable collection, Func callback)
+ {
+ if (collection == null || callback == null) return;
+
+ if (collection is T[] array)
+ {
+ var count = array.Length;
+ for (var i = 0; i < count; i++)
+ {
+ var @continue = callback.Invoke(array[i], i);
+ if (!@continue) break;
+ }
+ }
+ else
+ {
+ var index = 0;
+ foreach (var item in collection)
+ {
+ var @continue = callback.Invoke(item, index);
+ if (!@continue) break;
+ index += 1;
+ }
+ }
+ }
+
+ /// 遍历每个元素,执行回调。
+ /// 元素的类型。
+ /// 元素的集合。
+ /// 每次遍历的元素数量。
+ /// 回调。参数为本次遍历的元素和遍历的索引值,索引值从 0 开始。
+ public static void ForEach(this IEnumerable collection, int limit, Action callback)
+ {
+ if (collection == null || callback == null) return;
+
+ ForEach(collection, limit, (items, index) =>
+ {
+ callback.Invoke(items);
+ return true;
+ });
+ }
+
+ /// 遍历每个元素,执行回调。
+ /// 元素的类型。
+ /// 元素的集合。
+ /// 每次遍历的元素数量。
+ /// 回调。参数为本次遍历的元素和遍历的索引值,索引值从 0 开始。
+ public static void ForEach(this IEnumerable collection, int limit, Action callback)
+ {
+ if (collection == null || callback == null) return;
+
+ ForEach(collection, limit, (items, index) =>
+ {
+ callback.Invoke(items, index);
+ return true;
+ });
+ }
+
+ /// 遍历每个元素,执行回调。
+ /// 元素的类型。
+ /// 元素的集合。
+ /// 每次遍历的元素数量。
+ /// 回调。参数为本次遍历的元素。返回 true 将继续遍历,返回 false 打断遍历。
+ public static void ForEach(this IEnumerable collection, int limit, Func callback)
+ {
+ if (collection == null || callback == null) return;
+
+ ForEach(collection, limit, (items, index) =>
+ {
+ var result = callback.Invoke(items);
+ return result;
+ });
+ }
+
+ /// 遍历每个元素,执行回调。
+ /// 元素的类型。
+ /// 元素的集合。
+ /// 每次遍历的元素数量。
+ /// 回调。参数为本次遍历的元素和遍历的索引值,索引值从 0 开始。返回 true 将继续遍历,返回 false 打断遍历。
+ public static void ForEach(this IEnumerable collection, int limit, Func callback)
+ {
+ if (limit < 1) throw new ArgumentOutOfRangeException(nameof(limit));
+ if (collection == null) return;
+ if (callback == null) return;
+
+ if (collection is T[] array)
+ {
+ var total = array.LongLength;
+ var start = 0L;
+ var index = 0;
+ var length = 0L;
+ while (start < total)
+ {
+ length = limit;
+ if (start + length > total) length = total - start;
+ var temp = new T[length];
+ System.Array.Copy(array, start, temp, 0L, length);
+ var @continue = callback.Invoke(temp, index);
+ if (!@continue) break;
+
+ start += length;
+ index += 1;
+ }
+ return;
+ }
+ else
+ {
+ var queue = new Queue(collection);
+ var index = 0;
+ while (queue.Count > 0)
+ {
+ var list = new List(limit);
+ while (list.Count < limit && queue.Count > 0) list.Add(queue.Dequeue());
+
+ var temp = list.ToArray();
+ var @continue = callback.Invoke(temp, index);
+ if (!@continue) break;
+
+ index += 1;
+ }
+ }
+ }
+
+ #endregion
+
}
}
diff --git a/Apewer/Internals/NtfsUnlocker.cs b/Apewer/Internals/NtfsUnlocker.cs
index 8aad3ee..3f3e5f0 100644
--- a/Apewer/Internals/NtfsUnlocker.cs
+++ b/Apewer/Internals/NtfsUnlocker.cs
@@ -22,10 +22,12 @@ namespace Apewer.Internals
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DeleteFile(string name);
- static bool FileExists(string path) => GetFileAttributes(path) != -1;
-
#endregion
+ public const string Postfix = ":Zone.Identifier";
+
+ public static bool FileExists(string path) => GetFileAttributes(path) != -1;
+
public static string DeleteZoneIdentifier(string path)
{
if (!FileExists(path)) return "文件不存在。";
@@ -33,7 +35,7 @@ namespace Apewer.Internals
try
{
var info = new FileInfo(path);
- var streamPath = info.FullName + ":Zone.Identifier";
+ var streamPath = info.FullName + Postfix;
var streamExists = FileExists(streamPath);
if (!streamExists) return null;
diff --git a/Apewer/Json.cs b/Apewer/Json.cs
index a59f9b7..605e934 100644
--- a/Apewer/Json.cs
+++ b/Apewer/Json.cs
@@ -38,7 +38,7 @@ namespace Apewer
[NonSerialized]
private static bool _forceall = true;
- /// 允许产生 Exception,默认为不允许。
+ /// 允许抛出 Exception,默认为不允许。
public static bool AllowException { get { return _throw; } set { _throw = value; } }
/// 当存在递归引用时候包含递归项。指定为 True 时递归项为 Null 值,指定为 False 时不包含递归项。默认值:False。
@@ -1174,7 +1174,7 @@ namespace Apewer
if (list is Array array)
{
if (array != null && array.Rank > 1)
- {
+ {
ToJson(json, array, array.Rank, new int[0]);
return json;
}
@@ -1200,6 +1200,13 @@ namespace Apewer
}
if (recursively) continue;
+ // value 是 null。
+ if (value.IsNull())
+ {
+ json.AddItem();
+ continue;
+ }
+
// 处理 Type 对象。
if (value.GetType().Equals(typeof(Type)) && (previous.Length > 2)) value = FullName((Type)value);
@@ -1207,7 +1214,7 @@ namespace Apewer
if (value.GetType().Equals(typeof(Assembly)) && (previous.Length > 2)) value = ((Assembly)value).FullName;
if (value == null) { json.AddItem(); }
- else if (value is DateTime) { json.AddItem(value.ToString()); }
+ else if (value is DateTime dt) { json.AddItem(SerializeDateTime(dt)); }
else if (value is bool) { json.AddItem((bool)value); }
else if (value is byte) { json.AddItem((byte)value); }
else if (value is sbyte) { json.AddItem((sbyte)value); }
@@ -1318,7 +1325,7 @@ namespace Apewer
}
if (value == null) { json.SetProperty(field); }
- else if (value is DateTime) { json.SetProperty(field, value.ToString()); }
+ else if (value is DateTime dt) { json.SetProperty(field, SerializeDateTime(dt)); }
else if (value is bool) { json.SetProperty(field, (bool)value); }
else if (value is byte) { json.SetProperty(field, (byte)value); }
else if (value is sbyte) { json.SetProperty(field, (sbyte)value); }
@@ -1740,8 +1747,8 @@ namespace Apewer
var subtype = null as Type;
if (array is Array)
{
- string typeName = FullName(array.GetType()).Replace("[]", string.Empty);
- subtype = array.GetType().Assembly.GetType(typeName);
+ var arrayType = array.GetType();
+ subtype = RuntimeUtility.GetTypeOfArrayItem(arrayType);
}
else
{
@@ -1812,12 +1819,11 @@ namespace Apewer
{
try
{
- setter.Invoke(entity, new object[] { DateTime.Parse(value.ToString()) });
+ setter.Invoke(entity, new object[] { DeserializeDateTime(value as string) });
}
catch (Exception exception)
{
if (AllowException) throw exception;
- setter.Invoke(entity, new object[] { "" });
}
}
else if (pt.Equals(typeof(string)))
@@ -2239,6 +2245,35 @@ namespace Apewer
#endregion
+ #region DateTime
+
+ /// 自定义 DateTime 序列化程序。
+ public static Func DateTimeSerializer { get; set; }
+
+ /// 自定义 DateTime 反序列化程序。
+ public static Func DateTimeDeserializer { get; set; }
+
+ /// 序列化 DateTime 实例。
+ public static string SerializeDateTime(DateTime dateTime)
+ {
+ var serializer = DateTimeSerializer;
+ if (serializer != null) return serializer.Invoke(dateTime);
+
+ return ClockUtility.Lucid(dateTime);
+ }
+
+ /// 序列化 DateTime 实例。
+ public static DateTime DeserializeDateTime(string text)
+ {
+ var deserializer = DateTimeDeserializer;
+ if (deserializer != null) return deserializer.Invoke(text);
+
+ var ndt = ClockUtility.Parse(text);
+ return ndt == null ? default(DateTime) : ndt.Value;
+ }
+
+ #endregion
+
}
}
diff --git a/Apewer/Network/MailAddress.cs b/Apewer/Network/MailAddress.cs
index a899eb2..6c99c37 100644
--- a/Apewer/Network/MailAddress.cs
+++ b/Apewer/Network/MailAddress.cs
@@ -8,7 +8,6 @@ namespace Apewer.Network
{
/// 邮件地址。
- [Table("_mail_address")]
[Serializable]
public sealed class MailAddress : Record
{
diff --git a/Apewer/Network/MailClient.cs b/Apewer/Network/MailClient.cs
index d2d7117..34d0984 100644
--- a/Apewer/Network/MailClient.cs
+++ b/Apewer/Network/MailClient.cs
@@ -9,7 +9,6 @@ namespace Apewer.Network
{
/// 简单邮件传输协议客户端。
- [Table("_mail_client")]
[Serializable]
public sealed class MailClient : Record
{
diff --git a/Apewer/Network/MailRecord.cs b/Apewer/Network/MailRecord.cs
index 5ff9ba4..6ae4ca6 100644
--- a/Apewer/Network/MailRecord.cs
+++ b/Apewer/Network/MailRecord.cs
@@ -8,7 +8,6 @@ namespace Apewer.Network
{
/// 邮件记录。
- [Table("_mail_record")]
[Serializable]
public class MailRecord : Record
{
diff --git a/Apewer/NumberUtility.cs b/Apewer/NumberUtility.cs
index a05c914..32464e0 100644
--- a/Apewer/NumberUtility.cs
+++ b/Apewer/NumberUtility.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
+using System.Text.RegularExpressions;
namespace Apewer
{
@@ -164,6 +165,28 @@ namespace Apewer
#endregion
+ #region 表达式计算
+
+ /// 计算文本表达式,以 Int64 输出结果。
+ public static long ComputeInt64(this string expression)
+ {
+ var result = Source.SourceUtility.Compute(expression);
+ if (result == null) return 0L;
+ if (result is long @long) return @long;
+ return Int64(result);
+ }
+
+ /// 计算文本表达式,以 Double 输出结果。
+ public static double ComputeDouble(this string expression)
+ {
+ var result = Source.SourceUtility.Compute(expression);
+ if (result == null) return 0D;
+ if (result is double @double) return @double;
+ return Double(result);
+ }
+
+ #endregion
+
/// 平均值。
public static double Average(params double[] values)
{
@@ -392,41 +415,243 @@ namespace Apewer
return false;
}
+ private static Tout Try(Tin value, Func func)
+ {
+ try { return func.Invoke(value); }
+ catch { return default(Tout); }
+ }
+
/// 获取单精度浮点对象。
- public static float Float(object number) => GetNumber(number, Convert.ToSingle, (v, d) => v / Convert.ToSingle(d));
+ public static float Float(object number)
+ {
+ if (number is bool _bool) return Try(_bool, Convert.ToSingle);
+ if (number is byte _byte) return Try(_byte, Convert.ToSingle);
+ if (number is sbyte _sbyte) return Try(_sbyte, Convert.ToSingle);
+ if (number is short _short) return Try(_short, Convert.ToSingle);
+ if (number is ushort _ushort) return Try(_ushort, Convert.ToSingle);
+ if (number is int _int) return Try(_int, Convert.ToSingle);
+ if (number is uint _uint) return Try(_uint, Convert.ToSingle);
+ if (number is long _long) return Try(_long, Convert.ToSingle);
+ if (number is ulong _ulong) return Try(_ulong, Convert.ToSingle);
+ if (number is float _float) return Try(_float, Convert.ToSingle);
+ if (number is double _double) return Try(_double, Convert.ToSingle);
+ if (number is decimal _decimal) return Try(_decimal, Convert.ToSingle);
+ return GetNumber(number, Convert.ToSingle, (v, d) => v / Convert.ToSingle(d));
+ }
/// 获取单精度浮点对象。
- public static float Single(object number) => GetNumber(number, Convert.ToSingle, (v, d) => v / Convert.ToSingle(d));
+ public static float Single(object number)
+ {
+ if (number is bool _bool) return Try(_bool, Convert.ToSingle);
+ if (number is byte _byte) return Try(_byte, Convert.ToSingle);
+ if (number is sbyte _sbyte) return Try(_sbyte, Convert.ToSingle);
+ if (number is short _short) return Try(_short, Convert.ToSingle);
+ if (number is ushort _ushort) return Try(_ushort, Convert.ToSingle);
+ if (number is int _int) return Try(_int, Convert.ToSingle);
+ if (number is uint _uint) return Try(_uint, Convert.ToSingle);
+ if (number is long _long) return Try(_long, Convert.ToSingle);
+ if (number is ulong _ulong) return Try(_ulong, Convert.ToSingle);
+ if (number is float _float) return Try(_float, Convert.ToSingle);
+ if (number is double _double) return Try(_double, Convert.ToSingle);
+ if (number is decimal _decimal) return Try(_decimal, Convert.ToSingle);
+ return GetNumber(number, Convert.ToSingle, (v, d) => v / Convert.ToSingle(d));
+ }
/// 获取双精度浮点对象。
- public static double Double(object number) => GetNumber(number, Convert.ToDouble, (v, d) => v / d);
+ public static double Double(object number)
+ {
+ if (number is bool _bool) return Try(_bool, Convert.ToDouble);
+ if (number is byte _byte) return Try(_byte, Convert.ToDouble);
+ if (number is sbyte _sbyte) return Try(_sbyte, Convert.ToDouble);
+ if (number is short _short) return Try(_short, Convert.ToDouble);
+ if (number is ushort _ushort) return Try(_ushort, Convert.ToDouble);
+ if (number is int _int) return Try(_int, Convert.ToDouble);
+ if (number is uint _uint) return Try(_uint, Convert.ToDouble);
+ if (number is long _long) return Try(_long, Convert.ToDouble);
+ if (number is ulong _ulong) return Try(_ulong, Convert.ToDouble);
+ if (number is float _float) return Try(_float, Convert.ToDouble);
+ if (number is double _double) return Try(_double, Convert.ToDouble);
+ if (number is decimal _decimal) return Try(_decimal, Convert.ToDouble);
+ return GetNumber(number, Convert.ToDouble, (v, d) => v / d);
+ }
/// 获取 Decimal 对象。
- public static decimal Decimal(object number) => GetNumber(number, DecimalAsFloat, (v, d) => v / Convert.ToDecimal(d));
+ public static decimal Decimal(object number)
+ {
+ if (number is bool _bool) return Try(_bool, Convert.ToDecimal);
+ if (number is byte _byte) return Try(_byte, Convert.ToDecimal);
+ if (number is sbyte _sbyte) return Try(_sbyte, Convert.ToDecimal);
+ if (number is short _short) return Try(_short, Convert.ToDecimal);
+ if (number is ushort _ushort) return Try(_ushort, Convert.ToDecimal);
+ if (number is int _int) return Try(_int, Convert.ToDecimal);
+ if (number is uint _uint) return Try(_uint, Convert.ToDecimal);
+ if (number is long _long) return Try(_long, Convert.ToDecimal);
+ if (number is ulong _ulong) return Try(_ulong, Convert.ToDecimal);
+ if (number is float _float) return Try(_float, Convert.ToDecimal);
+ if (number is double _double) return Try(_double, Convert.ToDecimal);
+ if (number is decimal _decimal) return Try(_decimal, Convert.ToDecimal);
+ return GetNumber(number, DecimalAsFloat, (v, d) => v / Convert.ToDecimal(d));
+ }
/// 获取 Byte 对象。
- public static byte Byte(object number) => GetNumber(number, Convert.ToByte);
+ public static byte Byte(object number)
+ {
+ if (number is bool _bool) return Try(_bool, Convert.ToByte);
+ if (number is byte _byte) return Try(_byte, Convert.ToByte);
+ if (number is sbyte _sbyte) return Try(_sbyte, Convert.ToByte);
+ if (number is short _short) return Try(_short, Convert.ToByte);
+ if (number is ushort _ushort) return Try(_ushort, Convert.ToByte);
+ if (number is int _int) return Try(_int, Convert.ToByte);
+ if (number is uint _uint) return Try(_uint, Convert.ToByte);
+ if (number is long _long) return Try(_long, Convert.ToByte);
+ if (number is ulong _ulong) return Try(_ulong, Convert.ToByte);
+ if (number is float _float) return Try(_float, Convert.ToByte);
+ if (number is double _double) return Try(_double, Convert.ToByte);
+ if (number is decimal _decimal) return Try(_decimal, Convert.ToByte);
+ return GetNumber(number, Convert.ToByte);
+ }
/// 获取 SByte 对象。
- public static sbyte SByte(object number) => GetNumber(number, Convert.ToSByte);
+ public static sbyte SByte(object number)
+ {
+ if (number is bool _bool) return Try(_bool, Convert.ToSByte);
+ if (number is byte _byte) return Try(_byte, Convert.ToSByte);
+ if (number is sbyte _sbyte) return Try(_sbyte, Convert.ToSByte);
+ if (number is short _short) return Try(_short, Convert.ToSByte);
+ if (number is ushort _ushort) return Try(_ushort, Convert.ToSByte);
+ if (number is int _int) return Try(_int, Convert.ToSByte);
+ if (number is uint _uint) return Try(_uint, Convert.ToSByte);
+ if (number is long _long) return Try(_long, Convert.ToSByte);
+ if (number is ulong _ulong) return Try(_ulong, Convert.ToSByte);
+ if (number is float _float) return Try(_float, Convert.ToSByte);
+ if (number is double _double) return Try(_double, Convert.ToSByte);
+ if (number is decimal _decimal) return Try(_decimal, Convert.ToSByte);
+ return GetNumber(number, Convert.ToSByte);
+ }
/// 获取 Int16 对象。
- public static short Int16(object number) => GetNumber(number, Convert.ToInt16);
+ public static short Int16(object number)
+ {
+ if (number is bool _bool) return Try(_bool, Convert.ToInt16);
+ if (number is byte _byte) return Try(_byte, Convert.ToInt16);
+ if (number is sbyte _sbyte) return Try(_sbyte, Convert.ToInt16);
+ if (number is short _short) return Try(_short, Convert.ToInt16);
+ if (number is ushort _ushort) return Try(_ushort, Convert.ToInt16);
+ if (number is int _int) return Try(_int, Convert.ToInt16);
+ if (number is uint _uint) return Try(_uint, Convert.ToInt16);
+ if (number is long _long) return Try(_long, Convert.ToInt16);
+ if (number is ulong _ulong) return Try(_ulong, Convert.ToInt16);
+ if (number is float _float) return Try(_float, Convert.ToInt16);
+ if (number is double _double) return Try(_double, Convert.ToInt16);
+ if (number is decimal _decimal) return Try(_decimal, Convert.ToInt16);
+ return GetNumber(number, Convert.ToInt16);
+ }
/// 获取 UInt16 对象。
- public static ushort UInt16(object number) => GetNumber(number, Convert.ToUInt16);
+ public static ushort UInt16(object number)
+ {
+ if (number is bool _bool) return Try(_bool, Convert.ToUInt16);
+ if (number is byte _byte) return Try(_byte, Convert.ToUInt16);
+ if (number is sbyte _sbyte) return Try(_sbyte, Convert.ToUInt16);
+ if (number is short _short) return Try(_short, Convert.ToUInt16);
+ if (number is ushort _ushort) return Try(_ushort, Convert.ToUInt16);
+ if (number is int _int) return Try(_int, Convert.ToUInt16);
+ if (number is uint _uint) return Try(_uint, Convert.ToUInt16);
+ if (number is long _long) return Try(_long, Convert.ToUInt16);
+ if (number is ulong _ulong) return Try(_ulong, Convert.ToUInt16);
+ if (number is float _float) return Try(_float, Convert.ToUInt16);
+ if (number is double _double) return Try(_double, Convert.ToUInt16);
+ if (number is decimal _decimal) return Try(_decimal, Convert.ToUInt16);
+ return GetNumber(number, Convert.ToUInt16);
+ }
/// 获取 Int32 对象。
- public static int Int32(object number) => GetNumber(number, Convert.ToInt32);
+ public static int Int32(object number)
+ {
+ if (number is bool _bool) return Try(_bool, Convert.ToInt32);
+ if (number is byte _byte) return Try(_byte, Convert.ToInt32);
+ if (number is sbyte _sbyte) return Try(_sbyte, Convert.ToInt32);
+ if (number is short _short) return Try(_short, Convert.ToInt32);
+ if (number is ushort _ushort) return Try(_ushort, Convert.ToInt32);
+ if (number is int _int) return Try(_int, Convert.ToInt32);
+ if (number is uint _uint) return Try(_uint, Convert.ToInt32);
+ if (number is long _long) return Try(_long, Convert.ToInt32);
+ if (number is ulong _ulong) return Try(_ulong, Convert.ToInt32);
+ if (number is float _float) return Try(_float, Convert.ToInt32);
+ if (number is double _double) return Try(_double, Convert.ToInt32);
+ if (number is decimal _decimal) return Try(_decimal, Convert.ToInt32);
+ return GetNumber(number, Convert.ToInt32);
+ }
/// 获取 UInt32 对象。
- public static uint UInt32(object number) => GetNumber(number, Convert.ToUInt32);
+ public static uint UInt32(object number)
+ {
+ if (number is bool _bool) return Try(_bool, Convert.ToUInt32);
+ if (number is byte _byte) return Try(_byte, Convert.ToUInt32);
+ if (number is sbyte _sbyte) return Try(_sbyte, Convert.ToUInt32);
+ if (number is short _short) return Try(_short, Convert.ToUInt32);
+ if (number is ushort _ushort) return Try(_ushort, Convert.ToUInt32);
+ if (number is int _int) return Try(_int, Convert.ToUInt32);
+ if (number is uint _uint) return Try(_uint, Convert.ToUInt32);
+ if (number is long _long) return Try(_long, Convert.ToUInt32);
+ if (number is ulong _ulong) return Try(_ulong, Convert.ToUInt32);
+ if (number is float _float) return Try(_float, Convert.ToUInt32);
+ if (number is double _double) return Try(_double, Convert.ToUInt32);
+ if (number is decimal _decimal) return Try(_decimal, Convert.ToUInt32);
+ return GetNumber(number, Convert.ToUInt32);
+ }
/// 获取 Int64 对象。
- public static long Int64(object number) => GetNumber(number, Convert.ToInt64);
+ public static long Int64(object number)
+ {
+ if (number is bool _bool) return Try(_bool, Convert.ToInt64);
+ if (number is byte _byte) return Try(_byte, Convert.ToInt64);
+ if (number is sbyte _sbyte) return Try(_sbyte, Convert.ToInt64);
+ if (number is short _short) return Try(_short, Convert.ToInt64);
+ if (number is ushort _ushort) return Try(_ushort, Convert.ToInt64);
+ if (number is int _int) return Try(_int, Convert.ToInt64);
+ if (number is uint _uint) return Try(_uint, Convert.ToInt64);
+ if (number is long _long) return Try(_long, Convert.ToInt64);
+ if (number is ulong _ulong) return Try(_ulong, Convert.ToInt64);
+ if (number is float _float) return Try(_float, Convert.ToInt64);
+ if (number is double _double) return Try(_double, Convert.ToInt64);
+ if (number is decimal _decimal) return Try(_decimal, Convert.ToInt64);
+ return GetNumber(number, Convert.ToInt64);
+ }
/// 获取 UInt64 对象。
- public static ulong UInt64(object number) => GetNumber(number, Convert.ToUInt64);
+ public static ulong UInt64(object number)
+ {
+ if (number is bool _bool) return Try(_bool, Convert.ToUInt64);
+ if (number is byte _byte) return Try(_byte, Convert.ToUInt64);
+ if (number is sbyte _sbyte) return Try(_sbyte, Convert.ToUInt64);
+ if (number is short _short) return Try(_short, Convert.ToUInt64);
+ if (number is ushort _ushort) return Try(_ushort, Convert.ToUInt64);
+ if (number is int _int) return Try(_int, Convert.ToUInt64);
+ if (number is uint _uint) return Try(_uint, Convert.ToUInt64);
+ if (number is long _long) return Try(_long, Convert.ToUInt64);
+ if (number is ulong _ulong) return Try(_ulong, Convert.ToUInt64);
+ if (number is float _float) return Try(_float, Convert.ToUInt64);
+ if (number is double _double) return Try(_double, Convert.ToUInt64);
+ if (number is decimal _decimal) return Try(_decimal, Convert.ToUInt64);
+ return GetNumber(number, Convert.ToUInt64);
+ }
+
+ #endregion
+
+ #region 转为字符串
+
+ /// 转为大写中文金额。
+ public static string ToChinesePrice(decimal value)
+ {
+ if (value == 0M) return "零圆整";
+ var s = value.ToString("#L#E#D#C#K#E#D#C#J#E#D#C#I#E#D#C#H#E#D#C#G#E#D#C#F#E#D#C#.0B0A");
+ var d = Regex.Replace(s, @"((?<=-|^)[^1-9]*)|((?'z'0)[0A-E]*((?=[1-9])|(?'-z'(?=[F-L\.]|$))))|((?'b'[F-L])(?'z'0)[0A-L]*((?=[1-9])|(?'-z'(?=[\.]|$))))", "${b}${z}");
+ var r = Regex.Replace(d, ".", m => "负元空零壹贰叁肆伍陆柒捌玖空空空空空空空分角拾佰仟万亿兆京垓秭穰"[m.Value[0] - '-'].ToString());
+ if (!(r.Contains("分") || r.Contains("角"))) r = r + "整";
+ if (value < 0M) r = "负" + r;
+ return r;
+ }
#endregion
diff --git a/Apewer/RuntimeUtility.cs b/Apewer/RuntimeUtility.cs
index 88a1175..088bfa5 100644
--- a/Apewer/RuntimeUtility.cs
+++ b/Apewer/RuntimeUtility.cs
@@ -488,6 +488,32 @@ namespace Apewer
return true;
}
+ /// 获取数组元素的类型。
+ /// 参数必须是正确的数组类型。
+ ///
+ ///
+ public static Type GetTypeOfArrayItem(Type arrayType)
+ {
+ if (arrayType == null) throw new ArgumentNullException(nameof(arrayType));
+ if (!arrayType.BaseType.Equals(typeof(Array))) throw new ArgumentException($"参数 {arrayType} 不是有效的数组类型。");
+
+ var methods = arrayType.GetMethods();
+ foreach (var method in methods)
+ {
+ if (method.Name.ToLower() != "set") continue;
+
+ var parameters = method.GetParameters();
+ if (parameters.Length != 2) continue;
+
+ var length = parameters[0].ParameterType;
+ if (!length.Equals(typeof(int))) continue;
+
+ var item = parameters[1].ParameterType;
+ return item;
+ }
+ throw new ArgumentException($"参数 {arrayType} 不是有效的数组类型。");
+ }
+
#endregion
#region Collect & Dispose
@@ -648,6 +674,29 @@ namespace Apewer
return memory;
}
+#if NETFRAMEWORK
+
+ /// 将代码生成为程序集。
+ public static Assembly Compile(string code, bool executable = false, bool inMemory = true)
+ {
+ using (var cdp = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("C#"))
+ {
+ var cp = new System.CodeDom.Compiler.CompilerParameters();
+ cp.ReferencedAssemblies.Add(typeof(void).Assembly.Location);
+ cp.GenerateExecutable = false;
+ cp.GenerateInMemory = true;
+ cp.TempFiles = new System.CodeDom.Compiler.TempFileCollection(System.IO.Path.GetTempPath());
+
+ var cr = cdp.CompileAssemblyFromSource(cp, code);
+ if (cr.Errors.Count > 0) throw new Exception(cr.Errors[0].ErrorText);
+
+ var assembly = cr.CompiledAssembly;
+ return assembly;
+ }
+ }
+
+#endif
+
#endregion
#region Application
@@ -847,20 +896,16 @@ namespace Apewer
Logger.Internals.Info(typeof(RuntimeUtility), "Invoke " + type.FullName);
RuntimeUtility.Title = type.FullName;
- if (catchException)
+ try
{
- try
- {
- Activator.CreateInstance(type);
- }
- catch (Exception ex)
- {
- Logger.Internals.Exception(typeof(RuntimeUtility), ex);
- }
+ Activator.CreateInstance(type);
}
- else
+ catch (Exception ex)
{
- Activator.CreateInstance(type);
+ var ex2 = ex.InnerException ?? ex;
+ Logger.Internals.Exception(typeof(RuntimeUtility), ex2);
+
+ if (!catchException) throw ex2;
}
}
RuntimeUtility.Title = Title;
diff --git a/Apewer/Source/HttpRecord.cs b/Apewer/Source/HttpRecord.cs
index 611ff09..3f9fcbf 100644
--- a/Apewer/Source/HttpRecord.cs
+++ b/Apewer/Source/HttpRecord.cs
@@ -7,7 +7,6 @@ namespace Apewer.Source
{
///
- [Table]
[Serializable]
public class HttpRecord : Record
{
diff --git a/Apewer/Source/IDbAdo.cs b/Apewer/Source/IDbAdo.cs
index a1ab2e3..ebe8213 100644
--- a/Apewer/Source/IDbAdo.cs
+++ b/Apewer/Source/IDbAdo.cs
@@ -10,6 +10,10 @@ namespace Apewer.Source
public interface IDbAdo : IDisposable
{
+ /// 允许 ADO 抛出异常,取代返回错误信息。
+ /// FALSE(默认值)
+ bool ThrowAdoException { get; set; }
+
#region Connection
/// 获取连接。
diff --git a/Apewer/Source/Query.cs b/Apewer/Source/Query.cs
index 43bff5a..3db51bf 100644
--- a/Apewer/Source/Query.cs
+++ b/Apewer/Source/Query.cs
@@ -92,134 +92,30 @@ namespace Apewer.Source
#region Value
/// 获取默认表中第 0 行、第 0 列的单元格内容。
- public object Value()
- {
- if (_disposed) return null;
- return Value(0, 0);
- }
+ public object Value() => Table.Value(0, 0);
/// 获取默认表中指定行中第 0 列的内容。
/// 行索引,从 0 开始。
- public object Value(int rowIndex)
- {
- if (_disposed) return null;
- return Value(rowIndex, 0);
- }
+ public object Value(int rowIndex) => Table.Value(rowIndex, 0);
/// 获取默认表中第 0 行指定列的内容。
/// 列名称/字段名称,此名称不区分大小写。
- public object Value(string columnName)
- {
- if (_disposed) return null;
- return Value(0, columnName);
- }
+ public object Value(string columnName) => Table.Value(0, columnName);
/// 获取默认表中指定单元格的内容。
/// 行索引,从 0 开始。
/// 列索引,从 0 开始。
- public object Value(int rowIndex, int columnIndex)
- {
- if (_disposed) return null;
- 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;
- }
+ public object Value(int rowIndex, int columnIndex) => Table.Value(rowIndex, columnIndex);
/// 获取默认表中指定单元的内容。
/// 行索引,从 0 开始。
/// 列名称/字段名称,此名称不区分大小写。
- public object Value(int rowIndex, string columnName)
- {
- if (_disposed) return null;
- 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;
- }
-
- /// 搜索默认表。
- /// 搜索条件:列名。
- /// 搜索条件:列值。
- /// 搜索结果。
- private object Value(string conditionColumn, string conditionValue, string resultColumn)
- {
- if (_disposed) return null;
- if ((Table != null) && (!string.IsNullOrEmpty(conditionColumn)) && (conditionValue != null) && (!string.IsNullOrEmpty(resultColumn)))
- {
- for (int i = 0; i < Table.Rows.Count; i++)
- {
- try
- {
- var cValue = Table.Rows[i][conditionColumn];
- if (cValue == null || cValue.Equals(DBNull.Value)) cValue = null;
- if (cValue.ToString() == conditionValue) return Table.Rows[i][resultColumn];
- }
- catch { }
- }
- }
- return null;
- }
-
- /// 搜索默认表。
- /// 搜索条件:列名。
- /// 搜索条件:列值。
- /// 搜索结果的列名。
- private object Value(int conditionColumn, string conditionValue, int resultColumn)
- {
- if (_disposed) return null;
- if ((Table != null) && (conditionColumn >= 0) && (conditionValue != null) && (resultColumn >= 0))
- {
- if ((conditionColumn < Table.Columns.Count) && (resultColumn < Table.Columns.Count))
- {
- for (int i = 0; i < Table.Rows.Count; i++)
- {
- try
- {
- var cValue = Table.Rows[i][conditionColumn];
- if (cValue == null || cValue.Equals(DBNull.Value)) cValue = null;
- if (cValue.ToString() == conditionValue) return Table.Rows[i][resultColumn];
- }
- catch { }
- }
- }
- }
- return null;
- }
+ public object Value(int rowIndex, string columnName) => Table.Value(rowIndex, columnName);
#endregion
#region Method
- /// 搜索默认表。
- /// 搜索条件:列名。
- /// 搜索条件:列值。
- /// 搜索结果。
- private string Text(int conditionColumn, string conditionValue, int resultColumn)
- {
- var value = Value(conditionColumn, conditionValue, resultColumn);
- return Text(value);
- }
-
/// 释放系统资源。
public void Dispose()
{
@@ -236,28 +132,6 @@ namespace Apewer.Source
// GC.SuppressFinalize(this);
}
- /// 当不指定格式化程序时,自动根据类型选择预置的格式化程序。
- private static Func