Browse Source

Apewer-6.7.0

dev
王厅 3 years ago
parent
commit
a0c9eb41d7
  1. 20
      Apewer.Source/Source/Access.cs
  2. 205
      Apewer.Source/Source/DbClient.cs
  3. 50
      Apewer.Source/Source/MySql.cs
  4. 87
      Apewer.Source/Source/SqlClient.cs
  5. 26
      Apewer.Source/Source/SqlServerSouce.cs
  6. 68
      Apewer.Source/Source/Sqlite.cs
  7. 1
      Apewer/Apewer.csproj
  8. 10
      Apewer/Apewer.props
  9. 42
      Apewer/ClockUtility.cs
  10. 478
      Apewer/CollectionUtility.cs
  11. 23
      Apewer/Externals/Newtonsoft.Json-11.0.1/Utilities/LinqBridge.cs
  12. 36
      Apewer/Externals/System/Action.cs
  13. 18
      Apewer/Externals/System/EventHandler.cs
  14. 33
      Apewer/Externals/System/Func.cs
  15. 42
      Apewer/Externals/System/Linq/Enumerable.cs
  16. 2
      Apewer/Json.cs
  17. 121
      Apewer/Network/Extension.cs
  18. 6
      Apewer/Network/HttpClient.cs
  19. 63
      Apewer/Network/Icmp.cs
  20. 31
      Apewer/NetworkUtility.cs
  21. 128
      Apewer/Result.cs
  22. 27
      Apewer/RuntimeUtility.cs
  23. 14
      Apewer/Source/ColumnAttribute.cs
  24. 39
      Apewer/Source/IDbOrm.cs
  25. 11
      Apewer/Source/IRecordPrimaryKey.cs
  26. 89
      Apewer/Source/IndexAttribute.cs
  27. 26
      Apewer/Source/KeyRecord.cs
  28. 99
      Apewer/Source/ModelException.cs
  29. 418
      Apewer/Source/SourceUtility.cs
  30. 71
      Apewer/Source/SqlException.cs
  31. 6
      Apewer/Source/TableAttribute.cs
  32. 71
      Apewer/Source/TableStructure.cs
  33. 2
      Apewer/StorageUtility.cs
  34. 41
      Apewer/TextUtility.cs
  35. 10
      Apewer/Web/ApiModel.cs
  36. 4
      Apewer/Web/ApiProcessor.cs
  37. 2
      Apewer/Web/ApiResponse.cs
  38. 7
      Apewer/Web/ApiUtility.cs
  39. 71
      Apewer/_Delegates.cs
  40. 51
      Apewer/_Extensions.cs
  41. 15
      ChangeLog.md

20
Apewer.Source/Source/Access.cs

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.IO;
using System.Text;
using static Apewer.Source.SourceUtility;
@ -14,7 +13,7 @@ using static Apewer.Source.SourceUtility;
namespace Apewer.Source
{
/// <summary>用于快速连接 Microsoft Access 数据库的辅助。</summary>
/// <summary>连接 Access 数据库的客户端。</summary>
public abstract partial class Access
{
@ -80,7 +79,7 @@ namespace Apewer.Source
public override string[] StoreNames() => throw new InvalidOperationException();
/// <summary></summary>
public override string[] TableNames() => TextColumn("select name from msysobjects where type=1 and flags = 0");
public override string[] TableNames() => QueryStrings("select name from msysobjects where type=1 and flags = 0");
/// <summary></summary>
public override string Insert(object record, string table = null, bool adjust = true)
@ -271,21 +270,21 @@ namespace Apewer.Source
}
/// <summary></summary>
protected override string KeysSql(string tableName, string keyField, string flagField, long flagValue)
protected override string Keys(string tableName, string keyField, string flagField, long flagValue)
{
if (flagValue == 0) return $"select [{keyField}] from [{tableName}]";
else return $"select [{keyField}] from [{tableName}] where [{flagField}] = {flagValue}";
}
/// <summary></summary>
protected override string RecordSql(string tableName, string keyField, string keyValue, string flagField, long flagValue)
protected override string Get(string tableName, string keyField, string keyValue, string flagField, long flagValue)
{
if (flagValue == 0) return $"select top 1 * from [{tableName}] where [{keyField}] = '{keyValue}'";
else return $"select top 1 * from [{tableName}] where [{keyField}] = '{keyValue}' and [{flagField}] = {flagValue}";
}
/// <summary></summary>
protected override string RecordsSql(string tableName, string flagField, long flagValue)
protected override string List(string tableName, string flagField, long flagValue)
{
if (flagValue == 0) return $"select * from [{tableName}]";
else return $"select * from [{tableName}] where [{flagField}] = {flagValue}";
@ -406,9 +405,10 @@ namespace Apewer.Source
#region protected
/// <summary>获取或设置连接字符串。</summary>
/// <exception cref="FileNotFoundException"></exception>
internal protected static string GenerateCS(string provider, string path, string pass, string jo)
{
if (!File.Exists(path)) return null;
if (!File.Exists(path)) throw new FileNotFoundException("文件不存在。", path);
var sb = new StringBuilder();
@ -486,12 +486,13 @@ namespace Apewer.Source
}
/// <summary>使用 Microsoft.Jet.OLEDB.4.0 访问 Access 97 - 2003 数据库文件。</summary>
public sealed class AccessJet4 : Access
public class AccessJet4 : Access
{
const string JetOleDB4 = "microsoft.jet.oledb.4.0";
/// <summary>创建 Access 类的新实例。</summary>
/// <exception cref="FileNotFoundException"></exception>
public AccessJet4(string path, string pass = null, string jo = null, Timeout timeout = null)
: base(GenerateCS(JetOleDB4, path, pass, jo), timeout) { }
@ -503,12 +504,13 @@ namespace Apewer.Source
}
/// <summary>使用 Microsoft.ACE.OLEDB.12.0 访问 Access 2007 数据库文件。</summary>
public sealed class AccessAce12 : Access
public class AccessAce12 : Access
{
const string AceOleDB12 = "microsoft.ace.oledb.12.0";
/// <summary>创建 Access 类的新实例。</summary>
/// <exception cref="FileNotFoundException"></exception>
public AccessAce12(string path, string pass = null, string jo = null, Timeout timeout = null)
: base(GenerateCS(AceOleDB12, path, pass, jo), timeout) { }

205
Apewer.Source/Source/DbClient.cs

@ -1,9 +1,6 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Reflection;
using System.Text;
namespace Apewer.Source
{
@ -21,7 +18,7 @@ namespace Apewer.Source
_timeout = timeout ?? Timeout.Default;
}
#region connection
#region Connection
Timeout _timeout = null;
IDbConnection _conn = null;
@ -84,7 +81,7 @@ namespace Apewer.Source
}
/// <summary>关闭连接,并释放对象所占用的系统资源。</summary>
public void Close()
public virtual void Close()
{
if (_conn != null)
{
@ -100,7 +97,7 @@ namespace Apewer.Source
}
/// <summary>关闭连接,释放对象所占用的系统资源,并清除连接信息。</summary>
public void Dispose()
public virtual void Dispose()
{
Close();
}
@ -110,7 +107,7 @@ namespace Apewer.Source
#endregion
#region transaction
#region Transaction
private IDbTransaction _transaction = null;
private bool _autocommit = false;
@ -205,9 +202,11 @@ namespace Apewer.Source
#endregion
#region ado
#region ADO
/// <summary>查询。</summary>
/// <param name="sql">SQL 语句。</param>
/// <param name="parameters">为 SQL 语句提供的参数。</param>
public IQuery Query(string sql, IEnumerable<IDataParameter> parameters = null)
{
if (TextUtility.IsEmpty(sql)) return new Query(false, "语句无效。");
@ -268,7 +267,32 @@ namespace Apewer.Source
}
}
/// <summary>执行 SQL 语句,并加入参数。</summary>
/// <summary>输出查询结果的首列数据。</summary>
/// <exception cref="SqlException"></exception>
protected string[] QueryStrings(string sql, string[] excluded = null)
{
if (Connect().NotEmpty()) return new string[0];
using (var query = Query(sql))
{
if (!query.Success) throw new SqlException(query, sql);
var rows = query.Rows;
var list = new List<string>(rows);
for (int r = 0; r < query.Rows; r++)
{
var cell = query.Text(r, 0);
if (TextUtility.IsEmpty(cell)) continue;
if (excluded != null && excluded.Contains(cell)) continue;
list.Add(cell);
}
return list.ToArray();
}
}
/// <summary>执行。</summary>
/// <param name="sql">SQL 语句。</param>
/// <param name="parameters">为 SQL 语句提供的参数。</param>
/// <param name="autoTransaction">自动启动事务。</param>
public IExecute Execute(string sql, IEnumerable<IDataParameter> parameters = null, bool autoTransaction = false)
{
if (TextUtility.IsEmpty(sql)) return new Execute(false, "语句无效。");
@ -310,28 +334,18 @@ namespace Apewer.Source
}
}
/// <summary>输出查询结果的首列数据。</summary>
protected string[] TextColumn(string sql, string[] excluded = null)
{
if (Connect().NotEmpty()) return new string[0];
using (var query = Query(sql))
{
var rows = query.Rows;
var list = new List<string>(rows);
for (int r = 0; r < query.Rows; r++)
{
var cell = query.Text(r, 0);
if (TextUtility.IsEmpty(cell)) continue;
if (excluded != null && excluded.Contains(cell)) continue;
list.Add(cell);
}
return list.ToArray();
}
}
/// <summary>查询数据库中的所有表名。</summary>
public abstract string[] TableNames();
/// <summary>查询数据库实例中的所有数据库名。</summary>
public abstract string[] StoreNames();
/// <summary>查询表中的所有列名。</summary>
public abstract string[] ColumnNames(string tableName);
#endregion
#region parameter
#region Parameter
/// <summary>创建参数。</summary>
/// <exception cref="ArgumentNullException"></exception>
@ -360,7 +374,22 @@ namespace Apewer.Source
#endregion
#region orm
#region ORM
/// <summary>检查数据模型结构,存在异常时抛出异常。</summary>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ModelException"></exception>
protected static TableStructure Parse(Type model)
{
if (model == null) throw new ArgumentNullException(nameof(model), "数据模型类型无效。");
var ts = TableStructure.Parse(model);
if (ts == null) throw ModelException.InvalidStructure(model);
if (ts.TableName.IsEmpty()) throw ModelException.InvalidTableName(ts.Model);
if (ts.Key == null || ts.Key.Field.IsEmpty()) throw ModelException.MissingKey(ts.Model);
if (ts.Flag == null || ts.Flag.Field.IsEmpty()) throw ModelException.MissingFlag(ts.Model);
return ts;
}
/// <summary>初始化指定类型,以创建表或增加字段。</summary>
/// <param name="table">指定新的表名。</param>
@ -402,35 +431,32 @@ namespace Apewer.Source
/// <param name="model">目标记录的类型。</param>
/// <param name="sql">要执行的 SQL 语句。</param>
/// <param name="parameters">为 SQL 语句提供的参数。</param>
public Result<object[]> Query(Type model, string sql, IEnumerable<IDataParameter> parameters = null)
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public object[] Query(Type model, string sql, IEnumerable<IDataParameter> parameters = null)
{
if (model == null) return new Result<object[]>("数据模型类型无效。");
if (string.IsNullOrEmpty(sql)) return new Result<object[]>("SQL 语句无效。");
if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql), "SQL 语句无效。");
using (var query = Query(sql, parameters))
{
var result = null as Result<object[]>;
if (query.Success)
{
try
{
var array = SourceUtility.Fill(query, model);
return new Result<object[]>(array);
}
catch (Exception ex) { return new Result<object[]>(ex); }
}
else return new Result<object[]>(query.Message);
if (!query.Success) throw new SqlException(query, sql);
return SourceUtility.Fill(query, model);
}
}
/// <summary>使用指定语句查询,获取查询结果。</summary>
/// <param name="sql">要执行的 SQL 语句。</param>
/// <param name="parameters">为 SQL 语句提供的参数。</param>
public Result<T[]> Query<T>(string sql, IEnumerable<IDataParameter> parameters = null) where T : class, new() => Query(typeof(T), sql, parameters).As<object, T>();
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public T[] Query<T>(string sql, IEnumerable<IDataParameter> parameters = null) where T : class, new() => Query(typeof(T), sql, parameters).As<object, T>();
#endregion
#region record
#region ORM: Record
/// <summary>更新记录。</summary>
/// <param name="record">要更新的记录实体。</param>
@ -439,83 +465,89 @@ namespace Apewer.Source
/// <returns>错误信息。当成功时候返回空字符串。</returns>
public abstract string Update(IRecord record, string table = null, bool adjust = true);
/// <summary></summary>
protected abstract string KeysSql(string tableName, string keyField, string flagField, long flagValue);
/// <summary>生成用于 Keys 方法的 SQL 语句。</summary>
protected abstract string Keys(string tableName, string keyField, string flagField, long flagValue);
/// <summary>获取指定类型的主键,按 Flag 属性筛选。</summary>
/// <param name="model">要查询的类型。</param>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
/// <exception cref="ArgumentNullException"></exception>
public Result<string[]> Keys(Type model, long flag = 0)
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public string[] Keys(Type model, long flag = 0)
{
if (model == null) throw new ArgumentNullException(nameof(model));
var ts = TableStructure.Parse(model);
if (ts.TableName.IsEmpty()) return new Result<string[]>($"类型 <{model.FullName}> 中不包含表名称。");
if (ts.Key == null || ts.Key.Field.IsEmpty()) return new Result<string[]>($"类型 <{model.FullName}> 中不包含 Key 的字段。");
if (ts.Flag == null || ts.Flag.Field.IsEmpty()) return new Result<string[]>($"类型 <{model.FullName}> 中不包含 Flag 的字段。");
var sql = KeysSql(ts.TableName, ts.Key.Field, ts.Flag.Field, flag);
return new Result<string[]>(TextColumn(sql));
var ts = Parse(model);
var sql = Keys(ts.TableName, ts.Key.Field, ts.Flag.Field, flag);
return QueryStrings(sql);
}
/// <summary>获取指定类型的主键,按 Flag 属性筛选。</summary>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<string[]> Keys<T>(long flag = 0) where T : class, IRecord, new() => Keys(typeof(T), flag);
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public string[] Keys<T>(long flag = 0) where T : class, IRecord, new() => Keys(typeof(T), flag);
/// <summary></summary>
protected abstract string RecordSql(string tableName, string keyField, string keyValue, string flagField, long flagValue);
/// <summary>生成用于 Get 方法的 SQL 语句。</summary>
protected abstract string Get(string tableName, string keyField, string keyValue, string flagField, long flagValue);
/// <summary>获取具有指定 Key 的记录,并要求记录具有指定的 Flag 属性。</summary>
/// <param name="model">目标记录的类型。</param>
/// <param name="key">目标记录的主键。</param>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<object> Record(Type model, string key, long flag = 0)
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public object Get(Type model, string key, long flag = 0)
{
if (model == null) throw new ArgumentNullException(nameof(model));
var ts = TableStructure.Parse(model);
if (ts.TableName.IsEmpty()) return new Result<object>($"类型 <{model.FullName}> 中不包含表名称。");
if (ts.Key == null || ts.Key.Field.IsEmpty()) return new Result<object>($"类型 <{model.FullName}> 中不包含 Key 的字段。");
if (ts.Flag == null || ts.Flag.Field.IsEmpty()) return new Result<object>($"类型 <{model.FullName}> 中不包含 Flag 的字段。");
if (ts == null) throw new ModelException($"无法解析类型 {model.Name}。", model);
key = key.SafeKey();
if (key.IsEmpty()) return null;
var sql = RecordSql(ts.TableName, ts.Key.Field, key, ts.Flag.Field, flag);
var sql = Get(ts.TableName, ts.Key.Field, key, ts.Flag.Field, flag);
var records = Query(model, sql, null);
if (records) return new Result<object>(records.Value.First());
else return new Result<object>(records.Message);
return records.First();
}
/// <summary>获取具有指定 Key 的记录,并要求记录具有指定的 Flag 属性。</summary>
/// <param name="key">目标记录的主键。</param>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<T> Record<T>(string key, long flag = 0) where T : class, IRecord, new() => Record(typeof(T), key, flag).As<object, T>();
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public T Get<T>(string key, long flag = 0) where T : class, IRecord, new() => Get(typeof(T), key, flag) as T;
/// <summary></summary>
protected abstract string RecordsSql(string tableName, string flagField, long flagValue);
/// <summary>生成用于 List 方法的 SQL 语句。</summary>
protected abstract string List(string tableName, string flagField, long flagValue);
/// <summary>查询所有记录,可按 Flag 筛选。</summary>
/// <param name="model">目标记录的类型。</param>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<object[]> Records(Type model, long flag = 0)
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public object[] List(Type model, long flag = 0)
{
if (model == null) throw new ArgumentNullException(nameof(model));
var ts = TableStructure.Parse(model);
if (ts.TableName.IsEmpty()) return new Result<object[]>($"类型 <{model.FullName}> 中不包含表名称。");
if (ts.Key == null || ts.Key.Field.IsEmpty()) return new Result<object[]>($"类型 <{model.FullName}> 中不包含 Key 的字段。");
if (ts.Flag == null || ts.Flag.Field.IsEmpty()) return new Result<object[]>($"类型 <{model.FullName}> 中不包含 Flag 的字段。");
var ts = Parse(model);
if (ts == null) throw new ModelException($"无法解析类型 {model.Name}。", model);
var sql = RecordsSql(ts.TableName, ts.Flag.Field, flag);
var sql = List(ts.TableName, ts.Flag.Field, flag);
return Query(model, sql, null);
}
/// <summary>查询所有记录,可按 Flag 筛选。</summary>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<T[]> Records<T>(long flag = 0) where T : class, IRecord, new() => Records(typeof(T), flag).As<object, T>();
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public T[] List<T>(long flag = 0) where T : class, IRecord, new() => List(typeof(T), flag).As<object, T>();
#endregion
#region static
#region Static
/// <summary>获取表名。</summary>
protected static string Table<T>() => Table(typeof(T));
@ -533,7 +565,7 @@ namespace Apewer.Source
#endregion
#region derived
#region Derived
/// <summary>为 Ado 创建 IDataAdapter 对象。</summary>
protected abstract IDataAdapter CreateDataAdapter(IDbCommand command);
@ -546,19 +578,6 @@ namespace Apewer.Source
#endregion
#region initialization
/// <summary>查询数据库中的所有表名。</summary>
public abstract string[] TableNames();
/// <summary>查询数据库实例中的所有数据库名。</summary>
public abstract string[] StoreNames();
/// <summary>查询表中的所有列名。</summary>
public abstract string[] ColumnNames(string tableName);
#endregion
}
}

50
Apewer.Source/Source/MySql.cs

@ -1,24 +1,18 @@
#if MYSQL_6_9 || MYSQL_6_10
/* 2021.11.07 */
using Externals.MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Transactions;
using static Apewer.Source.SourceUtility;
namespace Apewer.Source
{
/// <summary></summary>
public sealed class MySql : DbClient
/// <summary>连接 MySQL 数据库的客户端。</summary>
public class MySql : DbClient
{
#region connection
@ -47,13 +41,13 @@ namespace Apewer.Source
}
/// <summary>构建连接字符串以创建实例。</summary>
/// <exception cref="ArgumentNullException"></exception>
public MySql(string address, string store, string user, string pass, Timeout timeout = null) : base(timeout)
{
var a = address ?? "";
var s = store ?? "";
var u = user ?? "";
var p = pass ?? "";
var cs = $"server={a}; database={s}; uid={u}; pwd={p}; ";
if (string.IsNullOrEmpty(address)) throw new ArgumentNullException(nameof(address));
if (string.IsNullOrEmpty(store)) store = "mysql";
if (string.IsNullOrEmpty(user)) user = "root";
var cs = $"server={address}; database={store}; uid={user}; pwd={pass ?? ""}; ";
_connstr = cs;
}
@ -96,7 +90,7 @@ namespace Apewer.Source
{
var store = StoreName();
var sql = $"select table_name from information_schema.tables where table_schema='{store}' and table_type='base table'";
return TextColumn(sql);
return QueryStrings(sql);
}
/// <summary></summary>
@ -105,7 +99,7 @@ namespace Apewer.Source
var store = StoreName();
var table = TextUtility.AntiInject(tableName);
var sql = $"select column_name from information_schema.columns where table_schema='{store}' and table_name='{table}'";
return TextColumn(sql);
return QueryStrings(sql);
}
/// <summary></summary>
@ -302,21 +296,21 @@ namespace Apewer.Source
}
/// <summary></summary>
protected override string KeysSql(string tableName, string keyField, string flagField, long flagValue)
protected override string Keys(string tableName, string keyField, string flagField, long flagValue)
{
if (flagValue == 0) return $"select `{keyField}` from `{tableName}`";
else return $"select `{keyField}` from `{tableName}` where `{flagField}` = {flagValue}";
}
/// <summary></summary>
protected override string RecordSql(string tableName, string keyField, string keyValue, string flagField, long flagValue)
protected override string Get(string tableName, string keyField, string keyValue, string flagField, long flagValue)
{
if (flagValue == 0) return $"select * from `{tableName}` where `{keyField}` = '{keyValue}' limit 1";
else return $"select * from `{tableName}` where `{keyField}` = '{keyValue}' and `{flagField}` = {flagValue} limit 1";
}
/// <summary></summary>
protected override string RecordsSql(string tableName, string flagField, long flagValue)
protected override string List(string tableName, string flagField, long flagValue)
{
if (flagValue == 0) return $"select * from `{tableName}`";
else return $"select * from `{tableName}` where `{flagField}` = {flagValue}";
@ -333,27 +327,31 @@ namespace Apewer.Source
{
var store = StoreName();
var sql = $"select table_name from information_schema.tables where table_schema='{store}' and table_type='view'";
return TextColumn(sql);
return QueryStrings(sql);
}
/// <summary>获取记录。</summary>
/// <param name="model">填充的记录模型。</param>
/// <param name="skip">要跳过的记录数,可用最小值为 0。</param>
/// <param name="count">要获取的记录数,可用最小值为 1。</param>
public Result<T[]> Range<T>(Type model, int skip, int count) where T : class, new()
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
/// <exception cref="ModelException"></exception>
public T[] Range<T>(Type model, int skip, int count) where T : class, new()
{
if (model == null) return new Result<T[]>("参数 model 无效。");
if (skip < 0) return new Result<T[]>("参数 skip 超出了范围。");
if (count < 1) return new Result<T[]>("参数 count 超出了范围。");
if (model == null) throw new ArgumentNullException(nameof(model));
if (skip < 0) throw new ArgumentOutOfRangeException(nameof(skip));
if (count < 1) throw new ArgumentOutOfRangeException(nameof(count));
var ts = TableStructure.Parse(model);
if (ts.TableName.IsEmpty()) return new Result<T[]>($"无法解析类型 {model.FullName}。");
if (ts.TableName.IsEmpty()) throw ModelException.InvalidTableName(model);
var sql = $"select * from `{ts.TableName}` limit {skip}, {count}";
using (var query = Query(sql))
{
if (!query.Success) return new Result<T[]>(query.Message);
return new Result<T[]>(query.Fill<T>());
if (!query.Success) throw new SqlException(query, sql);
var array = query.Fill<T>();
return array;
}
}

87
Apewer.Source/Source/SqlClient.cs

@ -1,15 +1,10 @@
/* 2021.12.07 */
using Apewer;
using System;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Text;
using static Apewer.Source.SourceUtility;
using System.Data.SqlClient;
using System.IO;
#if NETFRAMEWORK
using System.Data.Sql;
@ -18,9 +13,9 @@ using System.Data.Sql;
namespace Apewer.Source
{
/// <summary></summary>
/// <summary>连接 SQL Server 数据库的客户端。</summary>
[Serializable]
public sealed class SqlClient : DbClient
public class SqlClient : DbClient
{
#region connection
@ -49,18 +44,18 @@ namespace Apewer.Source
}
/// <summary>使用连接凭据创建数据库连接实例。</summary>
/// <exception cref="ArgumentNullException"></exception>
public SqlClient(string address, string store, string user, string pass, Timeout timeout = null) : base(timeout)
{
var a = address ?? "";
var s = store ?? "";
var u = user ?? "";
var p = pass ?? "";
var cs = $"data source = {a}; initial catalog = {s}; ";
if (string.IsNullOrEmpty(u)) cs += "integrated security = sspi; ";
if (address.IsEmpty()) throw new ArgumentNullException(nameof(address));
if (store.IsEmpty()) store = "master";
var cs = $"data source = {address ?? ""}; initial catalog = {store}; ";
if (string.IsNullOrEmpty(user)) cs += "integrated security = sspi; ";
else
{
cs += $"user id = {u}; ";
if (!string.IsNullOrEmpty(p)) cs += $"password = {p}; ";
cs += $"user id = {user}; ";
if (!string.IsNullOrEmpty(pass)) cs += $"password = {pass}; ";
}
if (timeout != null) cs += $"connection timeout = {timeout.Connect}; ";
@ -80,13 +75,13 @@ namespace Apewer.Source
#region override
/// <summary>查询数据库中的所有表名。</summary>
public override string[] TableNames() => TextColumn("select [name] from [sysobjects] where [type] = 'u' order by [name]");
public override string[] TableNames() => QueryStrings("select [name] from [sysobjects] where [type] = 'u' order by [name]");
/// <summary>查询数据库实例中的所有数据库名。</summary>
public override string[] StoreNames() => TextColumn("select [name] from [master]..[sysdatabases] order by [name]", new string[] { "master", "model", "msdb", "tempdb" });
public override string[] StoreNames() => QueryStrings("select [name] from [master]..[sysdatabases] order by [name]", new string[] { "master", "model", "msdb", "tempdb" });
/// <summary>查询表中的所有列名。</summary>
public override string[] ColumnNames(string tableName) => TextColumn($"select [name] from [syscolumns] where [id] = object_id('{TextUtility.AntiInject(tableName)}')");
public override string[] ColumnNames(string tableName) => QueryStrings($"select [name] from [syscolumns] where [id] = object_id('{TextUtility.AntiInject(tableName)}')");
/// <summary>创建表,当表不存在时创建表,当现存表中缺少模型中属性对应的列时增加列。成功时返回空字符串,发生异常时返回异常信息。</summary>
protected override string Initialize(TableStructure structure, string table)
@ -273,21 +268,21 @@ namespace Apewer.Source
protected override IDataParameter CreateParameter() => new SqlParameter();
/// <summary></summary>
protected override string KeysSql(string tableName, string keyField, string flagField, long flagValue)
protected override string Keys(string tableName, string keyField, string flagField, long flagValue)
{
if (flagValue == 0) return $"select [{keyField}] from [{tableName}]";
else return $"select [{keyField}] from [{tableName}] where [{flagField}] = {flagValue}";
}
/// <summary></summary>
protected override string RecordSql(string tableName, string keyField, string keyValue, string flagField, long flagValue)
protected override string Get(string tableName, string keyField, string keyValue, string flagField, long flagValue)
{
if (flagValue == 0) return $"select top 1 * from [{tableName}] where [{keyField}] = '{keyValue}'";
else return $"select top 1 * from [{tableName}] where [{keyField}] = '{keyValue}' and [{flagField}] = {flagValue}";
}
/// <summary></summary>
protected override string RecordsSql(string tableName, string flagField, long flagValue)
protected override string List(string tableName, string flagField, long flagValue)
{
if (flagValue == 0) return $"select * from [{tableName}]";
else return $"select * from [{tableName}] where [{flagField}] = {flagValue}";
@ -320,6 +315,7 @@ namespace Apewer.Source
/// <summary>批量插入,必须在 DataTable 中指定表名,或指定 tableName 参数。</summary>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="Exception"></exception>
public void BulkCopy(DataTable table, string tableName = null)
{
// 检查 table 参数。
@ -334,14 +330,20 @@ namespace Apewer.Source
var connect = Connect();
if (connect.NotEmpty()) throw new Exception(connect);
// 准备参数。
var options = SqlBulkCopyOptions.Default;
var trans = Transaction as SqlTransaction;
if (trans == null) options |= SqlBulkCopyOptions.UseInternalTransaction;
// 批量插入。
var bc = null as SqlBulkCopy;
try
{
bc = new SqlBulkCopy((SqlConnection)Connection);
bc = new SqlBulkCopy((SqlConnection)Connection, options, trans);
bc.DestinationTableName = tableName;
bc.BatchSize = table.Rows.Count;
bc.WriteToServer(table);
try { bc.Close(); } catch { }
}
catch (Exception ex)
{
@ -365,10 +367,10 @@ namespace Apewer.Source
var connect = source.Connect();
if (connect.NotEmpty()) return "创建失败:" + connect;
var schema = source.SimpleCell("select default_schema_name from sys.database_principals where type = 'S' and name=user_name()");
var schema = source.Cell("select default_schema_name from sys.database_principals where type = 'S' and name=user_name()");
if (schema.IsEmpty()) return "创建失败:无法获取默认模式名称。";
var refPath = source.SimpleCell(@"select f.physical_name path from sys.filegroups g left join sys.database_files f on f.data_space_id = g.data_space_id where g.name = 'PRIMARY' and g.type = 'FG' and g.is_default = 1 and g.filegroup_guid is null");
var refPath = source.Cell(@"select f.physical_name path from sys.filegroups g left join sys.database_files f on f.data_space_id = g.data_space_id where g.name = 'PRIMARY' and g.type = 'FG' and g.is_default = 1 and g.filegroup_guid is null");
if (refPath.IsEmpty()) return "创建失败:无法获取文件路径。";
var win = refPath.Substring(1, 2) == ":\\";
@ -465,17 +467,20 @@ COLLATE Chinese_PRC_CI_AS
#if NET20 || NET40
/// <summary>枚举本地网络中服务器的名称。</summary>
public static SqlServerSource[] EnumerateServer()
/// <summary>枚举本地网络中的 SQL Server 实例的信息。</summary>
public static Source[] EnumerateSources()
{
var list = new List<SqlServerSource>();
var list = new List<Source>();
// 表中列名:ServerName、InstanceName、IsClustered、Version。
using (var query = new Query(SqlDataSourceEnumerator.Instance.GetDataSources()))
using (var table = SqlDataSourceEnumerator.Instance.GetDataSources())
{
for (int i = 0; i < query.Rows; i++)
var query = new Query(table);
var rows = query.Rows;
list.Capacity = rows;
for (int i = 0; i < rows; i++)
{
var item = new SqlServerSource();
var item = new Source();
item.ServerName = query.Text(i, "ServerName");
list.Add(item);
}
@ -483,6 +488,24 @@ COLLATE Chinese_PRC_CI_AS
return list.ToArray();
}
/// <summary>SQL Server 实例的信息。</summary>
public sealed class Source
{
/// <summary></summary>
public string ServerName { get; set; }
/// <summary></summary>
public string InstanceName { get; set; }
/// <summary></summary>
public string IsClustered { get; set; }
/// <summary></summary>
public string Version { get; set; }
}
#endif
/// <summary>创建参数。</summary>

26
Apewer.Source/Source/SqlServerSouce.cs

@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Source
{
/// <summary>枚举的 SQL Server 源。</summary>
public class SqlServerSource
{
/// <summary></summary>
public string ServerName { get; set; }
/// <summary></summary>
public string InstanceName { get; set; }
/// <summary></summary>
public string IsClustered { get; set; }
/// <summary></summary>
public string Version { get; set; }
}
}

68
Apewer.Source/Source/Sqlite.cs

@ -1,11 +1,8 @@
/* 2021.11.07 */
using System;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SQLite;
using System.Drawing;
using System.IO;
using System.Text;
//using Mono.Data.Sqlite;
@ -14,18 +11,26 @@ using static Apewer.Source.SourceUtility;
namespace Apewer.Source
{
/// <summary>用于快速连接 SQLite 数据库的辅助。</summary>
public sealed class Sqlite : DbClient
/// <summary>连接 SQLite 数据库的客户端。</summary>
public class Sqlite : DbClient
{
#region 基础
#region connection
private SQLiteConnection _conn = null;
private string _connstr = null;
private string _path = null;
private string _pass = null;
/// <summary>使用连接字符串创建数据库连接实例。</summary>
/// <summary>连接字符串。</summary>
public override string ConnectionString => _connstr;
/// <summary>当前数据库的文件路径。</summary>
public string Path { get => _path; }
/// <summary>使用现有的连接创建实例。</summary>
/// <param name="connection">有效的 SQLite 连接。</param>
/// <param name="timeout">超时设定。</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public Sqlite(IDbConnection connection, Timeout timeout = null) : base(timeout)
@ -40,13 +45,33 @@ namespace Apewer.Source
}
/// <summary>创建连接实例。</summary>
/// <remarks>注意:<br />- 构造函数不会创建不存在的文件;<br />- 参数 path 为文件路径,指定为空时将使用 :memory: 作为路径连接内存。</remarks>
/// <param name="path">数据库的文件路径,指定为空时将使用 :memory: 作为路径。</param>
/// <param name="pass">连接数据库的密码,使用内存数据库时此参数将被忽略。</param>
/// <param name="timeout">超时设定。</param>
/// <exception cref="FileNotFoundException"></exception>
public Sqlite(string path = null, string pass = null, Timeout timeout = null) : base(timeout)
{
_path = path.IsEmpty() ? Memory : path;
// 使用内存。
if (string.IsNullOrEmpty(path) || path.ToLower() == Memory)
{
_connstr = "data source=':memory:'; version=3; ";
_path = Memory;
return;
}
// 使用文件。
if (!File.Exists(path)) throw new FileNotFoundException("文件不存在。", path);
_connstr = $"data source='{_path}'; version=3; ";
_path = path;
if (!string.IsNullOrEmpty(pass))
{
pass = pass.Trim();
if (!string.IsNullOrEmpty(pass))
{
_connstr += $"password={_pass}; ";
_pass = pass;
if (pass.IsEmpty()) _connstr = $"data source='{_path}'; version=3; ";
else _connstr = $"data source='{_path}'; password={_pass}; version=3; ";
}
}
}
/// <summary>
@ -86,14 +111,14 @@ namespace Apewer.Source
#region public
/// <summary></summary>
/// <exception cref="NotImplementedException"></exception>
public override string[] StoreNames() => throw new NotImplementedException();
/// <summary></summary>
public override string[] TableNames() => TextColumn("select name from sqlite_master where type='table' order by name");
public override string[] TableNames() => QueryStrings("select name from sqlite_master where type='table' order by name");
/// <summary></summary>
public override string[] ColumnNames(string tableName) => TextColumn($"pragma table_info('{tableName.SafeName()}'); ");
public override string[] ColumnNames(string tableName) => QueryStrings($"pragma table_info('{tableName.SafeName()}'); ");
/// <summary>插入记录。返回错误信息。</summary>
public override string Insert(object record, string table = null, bool adjust = true)
@ -244,9 +269,6 @@ namespace Apewer.Source
}
}
/// <summary></summary>
public override string ConnectionString => _connstr;
/// <summary></summary>
protected override IDataAdapter CreateDataAdapter(IDbCommand command) => new SQLiteDataAdapter((SQLiteCommand)command);
@ -261,21 +283,21 @@ namespace Apewer.Source
protected override IDataParameter CreateParameter() => new SQLiteParameter();
/// <summary></summary>
protected override string KeysSql(string tableName, string keyField, string flagField, long flagValue)
protected override string Keys(string tableName, string keyField, string flagField, long flagValue)
{
if (flagValue == 0) return $"select [{keyField}] from [{tableName}] where [{flagField}] = {flagValue}";
return $"select [{keyField}] from [{tableName}]";
}
/// <summary></summary>
protected override string RecordSql(string tableName, string keyField, string keyValue, string flagField, long flagValue)
protected override string Get(string tableName, string keyField, string keyValue, string flagField, long flagValue)
{
if (flagValue == 0) return $"select * from [{tableName}] where [{keyField}] = '{keyValue}' limit 1";
else return $"select * from [{tableName}] where [{keyField}] = '{keyValue}' and [{flagField}] = {flagValue} limit 1";
}
/// <summary></summary>
protected override string RecordsSql(string tableName, string flagField, long flagValue)
protected override string List(string tableName, string flagField, long flagValue)
{
if (flagValue == 0) return $"select * from [{tableName}]";
else return $"select * from [{tableName}] where [{flagField}] = {flagValue}";
@ -292,7 +314,7 @@ namespace Apewer.Source
public const string Memory = ":memory:";
/// <summary>查询数据库中的所有视图名。</summary>
public string[] ViewNames() => TextColumn("select name from sqlite_master where type='view' order by name");
public string[] ViewNames() => QueryStrings("select name from sqlite_master where type='view' order by name");
#endregion

1
Apewer/Apewer.csproj

@ -3,6 +3,7 @@
<Import Project="..\Apewer\Apewer.props" />
<PropertyGroup>
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net461;net40;net20</TargetFrameworks>
</PropertyGroup>

10
Apewer/Apewer.props

@ -9,7 +9,7 @@
<Description></Description>
<RootNamespace>Apewer</RootNamespace>
<Product>Apewer Libraries</Product>
<Version>6.6.28</Version>
<Version>6.7.0</Version>
</PropertyGroup>
<!-- 生成 -->
@ -51,13 +51,11 @@
</PropertyGroup>
<!-- Visual Studio 2022 -->
<!--
<ItemGroup>
<PackageReference Condition="'$(TargetFramework)'=='net461'" Include="Microsoft.NETFramework.ReferenceAssemblies.net461" Version="1.0.2" PrivateAssets="all" />
<PackageReference Condition="'$(TargetFramework)'=='net40'" Include="Microsoft.NETFramework.ReferenceAssemblies.net40" Version="1.0.2" PrivateAssets="all" />
<PackageReference Condition="'$(TargetFramework)'=='net20'" Include="Microsoft.NETFramework.ReferenceAssemblies.net20" Version="1.0.2" PrivateAssets="all" />
<PackageReference Condition="'$(TargetFramework)' == 'net461'" Include="Microsoft.NETFramework.ReferenceAssemblies.net461" Version="1.0.3" PrivateAssets="all" />
<PackageReference Condition="'$(TargetFramework)' == 'net40'" Include="Microsoft.NETFramework.ReferenceAssemblies.net40" Version="1.0.3" PrivateAssets="all" />
<PackageReference Condition="'$(TargetFramework)' == 'net20'" Include="Microsoft.NETFramework.ReferenceAssemblies.net20" Version="1.0.3" PrivateAssets="all" />
</ItemGroup>
-->
<!-- <Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" /> -->

42
Apewer/ClockUtility.cs

@ -9,14 +9,23 @@ namespace Apewer
{
/// <summary>时钟。</summary>
public class ClockUtility
public static class ClockUtility
{
#region Fixed
private static DateTime _zero = new DateTime(0L, DateTimeKind.Unspecified);
private static DateTime _origin = NewOrigin(DateTimeKind.Unspecified);
private static DateTime _utc_origin = NewOrigin(DateTimeKind.Utc);
/// <summary>创建新的零值 DateTime 对象。</summary>
public static DateTime Zero { get => new DateTime(0L, DateTimeKind.Utc); }
public static DateTime Zero { get => _zero; }
/// <summary>获取一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000,表示为本地时间。</summary>
public static DateTime Origin { get => _origin; }
/// <summary>获取一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000。</summary>
public static DateTime Origin { get => new DateTime(1970, 1, 1, 0, 0, 0, 0); }
/// <summary>获取一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000,表示为协调通用时间 (UTC)。</summary>
public static DateTime UtcOrigin { get => _utc_origin; }
/// <summary>获取一个 DateTime 对象,该对象设置为此计算机上的当前日期和时间,表示为本地时间。</summary>
public static DateTime Now { get => DateTime.Now; }
@ -24,10 +33,28 @@ namespace Apewer
/// <summary>获取一个 DateTime 对象,该对象设置为此计算机上的当前日期和时间,表示为协调通用时间 (UTC)。</summary>
public static DateTime UtcNow { get => DateTime.UtcNow; }
/// <summary>创建一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000。</summary>
public static DateTime NewOrigin(DateTimeKind kind) => new DateTime(1970, 1, 1, 0, 0, 0, 0, kind);
#endregion
#region Clone
/// <summary>克隆 DateTime 对象,并使用新的 Kind。</summary>
/// <param name="dateTime">要克隆的 DateTime 对象。</param>
/// <param name="kind">时间类型。</param>
/// <returns>克隆后带有新 Kind 的 DateTime 对象。</returns>
public static DateTime Clone(this DateTime dateTime, DateTimeKind kind)
{
return new DateTime(dateTime.Ticks, kind);
}
#endregion
#region Common
/// <summary>判断指定年份是闰年。</summary>
public static bool IsLeapYear(int year)
public static bool IsLeapYear(this int year)
{
if (year % 400 == 0) return true;
if (year % 100 == 0) return false;
@ -105,11 +132,12 @@ namespace Apewer
/// <summary>从毫秒时间戳获取 DateTime 对象。发生异常且不允许异常时将返回 1970-01-01 00:00:00.000。</summary>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static DateTime FromStamp(long stamp, bool throwException = true)
public static DateTime FromStamp(long stamp, DateTimeKind kind = DateTimeKind.Unspecified, bool throwException = true)
{
try
{
var datetime = Origin.AddMilliseconds(Convert.ToDouble(stamp));
var origin = NewOrigin(kind);
var datetime = origin.AddMilliseconds(Convert.ToDouble(stamp));
return datetime;
}
catch

478
Apewer/Internals/CollectionHelper.cs → Apewer/CollectionUtility.cs

@ -4,91 +4,166 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
namespace Apewer.Internals
namespace Apewer
{
internal class CollectionHelper
/// <summary>集合的实用工具。</summary>
public static class CollectionUtility
{
#region 排序。
#region 判断
public static List<T> Sort<T>(List<T> list, Func<T, T, int> comparison)
/// <summary>判断集合为空。</summary>
public static bool IsEmpty<T>(IEnumerable<T> objects)
{
if (list == null) return null;
if (comparison == null) return list;
list.Sort(new Comparison<T>(comparison));
return list;
if (objects == null) return true;
if (objects is T[]) return ((T[])objects).LongLength < 1L;
if (objects is ICollection<T>) return ((ICollection<T>)objects).Count < 1;
foreach (var item in objects) return false;
return true;
}
public static List<T> Ascend<T>(List<T> list) where T : IComparable<T>
/// <summary>判断集合存在元素。</summary>
public static bool NotEmpty<T>(IEnumerable<T> objects)
{
if (list == null) return null;
list.Sort((a, b) => a.CompareTo(b));
return list;
if (objects == null) return false;
if (objects is T[]) return ((T[])objects).LongLength > 0L;
if (objects is ICollection<T>) return ((ICollection<T>)objects).Count > 0;
foreach (var item in objects) return true;
return false;
}
public static List<T> Descend<T>(List<T> list) where T : IComparable<T>
/// <summary>获取集合中元素的数量。</summary>
public static int Count<T>(IEnumerable<T> objects)
{
if (list == null) return null;
list.Sort((a, b) => -a.CompareTo(b));
return list;
if (objects == null) return 0;
var array = objects as T[];
if (array != null) return array.Length;
var collection = objects as ICollection<T>;
if (collection != null) return collection.Count;
var count = 0;
foreach (var cell in objects) count++;
return count;
}
public static Dictionary<TKey, TValue> SortKey<TKey, TValue>(Dictionary<TKey, TValue> dict, Func<TKey, TKey, int> comparison)
/// <summary>检查集合是否包含 item。</summary>
public static bool Contains<T>(IEnumerable<T> objects, T cell)
{
if (dict == null) return null;
if (comparison == null) return null;
var list = new List<KeyValuePair<TKey, TValue>>(dict);
list.Sort(new Comparison<KeyValuePair<TKey, TValue>>((a, b) => comparison(a.Key, b.Key)));
dict.Clear();
foreach (var item in list) dict.Add(item.Key, item.Value);
return dict;
}
if (objects == null) return false;
public static Dictionary<TKey, TValue> SortValue<TKey, TValue>(Dictionary<TKey, TValue> dict, Func<TValue, TValue, int> comparison)
// objects 实现了含有 Contains 方法的接口。
if (objects is ICollection<T>) return ((ICollection<T>)objects).Contains(cell);
// cell 无效。
if (cell == null)
{
if (dict == null) return null;
if (comparison == null) return dict;
var list = new List<KeyValuePair<TKey, TValue>>(dict);
list.Sort(new Comparison<KeyValuePair<TKey, TValue>>((a, b) => comparison(a.Value, b.Value)));
dict.Clear();
foreach (var item in list) dict.Add(item.Key, item.Value);
return dict;
foreach (var i in objects)
{
if (i == null) return true;
}
return false;
}
#endregion
// cell 有效,进行默认比较。
var comparer = EqualityComparer<T>.Default;
foreach (var i in objects)
{
if (comparer.Equals(i, cell)) return true;
}
return false;
}
public static T First<T>(IEnumerable<T> collection, T failed = default(T))
/// <summary>获取 item 在集合中的偏移位置,不存在时返回 -1。</summary>
public static int IndexOf<T>(IEnumerable<T> objects, T item)
{
if (collection == null) return failed;
if (objects == null) return -1;
if (objects is IList list) return list.IndexOf(item);
var array = collection as T[];
if (array != null) return array.Length > 0 ? array[0] : failed;
if (item == null)
{
if (objects is T[] array)
{
var length = array.Length;
for (var i = 0; i < length; i++)
{
if (array[i] == null) return i;
}
return -1;
}
var list = collection as IList<T>;
if (list != null) return list.Count > 0 ? list[0] : failed;
var index = 0;
foreach (var obj in objects)
{
if (obj == null) return index;
index++;
}
return -1;
}
else
{
var comparer = EqualityComparer<T>.Default;
foreach (var item in collection) return item;
return failed;
if (objects is T[] array)
{
var length = array.Length;
for (var i = 0; i < length; i++)
{
if (comparer.Equals(item, array[i])) return i;
}
return -1;
}
public static T Last<T>(IEnumerable<T> collection, T failed = default(T))
var index = 0;
foreach (var obj in objects)
{
if (collection == null) return failed;
if (comparer.Equals(item, obj)) return index;
index++;
}
return -1;
}
}
var array = collection as T[];
if (array != null) return array.Length > 0 ? array[array.Length - 1] : failed;
#endregion
var list = collection as IList<T>;
if (list != null) return list.Count > 0 ? list[list.Count - 1] : failed;
#region 类型转换
var value = failed;
foreach (var item in collection) value = item;
return value;
/// <summary>转换模型类型。</summary>
public static TDst[] As<TSrc, TDst>(this TSrc[] array) where TDst : class
{
if (array == null) return null;
var count = array.Length;
var output = new TDst[count];
for (var i = 0; i < count; i++)
{
var item = array[i];
if (item == null) continue;
output[i] = item as TDst;
}
return output;
}
// 安全转换为 List<T> 对象。可指定排除 NULL 值元素。
public static List<T> ToList<T>(IEnumerable<T> objects, bool excludeNull = false)
/// <summary>转换模型类型。</summary>
public static TDst[] As<TSrc, TDst>(this TSrc[] array, Func<TSrc, TDst> convert)
{
if (convert == null) throw new ArgumentNullException(nameof(convert));
if (array == null) return null;
var count = array.Length;
var output = new TDst[count];
for (var i = 0; i < count; i++)
{
var item = array[i];
if (item == null) continue;
output[i] = convert(item);
}
return output;
}
/// <summary>安全转换为 List&lt;<typeparamref name="T"/>&gt; 对象。可指定排除 NULL 值元素。</summary>
public static List<T> List<T>(IEnumerable<T> objects, bool excludeNull = false)
{
if (objects == null) return new List<T>();
@ -122,8 +197,8 @@ namespace Apewer.Internals
return list;
}
// 安全转换为 T[] 对象。可指定排除 NULL 值元素。
public static T[] ToArray<T>(IEnumerable<T> objects, bool excludeNull = false)
/// <summary>安全转换为 &lt;<typeparamref name="T"/>&gt;[] 对象。可指定排除 NULL 值元素。</summary>
public static T[] Array<T>(IEnumerable<T> objects, bool excludeNull = false)
{
if (objects == null) return new T[0];
if (objects is T[]) return (T[])objects;
@ -148,7 +223,7 @@ namespace Apewer.Internals
{
capacity += group;
var temp = new T[capacity];
Array.Copy(array, 0, temp, 0, added);
System.Array.Copy(array, 0, temp, 0, added);
array = temp;
}
array[added] = item;
@ -156,115 +231,84 @@ namespace Apewer.Internals
}
if (added < 1 || added == capacity) return array;
var collapsed = new T[added];
Array.Copy(array, 0, collapsed, 0, added);
System.Array.Copy(array, 0, collapsed, 0, added);
return collapsed;
}
}
public static bool IsEmpty<T>(IEnumerable<T> objects)
{
if (objects == null) return true;
if (objects is T[]) return ((T[])objects).LongLength == 0;
if (objects is ICollection<T>) return ((ICollection<T>)objects).Count == 0;
foreach (var i in objects) return false;
return true;
}
public static bool NotEmpty<T>(IEnumerable<T> objects) => !IsEmpty<T>(objects);
public static int IndexOf<T>(IEnumerable<T> objects, T item)
{
if (objects == null) return -1;
if (objects is IList list) return list.IndexOf(item);
/// <summary>生成 StringPairs 对象实例为副本。</summary>
public static StringPairs StringPairs(NameValueCollection @this) => Apewer.StringPairs.From(@this);
if (item == null)
/// <summary>转换集合为数组。</summary>
/// <param name="collection"></param>
/// <returns></returns>
public static Dictionary<string, string[]> Dictionary(NameValueCollection collection)
{
if (objects is T[] array)
{
var length = array.Length;
for (var i = 0; i < length; i++)
if (collection == null) return null;
var count = collection.Count;
var dict = new Dictionary<string, string[]>();
for (var i = 0; i < count; i++)
{
if (array[i] == null) return i;
var key = collection.GetKey(i);
var values = collection.GetValues(i);
dict.Add(key, values);
}
return -1;
return dict;
}
var index = 0;
foreach (var obj in objects)
{
if (obj == null) return index;
index++;
}
return -1;
}
else
/// <summary>转换集合为字典。</summary>
/// <typeparam name="TKey">字典 Key 的类型。</typeparam>
/// <typeparam name="TValue">字典 Value 的类型。</typeparam>
/// <param name="items">要转换的集合。</param>
/// <param name="key">根据元素获取 Key 的函数。</param>
/// <exception cref="ArgumentNullException"></exception>
public static Dictionary<TKey, TValue> Dictionary<TKey, TValue>(IEnumerable<TValue> items, Func<TValue, TKey> key)
{
var comparer = EqualityComparer<T>.Default;
if (items == null) throw new ArgumentNullException(nameof(items));
if (key == null) throw new ArgumentNullException(nameof(key));
if (objects is T[] array)
{
var length = array.Length;
for (var i = 0; i < length; i++)
var dict = new Dictionary<TKey, TValue>();
foreach (var i in items)
{
if (comparer.Equals(item, array[i])) return i;
}
return -1;
}
if (i == null) continue;
var index = 0;
foreach (var obj in objects)
{
if (comparer.Equals(item, obj)) return index;
index++;
}
return -1;
var k = key(i);
if (k.IsNull()) continue;
if (dict.ContainsKey(k)) continue;
dict.Add(k, i);
}
return dict;
}
// 判断集合包含特定值。
public static bool Contains<T>(IEnumerable<T> objects, T cell)
{
if (objects == null) return false;
#endregion
// objects 实现了含有 Contains 方法的接口。
if (objects is ICollection<T>) return ((ICollection<T>)objects).Contains(cell);
#region 修改集合
// cell 无效。
if (cell == null)
{
foreach (var i in objects)
/// <summary>添加多个元素。</summary>
public static void Add<T>(List<T> list, params T[] items)
{
if (i == null) return true;
}
return false;
if (list != null && items != null) list.AddRange(items);
}
// cell 有效,进行默认比较。
var comparer = EqualityComparer<T>.Default;
foreach (var i in objects)
/// <summary>添加多个元素。</summary>
public static IList<T> Add<T>(IList<T> list, IEnumerable<T> items)
{
if (comparer.Equals(i, cell)) return true;
if (list != null && items != null)
{
foreach (var item in items) list.Add(item);
}
return false;
return list;
}
// 获取集合中元素的数量。
public static int Count<T>(IEnumerable<T> objects)
/// <summary>添加元素。</summary>
public static bool Add<TKey, TValue>(IList<KeyValuePair<TKey, TValue>> list, TKey key, TValue value)
{
if (objects == null) return 0;
var array = objects as T[];
if (array != null) return array.Length;
var collection = objects as ICollection<T>;
if (collection != null) return collection.Count;
var count = 0;
foreach (var cell in objects) count++;
return count;
if (list == null) return false;
list.Add(new KeyValuePair<TKey, TValue>(key, value));
return true;
}
// 对元素去重,且去除 NULL 值。
/// <summary>对元素去重,且去除 NULL 值。</summary>
public static T[] Distinct<T>(IEnumerable<T> items)
{
if (items == null) throw new ArgumentNullException(nameof(items));
@ -295,13 +339,20 @@ namespace Apewer.Internals
if (added < count)
{
var temp = new T[added];
Array.Copy(array, 0, temp, 0, added);
System.Array.Copy(array, 0, temp, 0, added);
array = temp;
}
return array;
}
// 获取可枚举集合的部分元素。
/// <summary>获取可枚举集合的部分元素。</summary>
/// <typeparam name="T">集合元素的类型。</typeparam>
/// <param name="objects">原集合。</param>
/// <param name="skip">在集合前段要跳过的元素数量。</param>
/// <param name="count">要获取的元素数量,指定为负数时不限元素数量。</param>
/// <param name="stuffer">填充器,获取范围超出原集合的部分,使用此方法填充元素;此函数默认返回 <typeparamref name="T"/> 的默认值。</param>
/// <returns>数量符合 count 的数组。</returns>
public static T[] Slice<T>(IEnumerable<T> objects, int skip = 0, int count = -1, Func<T> stuffer = null)
{
if (count == 0) return new T[0];
@ -318,6 +369,7 @@ namespace Apewer.Internals
if (count > 0 && added == count) return ab.Export();
}
}
if (objects != null)
{
var offset = 0;
@ -334,45 +386,161 @@ namespace Apewer.Internals
if (count > 0 && added == count) return ab.Export();
}
}
while (added < count)
{
ab.Add(stuffer == null ? default : stuffer());
added++;
}
return ab.Export();
}
public static IList<T> Add<T>(IList<T> list, IEnumerable<T> items)
#endregion
#region 排序
/// <summary>对列表中的元素排序。</summary>
public static List<T> Sort<T>(List<T> list, Func<T, T, int> comparison)
{
if (list != null && items != null)
if (list == null) return null;
if (comparison == null) return list;
list.Sort(new Comparison<T>(comparison));
return list;
}
/// <summary>对数组排序。</summary>
/// <exception cref="NullReferenceException"></exception>
public static T[] Sort<T>(T[] array, Func<T, T, int> comparison)
{
foreach (var item in items) list.Add(item);
if (array == null) return array;
if (comparison == null) return array;
System.Array.Sort(array, new Comparison<T>(comparison));
return array;
}
/// <summary>获取集合中的第一个元素。可指定失败时的默认返回值。</summary>
public static T First<T>(IEnumerable<T> collection, T failed = default(T))
{
if (collection == null) return failed;
var array = collection as T[];
if (array != null) return array.Length > 0 ? array[0] : failed;
var list = collection as IList<T>;
if (list != null) return list.Count > 0 ? list[0] : failed;
foreach (var item in collection) return item;
return failed;
}
/// <summary>获取集合中的最后一个元素。可指定失败时的默认返回值。</summary>
public static T Last<T>(IEnumerable<T> collection, T failed = default(T))
{
if (collection == null) return failed;
var array = collection as T[];
if (array != null) return array.Length > 0 ? array[array.Length - 1] : failed;
var list = collection as IList<T>;
if (list != null) return list.Count > 0 ? list[list.Count - 1] : failed;
var value = failed;
foreach (var item in collection) value = item;
return value;
}
/// <summary>对数组升序排序。</summary>
/// <exception cref="NullReferenceException"></exception>
public static T[] Ascend<T>(T[] array) where T : IComparable<T>
{
if (array == null) return null;
array.Sort((a, b) => a.CompareTo(b));
return array;
}
/// <summary>对数组升序排序。</summary>
/// <exception cref="NullReferenceException"></exception>
public static List<T> Ascend<T>(List<T> list) where T : IComparable<T>
{
if (list == null) return null;
list.Sort((a, b) => a.CompareTo(b));
return list;
}
public static bool Add<TKey, TValue>(IList<KeyValuePair<TKey, TValue>> list, TKey key, TValue value)
/// <summary>对数组升序排序。</summary>
/// <exception cref="NullReferenceException"></exception>
public static void Ascend<T, TProp>(T[] array, Func<T, TProp> func) where TProp : IComparable<TProp>
{
if (list == null) return false;
list.Add(new KeyValuePair<TKey, TValue>(key, value));
return true;
if (array != null && func != null) Sort(array, (a, b) => func(a).CompareTo(func(b)));
}
internal static Dictionary<string, string[]> Dict(NameValueCollection collection)
/// <summary>对数组降序排序。</summary>
/// <exception cref="NullReferenceException"></exception>
public static T[] Descend<T>(T[] array) where T : IComparable<T>
{
if (collection == null) return null;
var count = collection.Count;
var dict = new Dictionary<string, string[]>();
for (var i = 0; i < count; i++)
if (array == null) return array;
Sort(array, (a, b) => 0 - a.CompareTo(b));
return array;
}
/// <summary>对数组降序排序。</summary>
/// <exception cref="NullReferenceException"></exception>
public static List<T> Descend<T>(List<T> list) where T : IComparable<T>
{
var key = collection.GetKey(i);
var values = collection.GetValues(i);
dict.Add(key, values);
if (list == null) return null;
list.Sort((a, b) => -a.CompareTo(b));
return list;
}
/// <summary>对数组降序排序。</summary>
/// <exception cref="NullReferenceException"></exception>
public static void Descend<T, TProp>(T[] array, Func<T, TProp> func) where TProp : IComparable<TProp>
{
if (array != null && func != null) Sort(array, (a, b) => 0 - func(a).CompareTo(func(b)));
}
/// <summary>对字典中的键排序。</summary>
public static Dictionary<TKey, TValue> SortKey<TKey, TValue>(Dictionary<TKey, TValue> dict, Func<TKey, TKey, int> comparison)
{
if (dict == null) return null;
if (comparison == null) return null;
var list = new List<KeyValuePair<TKey, TValue>>(dict);
list.Sort(new Comparison<KeyValuePair<TKey, TValue>>((a, b) => comparison(a.Key, b.Key)));
dict.Clear();
foreach (var item in list) dict.Add(item.Key, item.Value);
return dict;
}
/// <summary>对字典中的键排序。</summary>
public static Dictionary<TKey, TValue> SortKey<TKey, TValue>(Dictionary<TKey, TValue> @this) where TKey : IComparable<TKey>
{
return SortKey(@this, (a, b) => a.CompareTo(b));
}
/// <summary>对字典中的值排序。</summary>
public static Dictionary<TKey, TValue> SortValue<TKey, TValue>(Dictionary<TKey, TValue> dict, Func<TValue, TValue, int> comparison)
{
if (dict == null) return null;
if (comparison == null) return dict;
var list = new List<KeyValuePair<TKey, TValue>>(dict);
list.Sort(new Comparison<KeyValuePair<TKey, TValue>>((a, b) => comparison(a.Value, b.Value)));
dict.Clear();
foreach (var item in list) dict.Add(item.Key, item.Value);
return dict;
}
public static object[] ParseParams(object cells)
/// <summary>对字典中的值排序。</summary>
public static Dictionary<TKey, TValue> SortValue<TKey, TValue>(Dictionary<TKey, TValue> @this) where TValue : IComparable<TValue>
{
return SortValue(@this, (a, b) => a.CompareTo(b));
}
#endregion
#region params
internal static object[] ParseParams(object cells)
{
var parsed = new ArrayBuilder<object>();
ParseParams(cells, parsed, 1);
@ -407,6 +575,8 @@ namespace Apewer.Internals
parsed.Add(cells.ToString());
}
#endregion
}
}

23
Apewer/Externals/Newtonsoft.Json-11.0.1/Utilities/LinqBridge.cs

@ -3030,29 +3030,6 @@ namespace Newtonsoft.Json.Utilities.LinqBridge
}
}
namespace Newtonsoft.Json.Serialization
{
#pragma warning disable 1591
internal delegate TResult Func<TResult>();
internal delegate TResult Func<T, TResult>(T a);
internal delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);
internal delegate TResult Func<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3);
internal delegate TResult Func<T1, T2, T3, T4, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
internal delegate void Action();
internal delegate void Action<T1, T2>(T1 arg1, T2 arg2);
internal delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);
internal delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
#pragma warning restore 1591
}
namespace System.Runtime.CompilerServices
{
/// <remarks>

36
Apewer/Externals/System/Action.cs

@ -0,0 +1,36 @@
#if NET20
using System;
using System.Collections.Generic;
using System.Text;
namespace System
{
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action();
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2>(T1 arg1, T2 arg2);
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2, T3, T4, T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2, T3, T4, T5, T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2, T3, T4, T5, T6, T7>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
}
#endif

18
Apewer/Externals/System/EventHandler.cs

@ -0,0 +1,18 @@
#if NET20
using System;
using System.Collections.Generic;
using System.Text;
namespace System
{
// /// <summary>表示当事件提供数据时将处理该事件的方法。</summary>
// /// <typeparam name="TEventArgs">事件生成的事件数据的类型。</typeparam>
// /// <param name="sender">事件源。</param>
// /// <param name="e">包含事件数据的对象。</param>
// public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
}
#endif

33
Apewer/Externals/System/Func.cs

@ -0,0 +1,33 @@
#if NET20
using System;
using System.Collections.Generic;
using System.Text;
namespace System
{
/// <summary>封装一个方法,该方法不具有参数,且返回由 TResult 参数指定的类型的值。</summary>
/// <typeparam name="TResult">此委托封装的方法的返回值类型。</typeparam>
/// <returns>此委托封装的方法的返回值。</returns>
public delegate TResult Func<out TResult>();
/// <summary>封装一个方法,该方法具有一个参数,且返回由 TResult 参数指定的类型的值。</summary>
/// <typeparam name="T">此委托封装的方法的参数类型。</typeparam>
/// <typeparam name="TResult">此委托封装的方法的返回值类型。</typeparam>
/// <param name="arg">此委托封装的方法的参数。</param>
/// <returns>此委托封装的方法的返回值。</returns>
public delegate TResult Func<in T, out TResult>(T arg);
/// <summary>封装一个方法,该方法具有两个参数,并返回由 TResult 参数指定的类型的值。</summary>
/// <typeparam name="T1">此委托封装的方法的第一个参数的类型。</typeparam>
/// <typeparam name="T2">此委托封装的方法的第二个参数的类型。</typeparam>
/// <typeparam name="TResult">此委托封装的方法的返回值类型。</typeparam>
/// <param name="arg1">此委托封装的方法的第一个参数。</param>
/// <param name="arg2">此委托封装的方法的第二个参数。</param>
/// <returns>此委托封装的方法的返回值。</returns>
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
}
#endif

42
Apewer/Externals/System/Linq/Enumerable.cs

@ -0,0 +1,42 @@
#if NET20
using System;
using System.Collections.Generic;
using System.Text;
namespace System.Linq
{
/// <summary></summary>
public static class Enumerable
{
/// <summary></summary>
public static List<T> ToList<T>(this IEnumerable<T> items)
{
var list = new List<T>();
foreach (var item in list) list.Add(item);
return list;
}
/// <summary></summary>
public static T[] ToArray<T>(this IEnumerable<T> items)
{
return ToList(items).ToArray();
}
/// <summary></summary>
public static List<TResult> Select<TSource, TResult>(this IEnumerable<TSource> items, Func<TSource, TResult> selector)
{
if (items == null) return new List<TResult>();
var list = new List<TResult>();
foreach (var item in items) list.Add(selector.Invoke(item));
return list;
}
}
}
#endif

2
Apewer/Json.cs

@ -1461,7 +1461,7 @@ namespace Apewer
else if (entity is IDictionary<string, object> asExpando) { return From(new Dictionary<string, object>(asExpando), lower, depth, force); }
else if (entity is IDictionary) { return From(entity as IDictionary, lower, depth, force); }
else if (entity is IList) { return From(entity as IList, lower, depth, force); }
else if (entity is NameValueCollection) { return From(CollectionHelper.Dict(entity as NameValueCollection), lower, depth, force); }
else if (entity is NameValueCollection nc) { return From(CollectionUtility.Dictionary(nc), lower, depth, force); }
var type = entity.GetType();
var independent = RuntimeUtility.Contains<IndependentAttribute>(type);

121
Apewer/Network/Extension.cs

@ -1,5 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
namespace Apewer.Network
@ -35,6 +39,123 @@ namespace Apewer.Network
return MailMethods.Send(value, sender, receiver, content, title);
}
/// <summary>生成 Json 数组。</summary>
static Json ToJsonArray<T>(this IEnumerable<T> items, Func<T, Json> serializer)
{
if (items == null) return null;
var array = Apewer.Json.NewArray();
if (serializer != null)
{
foreach (var item in items)
{
if (item == null) continue;
var json = serializer.Invoke(item);
array.AddItem(json);
}
}
return array;
}
/// <summary>生成 Json 对象。</summary>
public static Json ToJson(this IPAddress address)
{
if (address == null) return null;
var json = Json.NewObject();
json.SetProperty("text", address.ToString());
json.SetProperty("family", address.AddressFamily.ToString());
if (address.AddressFamily == AddressFamily.InterNetworkV6)
{
json.SetProperty("isLinkLocal", address.IsIPv6LinkLocal);
json.SetProperty("scopeId", address.ScopeId);
}
return json;
}
/// <summary>生成 Json 数组。</summary>
public static Json ToJson(this IEnumerable<IPAddress> addresses) => ToJsonArray(addresses, ToJson);
/// <summary>生成 Json 数组。</summary>
public static Json ToJson(this NetworkInterface @interface)
{
if (@interface == null) return null;
var item = @interface;
var json = Json.NewObject();
json.SetProperty("text", item.ToString());
json.SetProperty("description", item.Description);
json.SetProperty("id", item.Id);
json.SetProperty("isReceiveOnly", item.IsReceiveOnly);
json.SetProperty("name", item.Name);
json.SetProperty("type", item.NetworkInterfaceType.ToString());
json.SetProperty("operational", item.OperationalStatus.ToString());
json.SetProperty("speed", item.Speed);
json.SetProperty("multicast", item.SupportsMulticast);
json.SetProperty("mac", item.GetPhysicalAddress().ToString());
json.SetProperty("ipProperties", ToJson(item.GetIPProperties()));
return json;
}
/// <summary>生成 Json 数组。</summary>
public static Json ToJson(this IEnumerable<NetworkInterface> items) => ToJsonArray(items, ToJson);
/// <summary>生成 Json 对象。</summary>
public static Json ToJson(this IPInterfaceProperties properties)
{
if (properties == null) return null;
var json = Json.NewObject();
json.SetProperty("anycast", ToJson(properties.AnycastAddresses.Select(x => x.Address)));
json.SetProperty("dhcp", ToJson(properties.DhcpServerAddresses));
json.SetProperty("dns", ToJson(properties.DnsAddresses));
json.SetProperty("suffix", properties.DnsSuffix);
json.SetProperty("gateway", ToJson(properties.GatewayAddresses.Select(x => x.Address)));
json.SetProperty("multicast", ToJson(properties.MulticastAddresses.Select(x => x.Address)));
json.SetProperty("unicast", ToJson(properties.UnicastAddresses.Select(x => x.Address)));
json.SetProperty("wins", ToJson(properties.WinsServersAddresses));
return json;
}
/// <summary>生成 Json 对象。</summary>
public static Json ToJson(this IPAddressInformation information)
{
if (information == null) return null;
var json = Json.NewObject();
json.SetProperty("address", ToJson(information.Address));
json.SetProperty("isDnsEligible", information.IsDnsEligible);
json.SetProperty("isTransient", information.IsTransient);
return json;
}
/// <summary>生成 Json 对象。</summary>
public static Json ToJson(this PingReply reply)
{
if (reply == null) return null;
var buffer = reply.Buffer.X2();
if (buffer.Replace("0", "").IsEmpty()) buffer = null;
var json = Json.NewObject();
json.SetProperty("address", ToJson(reply.Address));
if (buffer != null) json.SetProperty("buffer", buffer);
json.SetProperty("options", ToJson(reply.Options));
json.SetProperty("roundtripTime", reply.RoundtripTime);
json.SetProperty("status", reply.Status.ToString());
return json;
}
/// <summary>生成 Json 对象。</summary>
public static Json ToJson(this PingOptions options)
{
if (options == null) return null;
var json = Json.NewObject();
json.SetProperty("dontFragment", options.DontFragment);
json.SetProperty("ttl", options.Ttl);
return json;
}
}
}

6
Apewer/Network/HttpClient.cs

@ -74,7 +74,8 @@ namespace Apewer.Network
/// <remarks>默认值:False</remarks>
public bool AllowRedirect { get; set; }
/// <summary>获取或设置要写入响应主体的流。</summary>
/// <summary>获取或设置要接收响应主体的流。默认为 NULL 值。</summary>
/// <remarks>指定为 NULL 时,响应体将写入字节数组;<br />非 NULL 时,响应体将写入此流,并忽略 ResponseData 属性。</remarks>
public Stream ResponseStream { get; set; }
/// <summary>获取或设置读取响应主体的进度回调</summary>
@ -287,8 +288,9 @@ namespace Apewer.Network
foreach (var key in response.Headers.AllKeys)
{
var values = response.Headers.GetValues(key);
foreach (var value in values) headers.Add(key, value);
headers.Add(key, values.Join(","));
}
ResponseHeaders = headers;
var cb = new ArrayBuilder<Cookie>();
foreach (var item in response.Cookies)

63
Apewer/Network/Icmp.cs

@ -1,63 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.NetworkInformation;
using Apewer;
namespace Apewer.Network
{
/// <summary>ICMP。</summary>
public class Icmp
{
/// <summary>发送 PING 命令,命令中包含 32 位零数据。</summary>
/// <param name="ip">目标地址。</param>
/// <param name="timeout">等待响应的超时时间(毫秒)。</param>
/// <param name="ttl">命令的起始 TTL 值(在丢弃数据之前可以转发该数据的路由节点数)。</param>
/// <param name="df">是否分段。</param>
/// <returns>命令的返回结果。</returns>
public static Icmp Ping(string ip, int timeout = 1000, byte ttl = 255, bool df = true)
{
var icmp = new Icmp();
if (!string.IsNullOrEmpty(ip) && (timeout > 0))
{
var vip = NetworkUtility.IsIP(ip) ? ip : NetworkUtility.Resolve(ip);
if (vip.Contains(",")) vip = vip.Split(',')[0];
var op = new Ping();
var oo = new PingOptions();
oo.DontFragment = df;
oo.Ttl = ttl;
byte[] bs = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
var or = op.Send((string)vip, NumberUtility.Restrict(timeout, 1, ushort.MaxValue), bs, oo);
icmp._success = (or.Status == IPStatus.Success) ? true : false;
icmp._address = or.Address?.ToString();
icmp._time = or.RoundtripTime;
icmp._ttl = or.Options == null ? -1 : or.Options.Ttl;
}
return icmp;
}
private bool _success = false;
private string _address = "";
private long _time = 0;
private int _ttl = 0;
/// <summary>构造函数。</summary>
public Icmp() { }
/// <summary>已成功获取目标的返回。</summary>
public bool Success { get { return _success; } }
/// <summary>返回的目标地址。</summary>
public string Addresss { get { return _address; } }
/// <summary>收到返回所经历的时间(毫秒)。</summary>
public long Time { get { return _time; } }
/// <summary>返回的 TTL 值(在丢弃数据之前可以转发该数据的路由节点数)。</summary>
public int Ttl { get { return _ttl; } }
}
}

31
Apewer/NetworkUtility.cs

@ -296,6 +296,37 @@ namespace Apewer
#endregion
#region ICMP
/// <summary>发送 PING 命令,命令中包含 32 位零数据。</summary>
/// <param name="address">目标地址。</param>
/// <param name="timeout">等待响应的超时时间(毫秒)。</param>
/// <param name="ttl">命令的起始 TTL 值(在丢弃数据之前可以转发该数据的路由节点数)。</param>
/// <param name="df">是否分段。</param>
/// <returns>命令的返回结果。</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
/// <exception cref="PingException"></exception>
public static PingReply Ping(string address, int timeout = 1000, byte ttl = 255, bool df = true)
{
if (string.IsNullOrEmpty(address)) throw new ArgumentNullException(nameof(address));
if (timeout < 1) throw new ArgumentOutOfRangeException(nameof(timeout));
var ip = IsIP(address) ? address : Resolve(address);
if (ip.Contains(",")) ip = ip.Split(',')[0];
var options = new PingOptions();
options.DontFragment = df;
options.Ttl = ttl;
var buffer = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
var ping = new Ping();
var reply = ping.Send(ip, timeout, buffer, options);
return reply;
}
#endregion
#region HTTP
/// <summary>解析 HTTP 方法。</summary>

128
Apewer/Result.cs

@ -1,128 +0,0 @@
using Apewer.Internals;
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer
{
/// <summary>结果状态,Code 为零时表示正常,Code 0 与 NULL 相等。</summary>
[Serializable]
public class Result
{
private int _code;
private string _message;
/// <summary>代码。</summary>
public int Code { get => _code; protected set => _code = value; }
/// <summary>消息。</summary>
public string Message { get => _message; protected set => _message = value; }
/// <summary>创建实例:Code = 0,Message = NULL。</summary>
public Result() { }
/// <summary>创建实例。</summary>
public Result(int code, string message = null)
{
_code = code;
_message = message;
}
/// <summary>创建实例:Code = 0。</summary>
public Result(string message, int code = 0)
{
_code = code;
_message = message;
}
/// <summary></summary>
public override string ToString()
{
if (string.IsNullOrEmpty(_message)) return Code.ToString();
if (Code == 0) return _message;
return Code.ToString() + "|" + _message;
}
#region 运算符。
/// <summary>获取 Code。</summary>
public static implicit operator int(Result result) => result == null ? 0 : result._code;
/// <summary>ToString</summary>
public static implicit operator string(Result result) => result == null ? null : result.ToString();
#endregion
}
/// <summary>装箱返回结果,T 不适用于 System.String。</summary>
[Serializable]
public class Result<T> : Result, IDisposable
{
private T _value;
private bool _has = false;
/// <summary>对象。</summary>
public T Value { get => _value; set => Set(value); }
/// <summary>含有实体对象。</summary>
public bool HasValue { get => _has; }
/// <summary>执行与释放或重置非托管资源关联的应用程序定义的任务。</summary>
public void Dispose() => RuntimeUtility.Dispose(Value);
/// <summary>创建实例:Code = 0,Message = NULL,Value = Default。</summary>
public Result() { }
/// <summary>创建实例:Code = 0,Message = NULL。</summary>
public Result(T value) => Set(value);
/// <summary>创建实例:Value = Default。</summary>
public Result(string message, int code = 0) : base(message, code) { }
/// <summary>创建实例:Value = Default。</summary>
public Result(int code, string message = null) : base(code, message) { }
/// <summary>创建实例:Value = Default。</summary>
public Result(Exception exception, int code = 0) : base(RuntimeUtility.Message(exception), code) { }
/// <summary>创建实例:Value = Default。</summary>
public Result(int code, Exception exception = null) : base(code, RuntimeUtility.Message(exception)) { }
private void Set(T value)
{
_value = value;
_has = typeof(T).IsValueType ? true : (value != null);
}
#region 运算符。
/// <summary>含有实体对象。</summary>
public static implicit operator bool(Result<T> result) => result == null ? false : result._has;
#endregion
#region 扩展方法。
internal static Result<TDst> As<TSrc, TDst>(Result<TSrc> source) where TDst : TSrc
{
if (source == null) return null;
var destination = new Result<TDst>();
destination.Code = source.Code;
destination.Message = source.Message;
if (source._has)
{
var maybe = source._value is TDst;
try { destination.Set((TDst)source._value); } catch { }
}
return destination;
}
#endregion
}
}

27
Apewer/RuntimeUtility.cs

@ -372,7 +372,11 @@ namespace Apewer
public static bool IsInherits(Type child, Type @base)
{
// 检查参数。
if (child == null || @base == null || child == @base) return false;
if (child == null || @base == null) return false;
if (child == @base) return false;
// 检查 interface 类型。
if (@base.IsInterface) return @base.IsAssignableFrom(child);
// 忽略 System.Object。
var quantum = typeof(object);
@ -463,6 +467,27 @@ namespace Apewer
return list.ToArray();
}
/// <summary>是匿名类型。</summary>
/// <exception cref="ArgumentNullException"></exception>
public static bool IsAnonymousType(Type type)
{
if (type == null) throw new ArgumentNullException(nameof(type));
// 类型是由编译器生成。
if (!Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false)) return false;
// 是泛型。
if (!type.IsGenericType) return false;
// 名称。
if (!type.Name.StartsWith("<>") || !type.Name.Contains("AnonymousType")) return false;
// 私有。
if (type.IsPublic) return false;
return true;
}
#endregion
#region Collect & Dispose

14
Apewer/Source/ColumnAttribute.cs

@ -116,17 +116,13 @@ namespace Apewer.Source
}
/// <summary>从 <see cref="ColumnAttribute"/> 到 Boolean 的隐式转换,判断 <see cref="ColumnAttribute"/> 有效。</summary>
public static implicit operator bool(ColumnAttribute instance)
{
if (instance == null) return false;
return true;
}
public static implicit operator bool(ColumnAttribute instance) => instance != null;
/// <summary>解析列特性。</summary>
/// <remarks>注意:此方法不再抛出异常,当不存在正确的列特性时将返回 NULL 值</remarks>
public static ColumnAttribute Parse(Type type, PropertyInfo property, TableAttribute ta)
public static ColumnAttribute Parse(PropertyInfo property, bool force = false)
{
if (type == null || property == null || ta == null) return null;
if (property == null) return null;
// 属性带有 Independent 特性。
if (property.Contains<IndependentAttribute>()) return null;
@ -137,7 +133,7 @@ namespace Apewer.Source
var cas = property.GetCustomAttributes(typeof(ColumnAttribute), false);
if (cas.LongLength < 1L)
{
if (!ta.AllProperties) return null;
if (!force) return null;
ca = new ColumnAttribute();
}
else ca = (ColumnAttribute)cas[0];
@ -249,6 +245,8 @@ namespace Apewer.Source
return sorted.ToArray();
}
internal void SetPrimaryKey() => _primarykey = true;
}
}

39
Apewer/Source/IDbOrm.cs

@ -34,12 +34,20 @@ namespace Apewer.Source
/// <param name="model">目标记录的类型。</param>
/// <param name="sql">要执行的 SQL 语句。</param>
/// <param name="parameters">为 SQL 命令提供参数。</param>
public Result<object[]> Query(Type model, string sql, IEnumerable<IDataParameter> parameters = null);
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public object[] Query(Type model, string sql, IEnumerable<IDataParameter> parameters = null);
/// <summary>使用指定语句查询,获取查询结果。</summary>
/// <param name="sql">要执行的 SQL 语句。</param>
/// <param name="parameters">为 SQL 命令提供参数。</param>
public Result<T[]> Query<T>(string sql, IEnumerable<IDataParameter> parameters = null) where T : class, new();
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public T[] Query<T>(string sql, IEnumerable<IDataParameter> parameters = null) where T : class, new();
#endregion
@ -55,31 +63,46 @@ namespace Apewer.Source
/// <summary>获取指定类型的主键,按 Flag 属性筛选。</summary>
/// <param name="model">要查询的类型。</param>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<string[]> Keys(Type model, long flag = 0);
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public string[] Keys(Type model, long flag = 0);
/// <summary>获取指定类型的主键,按 Flag 属性筛选。</summary>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<string[]> Keys<T>(long flag = 0) where T : class, IRecord, new();
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public string[] Keys<T>(long flag = 0) where T : class, IRecord, new();
/// <summary>获取具有指定 Key 的记录,并要求记录具有指定的 Flag 属性。</summary>
/// <param name="model">目标记录的类型。</param>
/// <param name="key">目标记录的主键。</param>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<object> Record(Type model, string key, long flag = 0);
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public object Get(Type model, string key, long flag = 0);
/// <summary>获取具有指定 Key 的记录,并要求记录具有指定的 Flag 属性。</summary>
/// <param name="key">目标记录的主键。</param>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<T> Record<T>(string key, long flag = 0) where T : class, IRecord, new();
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public T Get<T>(string key, long flag = 0) where T : class, IRecord, new();
/// <summary>查询所有记录。</summary>
/// <param name="model">目标记录的类型。</param>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<object[]> Records(Type model, long flag = 0);
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public object[] List(Type model, long flag = 0);
/// <summary>查询所有记录。</summary>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<T[]> Records<T>(long flag = 0) where T : class, IRecord, new();
/// <exception cref="ModelException"></exception>
/// <exception cref="SqlException"></exception>
public T[] List<T>(long flag = 0) where T : class, IRecord, new();
#endregion

11
Apewer/Source/IRecordPrimaryKey.cs

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Source
{
/// <summary>此记录的 Key 属性作为主键。</summary>
public interface IRecordPrimaryKey : IRecord { }
}

89
Apewer/Source/IndexAttribute.cs

@ -1,89 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Source
{
/// <summary>表示此表拥有索引,此特性不被继承。</summary>
[Serializable]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public sealed class IndexAttribute : Attribute
{
string _name = null;
string _sql = null;
/// <summary>索引名称。</summary>
public string Name { get; }
/// <summary>用于创建索引的 SQL 语句。</summary>
public string SqlStatement { get; set; }
/// <summary>声明索引。</summary>
/// <param name="name">索引名称。</param>
/// <param name="sqlStatement">用于创建此索引的 SQL 语句。</param>
public IndexAttribute(string name, string sqlStatement)
{
_name = name.ToTrim();
_sql = sqlStatement.ToTrim();
}
/// <summary>从 <see cref="IndexAttribute"/> 到 Boolean 的隐式转换,判断 <see cref="IndexAttribute"/> 有效。</summary>
public static implicit operator bool(IndexAttribute instance)
{
if (instance == null) return false;
if (instance._name.IsEmpty()) return false;
if (instance._sql.IsEmpty()) return false;
return true;
}
#region Parse & Cache
private static Dictionary<string, IndexAttribute[]> _cache = new Dictionary<string, IndexAttribute[]>();
/// <summary>解析索引特性,默认使用缓存以提升性能。</summary>
public static IndexAttribute[] Parse<T>(bool useCache = true) where T : class, new() => Parse(typeof(T), useCache);
/// <summary>解析索引特性,默认使用缓存以提升性能。</summary>
public static IndexAttribute[] Parse(Type type, bool useCache = true)
{
if (type == null) return new IndexAttribute[0];
var cacheKey = type.FullName;
if (useCache)
{
lock (_cache)
{
IndexAttribute[] cached;
if (_cache.TryGetValue(cacheKey, out cached)) return cached;
}
}
var attributes = type.GetCustomAttributes(typeof(IndexAttribute), false);
var list = new List<IndexAttribute>();
foreach (var attribute in attributes)
{
var ia = attribute as IndexAttribute;
if (!ia) continue;
list.Add(ia);
}
var items = list.ToArray();
if (useCache)
{
lock (_cache)
{
if (_cache.ContainsKey(cacheKey)) _cache[cacheKey] = items;
else _cache.Add(cacheKey, items);
}
}
return items;
}
#endregion
}
}

26
Apewer/Source/KeyRecord.cs

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Source
{
/// <summary>数据库记录通用字段模型,此模型中的 Key 属性带有主键特性。</summary>
/// <remarks>带有 Independent 特性的模型不包含此类型声明的属性。</remarks>
[Serializable]
public abstract class KeyRecord : Record
{
/// <summary>记录主键,一般使用 GUID 的字符串形式。</summary>
/// <remarks>
/// <para>注:</para>
/// <para>1. 默认长度为 32,需要修改长度时应该重写此属性;</para>
/// <para>2. 带有 Independent 特性的模型不包含此属性。</para>
/// </remarks>
[PrimaryKey]
[Column("_key", ColumnType.NVarChar, 32)]
public override string Key { get => base.Key; set => base.Key = value; }
}
}

99
Apewer/Source/ModelException.cs

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Source
{
/// <summary>表示在解析数据模型过程中发生的错误。</summary>
[Serializable]
public class ModelException : Exception
{
const string EmptyMessage = "(无消息)";
string _msg = null;
Type _model = null;
/// <summary>获取描述当前异常的消息。</summary>
public override string Message { get => _msg; }
/// <summary>数据模型的类型。</summary>
public Type Model { get => _model; }
/// <summary>初始化 <see cref="ModelException"/> 类的新实例。</summary>
/// <param name="message">描述当前异常的消息。</param>
/// <param name="model">数据模型的类型。</param>
public ModelException(string message, Type model = null)
{
_msg = string.IsNullOrEmpty(message) ? EmptyMessage : message;
_model = model;
}
/// <summary>表示数据模型类型无效的异常实例。</summary>
public static ArgumentNullException InvalidType() => new ArgumentNullException("数据模型的类型无效。");
/// <summary>表示数据模型结构无效的异常实例。</summary>
/// <param name="model">数据模型。</param>
public static ModelException InvalidStructure(Type model)
{
if (model == null) return new ModelException("数据模型的类型无效。");
return new ModelException($"类型 <{model.Name}> 的数据模型结构无效。");
}
/// <summary>表示表名称无效的异常实例。</summary>
/// <param name="model">数据模型。</param>
public static ModelException InvalidTableName(Type model)
{
if (model == null) return new ModelException("数据模型的类型无效。");
return new ModelException($"类型 <{model.Name}> 不包含表名称。");
}
/// <summary>表示丢失了 Key 字段的异常实例。</summary>
/// <param name="model">数据模型。</param>
public static ModelException MissingKey(Type model)
{
if (model == null) return new ModelException("数据模型的类型无效。");
return new ModelException($"类型 <{model.Name}> 不包含 Key 属性的字段。");
}
/// <summary>表示丢失了 Flag 字段的异常实例。</summary>
/// <param name="model">数据模型。</param>
public static ModelException MissingFlag(Type model)
{
if (model == null) return new ModelException("数据模型的类型无效。");
return new ModelException($"类型 <{model.Name}> 不包含 Flag 属性的字段。");
}
/// <summary>表示表名称无效的异常实例。</summary>
public static ModelException InvalidTableName<TModel>() => InvalidTableName(typeof(TModel));
/// <summary>表示丢失了 Key 字段的异常实例。</summary>
public static ModelException MissingKey<TModel>() => MissingKey(typeof(TModel));
/// <summary>表示丢失了 Flag 字段的异常实例。</summary>
public static ModelException MissingFlag<TModel>() => MissingFlag(typeof(TModel));
}
/// <summary>表示在解析数据模型过程中发生的错误。</summary>
[Serializable]
public class ModelException<TModel> : ModelException
{
/// <summary>初始化 <see cref="ModelException"/> 类的新实例。</summary>
/// <param name="message">描述当前异常的消息。</param>
public ModelException(string message) : base(message, typeof(TModel)) { }
/// <summary>表示表名称无效的异常实例。</summary>
public static ModelException InvalidTableName() => InvalidTableName(typeof(TModel));
/// <summary>表示丢失了 Key 字段的异常实例。</summary>
public static ModelException MissingKey() => MissingKey(typeof(TModel));
/// <summary>表示丢失了 Flag 字段的异常实例。</summary>
public static ModelException MissingFlag() => MissingFlag(typeof(TModel));
}
}

418
Apewer/Source/SourceUtility.cs

@ -13,57 +13,19 @@ namespace Apewer.Source
public static class SourceUtility
{
#region As
/// <summary>转换模型类型。</summary>
public static TDst[] As<TSrc, TDst>(this TSrc[] input) where TDst : class
{
if (input == null) return null;
var count = input.Length;
var output = new TDst[count];
for (var i = 0; i < count; i++)
{
var item = input[i];
if (item == null) continue;
output[i] = item as TDst; // 此处可能抛出异常。
}
return output;
}
/// <summary>转换模型类型。</summary>
public static Result<TDst> As<TSrc, TDst>(this Result<TSrc> input) where TDst : class
{
if (input == null) return null;
if (!input.HasValue) return new Result<TDst>(input.Code, input.Message);
var value = input.Value as TDst;
if (value == null)
{
var src = input.Value.GetType().FullName;
var dst = typeof(TDst).FullName;
return new Result<TDst>($"无法将记录从转换 {src} 到 {dst}。");
}
return new Result<TDst>(value);
}
/// <summary>转换模型类型。</summary>
public static Result<TDst[]> As<TSrc, TDst>(this Result<TSrc[]> input) where TDst : class
{
if (input == null) return null;
if (!input.HasValue) return new Result<TDst[]>(input.Code, input.Message);
var count = input.Value.Length;
var output = new TDst[count];
for (var i = 0; i < count; i++) output[i] = input.Value[i] as TDst;
return new Result<TDst[]>(output);
}
#endregion
#region IQuery -> IRecord
/// <summary>读取所有行,生成列表。</summary>
public static T[] Fill<T>(this IQuery query) where T : class, new() => As<object, T>(Fill(query, typeof(T)));
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];
@ -203,172 +165,6 @@ namespace Apewer.Source
#endregion
#region IOrm
/// <summary>查询记录。</summary>
/// <param name="database">数据库对象。</param>
/// <param name="model">记录模型。</param>
/// <param name="sql">SQL 语句。</param>
/// <param name="parameters">为 SQL 命令提供参数。</param>
public static Result<object[]> Query(IDbAdo database, Type model, string sql, IEnumerable<IDataParameter> parameters)
{
if (database == null) return new Result<object[]>("数据库无效。");
if (model == null) return new Result<object[]>("模型类型无效。");
if (string.IsNullOrEmpty(sql)) return new Result<object[]>("SQL 语句无效。");
using (var query = database.Query(sql, parameters))
{
if (query == null) return new Result<object[]>("查询实例无效。");
if (query.Table == null)
{
if (!string.IsNullOrEmpty(query.Message)) return new Result<object[]>(query.Message);
return new Result<object[]>("查询实例不包含数据表。");
}
try
{
var array = Fill(query, model);
return new Result<object[]>(array);
}
catch (Exception ex)
{
return new Result<object[]>(ex);
}
}
}
// /// <summary>查询记录。</summary>
// /// <typeparam name="T">记录模型。</typeparam>
// /// <param name="database">数据库对象。</param>
// /// <param name="sql">SQL 语句。</param>
// public static Result<T[]> Query<T>(IDbClientAdo database, string sql) where T : class, new() => As<object, T>(Query(database, typeof(T), sql));
/// <summary>查询记录。</summary>
/// <param name="database">数据库对象。</param>
/// <param name="model">记录模型。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
public static Result<object[]> Query(IDbAdo database, Type model, Func<string, string> sqlGetter)
{
if (sqlGetter == null) return new Result<object[]>("SQL 语句获取函数无效。");
try
{
var tableName = TableStructure.Parse(model).TableName;
if (string.IsNullOrEmpty(tableName)) return new Result<object[]>("表名无效。");
return Query(database, model, sqlGetter(tableName), null);
}
catch (Exception ex)
{
return new Result<object[]>(ex);
}
}
/// <summary>查询记录。</summary>
/// <typeparam name="T">记录模型。</typeparam>
/// <param name="database">数据库对象。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
public static Result<T[]> Query<T>(IDbAdo database, Func<string, string> sqlGetter) where T : class, new() => As<object, T>(Query(database, typeof(T), sqlGetter));
/// <summary>获取具有指定主键的记录。</summary>
/// <param name="database">数据库对象。</param>
/// <param name="model">记录模型。</param>
/// <param name="key">主键。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名和主键值。</param>
public static Result<object> Get(IDbAdo database, Type model, string key, Func<string, string, string> sqlGetter)
{
if (sqlGetter == null) return new Result<object>("SQL 语句获取函数无效。");
var safetyKey = TextUtility.SafeKey(key);
if (string.IsNullOrEmpty(safetyKey)) return new Result<object>("主键无效。");
var query = null as IQuery;
var record = null as object;
try
{
record = Activator.CreateInstance(model);
var ts = TableStructure.Parse(model);
var tableName = ts.TableName;
if (string.IsNullOrEmpty(tableName)) return new Result<object>("表名无效。");
var sql = sqlGetter(tableName, safetyKey);
query = database.Query(sql);
if (query.Table == null) return new Result<object>("没有获取到记录。");
record = Row(query, 0, model, ts);
}
catch (Exception ex)
{
RuntimeUtility.Dispose(query);
return new Result<object>(ex);
}
RuntimeUtility.Dispose(query);
if (record == null) return new Result<object>("没有获取到记录。");
return new Result<object>(record);
}
/// <summary>获取具有指定主键的记录。</summary>
/// <typeparam name="T">记录模型。</typeparam>
/// <param name="database">数据库对象。</param>
/// <param name="key">主键。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名和主键值。</param>
public static Result<T> Get<T>(IDbAdo database, string key, Func<string, string, string> sqlGetter) where T : class, IRecord, new() => As<object, T>(Get(database, typeof(T), key, sqlGetter));
/// <summary>获取主键。</summary>
/// <param name="database">数据库对象。</param>
/// <param name="model">记录模型。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
public static Result<string[]> Keys(IDbAdo database, Type model, Func<string, string> sqlGetter)
{
if (database == null) return new Result<string[]>("数据库无效。");
if (model == null) return new Result<string[]>("模型类型无效。");
if (sqlGetter == null) return new Result<string[]>("SQL 语句获取函数无效。");
var tableStructure = null as TableStructure;
try
{
tableStructure = TableStructure.Parse(model);
}
catch (Exception ex)
{
return new Result<string[]>(ex);
}
var tableName = tableStructure.TableName;
if (string.IsNullOrEmpty(tableName)) return new Result<string[]>("表名无效。");
var sql = sqlGetter(tableName);
var query = null as IQuery;
try
{
query = database.Query(sql);
if (query == null) return new Result<string[]>("查询实例无效。");
var list = new List<string>(query.Rows);
for (var r = 0; r < query.Rows; r++)
{
var key = TextUtility.SafeKey(query.Text(r));
if (string.IsNullOrEmpty(key)) continue;
list.Add(key);
}
query.Dispose();
list.Capacity = list.Count;
var array = list.ToArray();
return new Result<string[]>(array);
}
catch (Exception ex)
{
RuntimeUtility.Dispose(query);
return new Result<string[]>(ex);
}
}
/// <summary>获取主键。</summary>
/// <typeparam name="T">记录模型。</typeparam>
/// <param name="database">数据库对象。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
public static Result<string[]> Keys<T>(IDbAdo database, Func<string, string> sqlGetter) where T : IRecord
{
return Keys(database, typeof(T), sqlGetter);
}
#endregion
#region Record
/// <summary>修复记录属性。</summary>
@ -449,72 +245,211 @@ namespace Apewer.Source
#endregion
#region DbClient
#region Query
/// <summary>简单查询:取结果中第 0 列所有单元格的文本形式,可指定查询后关闭服务器连接,返回结果中不包含无效文本。</summary>
/// <param name="source">数据库客户端。</param>
/// <param name="sql">用于查询的 SQL 语句。</param>
/// <param name="close">查询后,关闭数据库链接。</param>
public static string[] SimpleColumn(this IDbAdo source, string sql, bool close = false)
/// <exception cref="SqlException"></exception>
public static string[] Column(this IDbAdo source, string sql)
{
if (source == null) return new string[0];
var ab = new ArrayBuilder<string>();
var pool = null as string[];
var rows = 0;
var count = 0;
using (var query = source.Query(sql))
{
var rows = query.Rows;
if (rows > 0)
{
var added = 0;
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));
if (string.IsNullOrEmpty(cell)) continue;
ab.Add(cell);
added++;
}
pool[count] = cell;
count++;
}
}
if (close) RuntimeUtility.Dispose(source);
return ab.Export();
if (count < 1) return new string[0];
var array = new string[count];
Array.Copy(pool, 0, array, 0, count);
return array;
}
/// <summary>简单查询:取结果中第 0 行、第 0 列单元格中的文本,可指定查询后关闭服务器连接。</summary>
/// <param name="source">数据库客户端。</param>
/// <param name="dbClient">数据库客户端。</param>
/// <param name="sql">用于查询的 SQL 语句。</param>
/// <param name="close">查询后,关闭数据库链接。</param>
public static string SimpleCell(this IDbAdo source, string sql, bool close = false)
/// <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 (source == null) return null;
var value = null as string;
using (var query = source.Query(sql)) value = TextUtility.Trim(query.Text());
if (close) RuntimeUtility.Dispose(source);
if (!query.Success) throw new SqlException(query, sql);
var value = TextUtility.Trim(query.Text());
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 IDbClient dbClient, string sql, IEnumerable<KeyValuePair<string, object>> parameters)
{
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
return dbClient.Query(sql, Parameters(dbClient, parameters));
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 IDbClient 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);
}
}
#endregion
#region Execute
/// <summary>执行 SQL 语句,并加入参数。</summary>
/// <exception cref="ArgumentNullException"></exception>
public static IExecute Execute(this IDbClient dbClient, string sql, IEnumerable<KeyValuePair<string, object>> parameters, bool autoTransaction = false)
{
if (dbClient == null) throw new ArgumentNullException(nameof(dbClient));
return dbClient.Execute(sql, Parameters(dbClient, parameters), autoTransaction);
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 IDbClient 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);
}
}
#endregion
#region Parameter
/// <exception cref="ArgumentNullException"></exception>
static List<IDataParameter> Parameters(IDbClient dbClient, IEnumerable<KeyValuePair<string, object>> parameters)
static List<IDataParameter> ParametersByProperites(IDbClient 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.EndsWith("@")) 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>();
if (parameters != null)
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(IDbClient 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)
{
foreach (var parameter in parameters) ps.Add(dbClient.Parameter(parameter.Key, parameter.Value));
var name = kvp.Key;
if (name.IsEmpty()) continue;
// 属性不可重复。
if (!name.EndsWith("@")) 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;
}
@ -625,12 +560,13 @@ namespace Apewer.Source
if (sc < 1) return array;
// 解析模型列。
var cas = ts.Fillable;
var dc = 0;
var dfs = new string[ts.Columns.Length];
var dts = new ColumnAttribute[ts.Columns.Length];
for (var i = 0; i < ts.Columns.Length; i++)
var dfs = new string[cas.Length];
var dts = new ColumnAttribute[cas.Length];
for (var i = 0; i < cas.Length; i++)
{
var ca = ts.Columns[i];
var ca = cas[i];
var key = ca.Field.Lower();
if (string.IsNullOrEmpty(key)) continue;
if (dfs.Contains(key)) continue;

71
Apewer/Source/SqlException.cs

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Source
{
/// <summary>表示在执行 SQL 语句执行过程中发生的错误。</summary>
[Serializable]
public sealed class SqlException : Exception
{
const string EmptyMessage = "(无消息)";
string _msg = null;
string _sql = null;
/// <summary>获取描述当前异常的消息。</summary>
public override string Message { get => _msg; }
/// <summary>获取引发异常的 SQL 语句。</summary>
public string Statement { get => _sql; }
/// <summary>初始化 <see cref="SqlException"/> 类的新实例。</summary>
/// <param name="message">描述当前异常的消息。</param>
/// <param name="statement">附带 SQL 语句。</param>
public SqlException(string message, string statement = null)
{
_msg = string.IsNullOrEmpty(message) ? EmptyMessage : message;
_sql = statement;
}
/// <summary>初始化 <see cref="SqlException"/> 类的新实例。</summary>
/// <param name="query">用于获取消息的查询结果。</param>
/// <param name="statement">附带 SQL 语句。</param>
public SqlException(IQuery query, string statement = null)
{
if (query == null)
{
_msg = "查询结果实例无效。";
_sql = statement;
return;
}
_msg = query.Message;
if (string.IsNullOrEmpty(_msg)) _msg = EmptyMessage;
_sql = statement;
}
/// <summary>初始化 <see cref="SqlException"/> 类的新实例。</summary>
/// <param name="execute">用于获取消息的执行结果。</param>
/// <param name="statement">附带 SQL 语句。</param>
public SqlException(IExecute execute, string statement = null)
{
if (execute == null)
{
_msg = "执行结果实例无效。";
return;
}
_msg = execute.Message;
if (string.IsNullOrEmpty(_msg)) _msg = EmptyMessage;
_sql = statement;
}
}
}

6
Apewer/Source/TableAttribute.cs

@ -21,6 +21,7 @@ namespace Apewer.Source
private string _name = null;
private string _store = null;
private Type _model = null;
private bool _primarykey = false;
/// <summary>标记表属性。</summary>
public TableAttribute(string name = null, string store = null)
@ -38,6 +39,9 @@ namespace Apewer.Source
/// <summary>使用此特性的类型。</summary>
public Type Model { get => _model; }
/// <summary>模型实现了 <see cref="IRecordPrimaryKey"/> 接口。</summary>
public bool PrimaryKey { get => _primarykey; }
/// <summary>从 <see cref="TableAttribute"/> 到 Boolean 的隐式转换,判断 <see cref="TableAttribute"/> 有效。</summary>
public static implicit operator bool(TableAttribute instance)
{
@ -64,6 +68,7 @@ namespace Apewer.Source
#region cache
private static Dictionary<string, TableAttribute> _tac = new Dictionary<string, TableAttribute>();
private static Type InterfacePrimaryKey = typeof(IRecordPrimaryKey);
/// <summary>解析表特性,默认使用缓存以提升性能。</summary>
public static TableAttribute Parse<T>(bool useCache = true) where T : class, new() => Parse(typeof(T), useCache);
@ -103,6 +108,7 @@ namespace Apewer.Source
ta._model = type;
if (string.IsNullOrEmpty(ta.Name)) ta._name = type.Name;
ta.Independent = RuntimeUtility.Contains<IndependentAttribute>(type, true);
ta._primarykey = RuntimeUtility.IsInherits(type, InterfacePrimaryKey);
if (useCache)
{

71
Apewer/Source/TableStructure.cs

@ -21,7 +21,7 @@ namespace Apewer.Source
ColumnAttribute _key = null;
ColumnAttribute _flag = null;
ColumnAttribute[] _columns = null;
IndexAttribute[] _indexes = null;
ColumnAttribute[] _fillable = null;
private TableStructure() { }
@ -31,12 +31,12 @@ namespace Apewer.Source
/// <summary>表特性。</summary>
public TableAttribute Table { get => _table; }
/// <summary>索引。</summary>
public IndexAttribute[] Indexed { get => _indexes; }
/// <summary>列信息。</summary>
public ColumnAttribute[] Columns { get => _columns; }
/// <summary>可填充的列信息。</summary>
public ColumnAttribute[] Fillable { get => _fillable; }
/// <summary>主键。</summary>
public ColumnAttribute Key { get => _key; }
@ -87,6 +87,7 @@ namespace Apewer.Source
public static TableStructure Parse<T>(bool useCache = true, bool force = false) where T : IRecord => Parse(typeof(T), useCache, force);
/// <summary>解析表结构。</summary>
/// <returns>表结构。类型不可用于表结构时返回 NULL 值。</returns>
public static TableStructure Parse(Type model, bool useCache = true, bool force = false)
{
var type = model;
@ -94,6 +95,7 @@ namespace Apewer.Source
// 使用缓存。
var cacheKey = type.FullName;
if (force) cacheKey = "[force] " + cacheKey;
if (useCache)
{
lock (_tsc)
@ -103,36 +105,63 @@ namespace Apewer.Source
}
}
// 获取 Table Attribute。
// 解析 TableAttribute。
var ta = TableAttribute.Parse(type, useCache, force);
if (!ta && !force) return null;
// 获取索引。
var ias = IndexAttribute.Parse(type);
// 遍历所有属性。
// 类型。
var isRecord = RuntimeUtility.IsInherits(type, typeof(Record));
var properties = type.GetProperties();
var total = properties.Length;
// 解析 ColumnAttribute。
var key = null as ColumnAttribute;
var flag = null as ColumnAttribute;
var columns = new ColumnAttribute[properties.Length];
var columns = new ColumnAttribute[total];
var columnsCount = 0;
if (properties.Length > 0)
var fillable = new List<ColumnAttribute>(total);
if (total > 0)
{
var addedFields = new List<string>(properties.Length);
var caForce = force || (ta ? ta.AllProperties : false);
var addedFields = new List<string>(total);
foreach (var property in properties)
{
// 解析 ColumnAttribute,抛弃无效。
var ca = ColumnAttribute.Parse(type, property, ta);
if (ca == null) continue;
var ca = ColumnAttribute.Parse(property, caForce);
if (ca != null)
{
// 检查 field 重复,只保留第一个。
var field = ca.Field;
if (addedFields.Contains(field)) continue;
if (!addedFields.Contains(field))
{
addedFields.Add(field);
if (property.Name == "Key") key = ca;
if (property.Name == "Flag") flag = ca;
columns[columnsCount] = ca;
columnsCount += 1;
if (isRecord)
{
if (property.Name == "Key")
{
key = ca;
if (ta != null && ta.PrimaryKey) ca.SetPrimaryKey();
}
else if (property.Name == "Flag")
{
flag = ca;
}
}
}
// 可查询的列。
fillable.Add(ca);
continue;
}
// 可查询的列。
if (!caForce)
{
ca = ColumnAttribute.Parse(property, caForce);
if (ca) fillable.Add(ca);
}
}
}
if (columnsCount > 0 && columnsCount != columns.Length) columns = columns.Slice(0, columnsCount);
@ -143,11 +172,11 @@ namespace Apewer.Source
// 返回结果。
var ts = new TableStructure();
ts._table = ta;
ts._indexes = ias;
ts._key = key;
ts._flag = flag;
ts._columns = columns;
ts._model = model;
ts._fillable = fillable.ToArray();
// 加入缓存。
if (useCache)

2
Apewer/StorageUtility.cs

@ -106,6 +106,8 @@ namespace Apewer
/// <summary>无效的路径字符。</summary>
public static char[] InvalidPathChars
{
// SMB 共享文件夹无效字符
// ! " # % & ' ( ) * + , / : ; < = > ? @ [ ] \ ^ ` { } | ~
get => new char[] {
'\\', '/', '\'', '"', ':', '*', '?', '<', '>', '|',
'\0', '\a', '\b', '\t', '\n', '\v', '\f', '\r',

41
Apewer/TextUtility.cs

@ -111,10 +111,10 @@ namespace Apewer
}
/// <summary>合并为字符串。</summary>
public static string Merge(params object[] cells) => PrivateJoin(null, CollectionHelper.ParseParams(cells));
public static string Merge(params object[] cells) => PrivateJoin(null, CollectionUtility.ParseParams(cells));
/// <summary>合并为字符串。</summary>
public static string Join(string separator, params object[] cells) => PrivateJoin(separator, CollectionHelper.ParseParams(cells));
public static string Join(string separator, params object[] cells) => PrivateJoin(separator, CollectionUtility.ParseParams(cells));
/// <summary>重复指定字符,直到达到指定长度。</summary>
/// <param name="cell">要重复的字符。</param>
@ -1059,6 +1059,43 @@ namespace Apewer
return true;
}
/// <summary>解析编码名称。</summary>
/// <returns>解析失败时,返回 NULL 值。</returns>
private static Encoding ParseEncoding(string encoding)
{
if (encoding.IsEmpty()) return null;
var lower = encoding.Lower();
var nick = lower.Replace("-", "");
switch (nick)
{
case "ascii":
return Encoding.ASCII;
case "bigendia":
case "bigendianunicode":
return Encoding.BigEndianUnicode;
case "utf7":
return Encoding.UTF7;
case "utf8":
return Encoding.UTF8;
case "utf16":
case "unicode":
return Encoding.Unicode;
case "utf32":
return Encoding.UTF7;
case "default":
return Encoding.Default;
case "ansi":
case "gb2312":
case "gb18030":
return Encoding.Default;
}
return null;
}
#endregion
}

10
Apewer/Web/ApiModel.cs

@ -107,6 +107,7 @@ namespace Apewer.Web
}
_provider.ResponseBody().Write(stream);
_provider.Sent();
if (dispose) RuntimeUtility.Dispose(stream);
}
#endregion
@ -228,18 +229,19 @@ namespace Apewer.Web
var info = new FileInfo(Path);
if (string.IsNullOrEmpty(Attachment)) Attachment = info.Name;
var stream = new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.Read);
Output(stream, true);
using (var stream = new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
Output(stream, false);
}
}
catch { }
}
/// <summary></summary>
/// <exception cref="FileNotFoundException"></exception>
public ApiFileModel(string path, string name = null)
public ApiFileModel(string path)
{
SetPath(path);
Attachment = name;
}
}

4
Apewer/Web/ApiProcessor.cs

@ -214,7 +214,11 @@ namespace Apewer.Web
if (function != null)
{
// 调用 API,获取返回值。
var result = function.Method.Invoke(controller, ReadParameters(request, function));
if (response.StopReturn) return;
// 检查返回值。
if (result == null || function.Returnable == null) return;
var returnable = function.Returnable;

2
Apewer/Web/ApiResponse.cs

@ -16,6 +16,8 @@ namespace Apewer.Web
private ApiModel _model = null;
private Json _data = Json.NewObject();
internal bool StopReturn = false;
/// <summary>API 的执行时间,以毫秒为单位。</summary>
public long Duration { get; set; }

7
Apewer/Web/ApiUtility.cs

@ -756,6 +756,13 @@ namespace Apewer.Web
return json;
}
/// <summary>停止 Invoker 对返回值的处理。</summary>
public static void StopReturn(ApiResponse response)
{
if (response == null) return;
response.StopReturn = true;
}
#endregion
#region ApiModel

71
Apewer/_Delegates.cs

@ -5,22 +5,6 @@ using System.IO;
namespace Apewer
{
// /// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
// public delegate void Action();
// /// <summary>表示当事件提供数据时将处理该事件的方法。</summary>
// /// <typeparam name="TEventArgs">事件生成的事件数据的类型。</typeparam>
// /// <param name="sender">事件源。</param>
// /// <param name="e">包含事件数据的对象。</param>
// public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
// /// <summary>封装一个方法,该方法具有一个参数,且返回由 TResult 参数指定的类型的值。</summary>
// /// <typeparam name="T">此委托封装的方法的参数类型。</typeparam>
// /// <typeparam name="TResult">此委托封装的方法的返回值类型。</typeparam>
// /// <param name="arg">此委托封装的方法的参数。</param>
// /// <returns>此委托封装的方法的返回值。</returns>
// public delegate TResult Func<in T, out TResult>(T arg);
/// <summary></summary>
public delegate void Event(object sender);
@ -65,59 +49,4 @@ namespace Apewer
/// <param name="days">日志文件距离今天的天数,例:昨日为 1。</param>
public delegate void LogCollector(string path, int days);
#if NET20
/// <summary>封装一个方法,该方法不具有参数,且返回由 TResult 参数指定的类型的值。</summary>
/// <typeparam name="TResult">此委托封装的方法的返回值类型。</typeparam>
/// <returns>此委托封装的方法的返回值。</returns>
public delegate TResult Func<out TResult>();
/// <summary>封装一个方法,该方法具有一个参数,且返回由 TResult 参数指定的类型的值。</summary>
/// <typeparam name="T">此委托封装的方法的参数类型。</typeparam>
/// <typeparam name="TResult">此委托封装的方法的返回值类型。</typeparam>
/// <param name="arg">此委托封装的方法的参数。</param>
/// <returns>此委托封装的方法的返回值。</returns>
public delegate TResult Func<in T, out TResult>(T arg);
/// <summary>封装一个方法,该方法具有两个参数,并返回由 TResult 参数指定的类型的值。</summary>
/// <typeparam name="T1">此委托封装的方法的第一个参数的类型。</typeparam>
/// <typeparam name="T2">此委托封装的方法的第二个参数的类型。</typeparam>
/// <typeparam name="TResult">此委托封装的方法的返回值类型。</typeparam>
/// <param name="arg1">此委托封装的方法的第一个参数。</param>
/// <param name="arg2">此委托封装的方法的第二个参数。</param>
/// <returns>此委托封装的方法的返回值。</returns>
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action();
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2>();
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2, T3>();
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2, T3, T4>();
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2, T3, T4, T5>();
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2, T3, T4, T5, T6>();
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2, T3, T4, T5, T6, T7>();
/// <summary>封装一个方法,该方法不具有参数且不返回值。</summary>
public delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8>();
/// <summary>表示当事件提供数据时将处理该事件的方法。</summary>
/// <typeparam name="TEventArgs">事件生成的事件数据的类型。</typeparam>
/// <param name="sender">事件源。</param>
/// <param name="e">包含事件数据的对象。</param>
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
#endif
}

51
Apewer/_Extensions.cs

@ -5,12 +5,12 @@ using Apewer.Web;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Collections.Specialized;
#if !NET20
using System.Dynamic;
@ -66,7 +66,7 @@ public static class Extensions
/// <param name="obj">要设置属性的对象。</param>
/// <param name="value">属性值。</param>
/// <exception cref="ArgumentNullException"></exception>
public static void SetValue(this PropertyInfo property , object obj, object value)
public static void SetValue(this PropertyInfo property, object obj, object value)
{
if (property == null) throw new ArgumentNullException(nameof(property));
property.SetValue(obj, value, null);
@ -278,7 +278,7 @@ public static class Extensions
/// <summary>从毫秒时间戳获取 DateTime 对象。发生异常且不允许异常时将返回 1970-01-01 00:00:00.000。</summary>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static DateTime DateTime(this long stamp, bool throwException = true) => ClockUtility.FromStamp(stamp, throwException);
public static DateTime DateTime(this long stamp, DateTimeKind kind = DateTimeKind.Unspecified, bool throwException = true) => ClockUtility.FromStamp(stamp, kind, throwException);
#endregion
@ -437,55 +437,55 @@ public static class Extensions
}
/// <summary>添加元素。</summary>
public static bool Add<TKey, TValue>(this IList<KeyValuePair<TKey, TValue>> @this, TKey key, TValue value) => CollectionHelper.Add<TKey, TValue>(@this, key, value);
public static bool Add<TKey, TValue>(this IList<KeyValuePair<TKey, TValue>> @this, TKey key, TValue value) => CollectionUtility.Add<TKey, TValue>(@this, key, value);
/// <summary>判断集合为空。</summary>
public static bool IsEmpty<T>(this IEnumerable<T> @this) => CollectionHelper.IsEmpty(@this);
public static bool IsEmpty<T>(this IEnumerable<T> @this) => CollectionUtility.IsEmpty(@this);
/// <summary>判断集合存在元素。</summary>
public static bool NotEmpty<T>(this IEnumerable<T> @this) => CollectionHelper.NotEmpty(@this);
public static bool NotEmpty<T>(this IEnumerable<T> @this) => CollectionUtility.NotEmpty(@this);
/// <summary>检查集合是否包含 item。</summary>
public static bool Contains<T>(this IEnumerable<T> @this, T item) => CollectionHelper.Contains(@this, item);
public static bool Contains<T>(this IEnumerable<T> @this, T item) => CollectionUtility.Contains(@this, item);
/// <summary>获取 item 在集合中的偏移位置,不存在时返回 -1。</summary>
public static int IndexOf<T>(this IEnumerable<T> objects, T item) => CollectionHelper.IndexOf(objects, item);
public static int IndexOf<T>(this IEnumerable<T> objects, T item) => CollectionUtility.IndexOf(objects, item);
/// <summary>获取集合中元素的数量。</summary>
public static int Count<T>(this IEnumerable<T> @this) => CollectionHelper.Count(@this);
public static int Count<T>(this IEnumerable<T> @this) => CollectionUtility.Count(@this);
/// <summary>对元素去重,且去除 NULL 值。</summary>
public static T[] Distinct<T>(this IEnumerable<T> @this) => CollectionHelper.Distinct(@this);
public static T[] Distinct<T>(this IEnumerable<T> @this) => CollectionUtility.Distinct(@this);
/// <summary>获取可枚举集合的部分元素。</summary>
public static T[] Slice<T>(this IEnumerable<T> @this, int start = 0, int count = -1, Func<T> stuffer = null) => CollectionHelper.Slice<T>(@this, start, count, stuffer);
public static T[] Slice<T>(this IEnumerable<T> @this, int start = 0, int count = -1, Func<T> stuffer = null) => CollectionUtility.Slice<T>(@this, start, count, stuffer);
/// <summary>安全转换为 List&lt;<typeparamref name="T"/>&gt; 对象。可指定排除 NULL 值元素。</summary>
public static List<T> List<T>(this IEnumerable<T> @this, bool excludeNull = false) => CollectionHelper.ToList<T>(@this, excludeNull);
public static List<T> List<T>(this IEnumerable<T> @this, bool excludeNull = false) => CollectionUtility.List<T>(@this, excludeNull);
/// <summary>安全转换为 &lt;<typeparamref name="T"/>&gt;[] 对象。可指定排除 NULL 值元素。</summary>
public static T[] Array<T>(IEnumerable<T> @this, bool excludeNull = false) => CollectionHelper.ToArray<T>(@this, excludeNull);
public static T[] Array<T>(IEnumerable<T> @this, bool excludeNull = false) => CollectionUtility.Array<T>(@this, excludeNull);
/// <summary>对列表中的元素排序。</summary>
public static List<T> Sort<T>(this List<T> @this, Func<T, T, int> comparison) => CollectionHelper.Sort(@this, comparison);
public static List<T> Sort<T>(this List<T> @this, Func<T, T, int> comparison) => CollectionUtility.Sort(@this, comparison);
/// <summary>对字典中的键排序。</summary>
public static Dictionary<TKey, TValue> SortKey<TKey, TValue>(this Dictionary<TKey, TValue> @this, Func<TKey, TKey, int> comparison) => CollectionHelper.SortKey(@this, comparison);
public static Dictionary<TKey, TValue> SortKey<TKey, TValue>(this Dictionary<TKey, TValue> @this, Func<TKey, TKey, int> comparison) => CollectionUtility.SortKey(@this, comparison);
/// <summary>对字典中的键排序。</summary>
public static Dictionary<TKey, TValue> SortKey<TKey, TValue>(this Dictionary<TKey, TValue> @this) where TKey : IComparable<TKey> => CollectionHelper.SortKey(@this, (a, b) => a.CompareTo(b));
public static Dictionary<TKey, TValue> SortKey<TKey, TValue>(this Dictionary<TKey, TValue> @this) where TKey : IComparable<TKey> => CollectionUtility.SortKey(@this, (a, b) => a.CompareTo(b));
/// <summary>对字典中的值排序。</summary>
public static Dictionary<TKey, TValue> SortValue<TKey, TValue>(this Dictionary<TKey, TValue> @this, Func<TValue, TValue, int> comparison) => CollectionHelper.SortValue(@this, comparison);
public static Dictionary<TKey, TValue> SortValue<TKey, TValue>(this Dictionary<TKey, TValue> @this, Func<TValue, TValue, int> comparison) => CollectionUtility.SortValue(@this, comparison);
/// <summary>对字典中的值排序。</summary>
public static Dictionary<TKey, TValue> SortValue<TKey, TValue>(this Dictionary<TKey, TValue> @this) where TValue : IComparable<TValue> => CollectionHelper.SortValue(@this, (a, b) => a.CompareTo(b));
public static Dictionary<TKey, TValue> SortValue<TKey, TValue>(this Dictionary<TKey, TValue> @this) where TValue : IComparable<TValue> => CollectionUtility.SortValue(@this, (a, b) => a.CompareTo(b));
/// <summary>获取集合中的第一个元素。可指定失败时的默认返回值。</summary>
public static T First<T>(this IEnumerable<T> collection, T failed = default(T)) => CollectionHelper.First(collection, failed);
public static T First<T>(this IEnumerable<T> collection, T failed = default(T)) => CollectionUtility.First(collection, failed);
/// <summary>获取集合中的最后一个元素。可指定失败时的默认返回值。</summary>
public static T Last<T>(this IEnumerable<T> collection, T failed = default(T)) => CollectionHelper.Last(collection, failed);
public static T Last<T>(this IEnumerable<T> collection, T failed = default(T)) => CollectionUtility.Last(collection, failed);
/// <summary>生成 StringPairs 对象实例为副本。</summary>
public static StringPairs StringPairs(this NameValueCollection @this) => Apewer.StringPairs.From(@this);
@ -604,20 +604,11 @@ public static class Extensions
public static void Json(this ApiResponse @this, Json json, bool indented = true, bool camel = false) => ApiUtility.Model(@this, new ApiJsonModel(json, camel, indented));
/// <summary>输出文件。</summary>
public static void File(this ApiResponse @this, string path, string name = null) => ApiUtility.Model(@this, new ApiFileModel(path, name));
public static void File(this ApiResponse @this, string path) => ApiUtility.Model(@this, new ApiFileModel(path));
/// <summary>重定向。</summary>
public static void Redirect(this ApiResponse @this, string location) => ApiUtility.Model(@this, new ApiRedirectModel() { Location = location });
/// <summary>设置响应,当发生错误时设置响应。返回错误信息。</summary>
public static string Set(this ApiResponse @this, IList list, bool lower = true, int depth = -1, bool force = false) => ApiUtility.Respond(@this, list, lower, depth, force);
/// <summary>设置响应,当发生错误时设置响应。返回错误信息。</summary>
public static string Set(this ApiResponse @this, IRecord record, bool lower = true) => ApiUtility.Respond(@this, record, lower);
/// <summary>设置响应,当发生错误时设置响应。返回错误信息。</summary>
public static string Set(this ApiResponse @this, Json data, bool lower = true) => ApiUtility.Respond(@this, data, lower);
#endregion
}

15
ChangeLog.md

@ -1,6 +1,21 @@

### 最新提交
### 6.7.0
- 大更新
- Result 模型已完全删除,使用 Result 的程序现在已经改为抛出异常,解决程序返回值混乱的问题;
- 新增了 SqlException 和 ModelException 类型。
- 新功能
- 新增 ApiUtility.StopReturn 方法,可阻止框架解析 ApiResponse 的返回值;
- 新增 RuntimeUtility.IsAnonymousType 方法,用于判断匿名对象;
- 新增 NetworkUtility.Ping 方法,替代原 Icmp 类。
- 增加了基类 KeyRecord 和接口 IRecordPrimaryKey,可以方便地将 Key 属性标记为主键;
- 新增 CollectionUtility 类;开放其中方法,不再限于扩展方法;
- 新增关于时区的方法。
- 问题修正
- 修正 HttpClient 缺少 ResponseHeaders 的问题;
- 修正 ApiModel 不释放 Stream 的问题。
### 6.6.28
- BytesUtility:优化 ToX2 性能;
- Logger:修正默认删除所有日志文件的问题;

Loading…
Cancel
Save