Browse Source

Apewer-6.5.6

dev
王厅 4 years ago
parent
commit
7b0aeb60ae
  1. 2
      Apewer.Source/Source/Access.cs
  2. 480
      Apewer.Source/Source/DbClient.cs
  3. 8
      Apewer.Source/Source/MySql.cs
  4. 6
      Apewer.Source/Source/SqlClient.cs
  5. 528
      Apewer.Source/Source/SqlClientThin.cs
  6. 6
      Apewer.Source/Source/Sqlite.cs
  7. 8
      Apewer.Web/WebSocket/ChatServer.cs
  8. 25
      Apewer/ArrayBuilder.cs
  9. 29
      Apewer/Network/SocketEndPoint.cs
  10. 35
      Apewer/Network/SocketReceived.cs
  11. 5
      Apewer/Network/TcpBuffer.cs
  12. 129
      Apewer/Network/TcpClient.cs
  13. 28
      Apewer/Network/TcpInstance.cs
  14. 129
      Apewer/Network/TcpProxy.cs
  15. 130
      Apewer/Network/TcpServer.cs
  16. 22
      Apewer/Network/UdpServer.cs
  17. 21
      Apewer/Network/_Delegate.cs
  18. 58
      Apewer/NetworkUtility.cs
  19. 71
      Apewer/RuntimeUtility.cs
  20. 0
      Apewer/Source/ColumnInfo.cs
  21. 339
      Apewer/Source/DbClient.cs
  22. 75
      Apewer/Source/IDbAdo.cs
  23. 10
      Apewer/Source/IDbClient.cs
  24. 72
      Apewer/Source/IDbClientAdo.cs
  25. 17
      Apewer/Source/IDbClientBase.cs
  26. 15
      Apewer/Source/IDbOrm.cs
  27. 4
      Apewer/Source/IRecordMoment.cs
  28. 3
      Apewer/Source/IRecordStamp.cs
  29. 37
      Apewer/Source/OrmHelper.cs
  30. 19
      Apewer/Source/Query.cs
  31. 4
      Apewer/Source/Timeout.cs
  32. 45
      Apewer/SystemUtility.cs
  33. 17
      Apewer/_Delegates.cs
  34. 6
      Apewer/_Extensions.cs
  35. 8
      ChangeLog.md

2
Apewer.Source/Source/Access.cs

@ -24,7 +24,7 @@ namespace Apewer.Source
#if NETFRAMEWORK
public partial class Access : IDbClientBase, IDbClientAdo, IDisposable
public partial class Access : IDbAdo
{
#region 连接

480
Apewer.Source/Source/DbClient.cs

@ -0,0 +1,480 @@
#if DEBUG
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Text;
namespace Apewer.Source
{
/// <summary>数据库客户端基类。</summary>
public abstract class DbClient : IDbClient
{
/// <summary></summary>
public virtual Logger Logger { get; set; }
/// <summary></summary>
public DbClient(Timeout timeout) { _timeout = timeout ?? Timeout.Default; }
#region connection
Timeout _timeout = null;
IDbConnection _conn = null;
string _str = null;
/// <summary></summary>
public Timeout Timeout { get => _timeout; set => _timeout = value ?? Timeout.Default; }
/// <summary>获取当前的 SqlConnection 对象。</summary>
public IDbConnection Connection { get => _conn; }
/// <summary></summary>
public bool Online { get => _conn == null ? false : (_conn.State == ConnectionState.Open); }
/// <summary>连接字符串。</summary>
public string ConnectionString { get => _str; }
/// <summary>连接数据库,若未连接则尝试连接,获取连接成功的状态。</summary>
public string Connect()
{
if (_conn == null)
{
_str = NewConnectionString();
_conn = NewConnection();
_conn.ConnectionString = _str;
}
else
{
if (_conn.State == ConnectionState.Open) return null;
}
try
{
_conn.Open();
if (_conn.State == ConnectionState.Open) return null;
var message = $"连接后状态为 {_conn.State},无法验证打开状态。";
Logger.Error(this, "Connect", message, _str);
return "连接失败," + message;
}
catch (Exception ex)
{
Logger.Error(this, "Connect", ex.GetType().Name, ex.Message, _str);
Close();
return ex.Message;
}
}
/// <summary>关闭连接,并释放对象所占用的系统资源。</summary>
public void Close()
{
if (_conn != null)
{
if (_transaction != null)
{
if (_autocommit) Commit();
else Rollback();
}
_conn.Close();
_conn.Dispose();
_conn = null;
}
}
/// <summary>关闭连接,释放对象所占用的系统资源,并清除连接信息。</summary>
public void Dispose() { Close(); }
#endregion
#region transaction
private IDbTransaction _transaction = null;
private bool _autocommit = false;
/// <summary>
/// <para>启动事务,可指定事务锁定行为。</para>
/// <para>Chaos<br />无法覆盖隔离级别更高的事务中的挂起的更改。</para>
/// <para>ReadCommitted<br />在正在读取数据时保持共享锁,以避免脏读,但是在事务结束之前可以更改数据,从而导致不可重复的读取或幻像数据。</para>
/// <para>ReadUncommitted<br />可以进行脏读,意思是说,不发布共享锁,也不接受独占锁。</para>
/// <para>RepeatableRead<br />在查询中使用的所有数据上放置锁,以防止其他用户更新这些数据。 防止不可重复的读取,但是仍可以有幻像行。</para>
/// <para>Serializable<br />在 System.Data.DataSet 上放置范围锁,以防止在事务完成之前由其他用户更新行或向数据集中插入行。</para>
/// <para>Snapshot<br />通过在一个应用程序正在修改数据时存储另一个应用程序可以读取的相同数据版本来减少阻止。 表示您无法从一个事务中看到在其他事务中进行的更改,即便重新查询也是如此。</para>
/// <para>Unspecified = -1<br />正在使用与指定隔离级别不同的隔离级别,但是无法确定该级别。</para>
/// </summary>
/// <param name="commit">在连接的生命周期结束时未结束事务,指定 TRUE 将自动提交,指定 FALSE 将自动回滚。</param>
/// <param name="isolation">指定事务锁定行为,不指定时将使用默认值。</param>
public string Begin(bool commit = false, Class<IsolationLevel> isolation = null)
{
if (_transaction != null) return "已存在未完成的事务,无法再次启动。";
var connect = Connect();
if (connect.NotEmpty()) return $"无法启动事务:连接失败。(${connect})";
try
{
_transaction = isolation ? _conn.BeginTransaction(isolation.Value) : _conn.BeginTransaction();
_autocommit = commit;
return null;
}
catch (Exception ex)
{
Logger.Error(this, "Begin", ex.Message());
return ex.Message();
}
}
/// <summary>提交事务。</summary>
public string Commit()
{
if (_transaction == null) return "事务不存在。";
try
{
_transaction.Commit();
RuntimeUtility.Dispose(_transaction);
_transaction = null;
return null;
}
catch (Exception ex)
{
RuntimeUtility.Dispose(_transaction);
_transaction = null;
Logger.Error(this, "Commit", ex.Message());
return ex.Message();
}
}
/// <summary>从挂起状态回滚事务。</summary>
public string Rollback()
{
if (_transaction == null) return "事务不存在。";
try
{
_transaction.Rollback();
RuntimeUtility.Dispose(_transaction);
_transaction = null;
return null;
}
catch (Exception ex)
{
RuntimeUtility.Dispose(_transaction);
_transaction = null;
Logger.Error(this, "Rollback", ex.Message());
return ex.Message();
}
}
#endregion
#region ado
/// <summary>查询。</summary>
public IQuery Query(string sql) => Query(sql, null);
/// <summary>查询。</summary>
public IQuery Query(string sql, IEnumerable<IDataParameter> parameters)
{
if (TextUtility.IsEmpty(sql)) return new Query(false, "语句无效。");
var connected = Connect();
if (connected.NotEmpty()) return new Query(false, connected);
try
{
using (var command = NewCommand())
{
command.Connection = _conn;
if (_timeout != null) command.CommandTimeout = Timeout.Query;
command.CommandText = sql;
if (parameters != null)
{
foreach (var parameter in parameters)
{
if (parameter != null) command.Parameters.Add(parameter);
}
}
var ex = null as Exception;
var da = null as IDataAdapter;
try { da = NewDataAdapter(command); }
catch (Exception adapterEx) { ex = adapterEx; }
if (ex == null)
{
using (var ds = new DataSet())
{
da.Fill(ds);
if (ds.Tables.Count > 0)
{
var tables = new DataTable[ds.Tables.Count];
ds.Tables.CopyTo(tables, 0);
return new Query(tables, true);
}
else
{
Logger.Error(this, "Query", "查询结果不包含任何数据表。", sql);
return new Query(false, "查询结果不包含任何数据表。");
}
}
}
else
{
Logger.Error(this, "Query", ex.GetType().FullName, ex.Message, sql);
return new Query(ex);
}
}
}
catch (Exception exception)
{
Logger.Error(this, "Query", exception.GetType().FullName, exception.Message, sql);
return new Query(exception);
}
}
/// <summary>执行。</summary>
public IExecute Execute(string sql) => Execute(sql, null);
/// <summary>执行单条 Transact-SQL 语句,并加入参数。</summary>
public IExecute Execute(string sql, IEnumerable<IDataParameter> parameters)
{
if (TextUtility.IsEmpty(sql)) return new Execute(false, "语句无效。");
var connected = Connect();
if (connected.NotEmpty()) return new Execute(false, connected);
var inTransaction = _transaction != null;
if (!inTransaction) Begin();
try
{
using (var command = NewCommand())
{
command.Connection = _conn;
command.Transaction = (DbTransaction)_transaction;
if (Timeout != null) command.CommandTimeout = Timeout.Execute;
command.CommandText = sql;
if (parameters != null)
{
foreach (var parameter in parameters)
{
if (parameter != null) command.Parameters.Add(parameter);
}
}
var rows = command.ExecuteNonQuery();
if (!inTransaction) Commit(); // todo 此处应该检查事务提交产生的错误。
return new Execute(true, rows);
}
}
catch (Exception exception)
{
Logger.Error(this, "Execute", exception, sql);
if (!inTransaction) Rollback();
return new Execute(exception);
}
}
/// <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();
}
}
#endregion
#region orm
/// <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() => OrmHelper.As<object, T>(Query(typeof(T), sql, parameters));
/// <summary>使用指定语句查询,获取查询结果。</summary>
/// <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)
{
if (_conn == null) return new Result<object[]>("连接无效。");
if (model == null) return new Result<object[]>("数据模型类型无效。");
if (string.IsNullOrEmpty(sql)) return new Result<object[]>("SQL 语句无效。");
using (var query = Query(sql, parameters))
{
var result = null as Result<object[]>;
if (query.Success)
{
try
{
var array = OrmHelper.Fill(query, model);
result = new Result<object[]>(array);
}
catch (Exception ex)
{
result = new Result<object[]>(ex);
}
}
else
{
result = new Result<object[]>(query.Message);
}
return result;
}
}
#endregion
#region static
/// <summary>对文本转义,符合 SQL 安全性。可根据字段类型限制 UTF-8 字节数,默认为 0 时不限制字节数。</summary>
public static string Escape(string text, int bytes = 0)
{
if (text.IsEmpty()) return "";
var t = text ?? "";
t = t.Replace("\\", "\\\\");
t = t.Replace("'", "\\'");
t = t.Replace("\n", "\\n");
t = t.Replace("\r", "\\r");
t = t.Replace("\b", "\\b");
t = t.Replace("\t", "\\t");
t = t.Replace("\f", "\\f");
if (bytes > 5)
{
if (t.Bytes(Encoding.UTF8).Length > bytes)
{
while (true)
{
t = t.Substring(0, t.Length - 1);
if (t.Bytes(Encoding.UTF8).Length <= (bytes - 4)) break;
}
t = t + " ...";
}
}
return t;
}
/// <summary>获取表名。</summary>
protected static string Table<T>() => Table(typeof(T));
/// <summary>获取表名。</summary>
protected static string Table(Type model)
{
if (model == null) throw new Exception($"无法从无效类型获取表名。");
var ts = TableStructure.Parse(model);
if (ts == null) throw new Exception($"无法从类型 {model.FullName} 获取表名。");
return ts.Name;
}
#endregion
#region derived
/// <summary>为 Ado 创建连接字符串。</summary>
protected abstract string NewConnectionString();
/// <summary>为 Ado 创建 IDbConnection 对象。</summary>
protected abstract IDbConnection NewConnection();
/// <summary>为 Ado 创建 IDbCommand 对象。</summary>
protected abstract IDbCommand NewCommand();
/// <summary>为 Ado 创建 IDataAdapter 对象。</summary>
protected abstract IDataAdapter NewDataAdapter(IDbCommand command);
// /// <summary>为 Ado 创建 IDataParameter 对象。</summary>
// protected abstract IDataParameter NewDataParameter();
#endregion
#region initialization
/// <summary>查询数据库中的所有表名。</summary>
public abstract string[] TableNames();
/// <summary>查询数据库实例中的所有数据库名。</summary>
public abstract string[] StoreNames();
/// <summary>查询表中的所有列名。</summary>
public abstract string[] ColumnNames(string tableName);
/// <summary>获取列信息。</summary>
public abstract ColumnInfo[] ColumnsInfo(string tableName);
/// <summary>初始化指定类型,以创建表或增加字段。</summary>
/// <returns>错误信息。当成功时候返回空字符串。</returns>
public string Initialize<T>() where T : class, new() => Initialize(typeof(T));
#endregion
#region IDbClientOrm
/// <summary>初始化指定类型,以创建表或增加字段。</summary>
/// <param name="model">要初始化的类型。</param>
/// <returns>错误信息。当成功时候返回空字符串。</returns>
public abstract string Initialize(Type model);
/// <summary>插入记录。</summary>
/// <param name="record">要插入的记录实体。</param>
/// <param name="table">插入到指定表。当不指定时,由 record 类型决定。</param>
/// <returns>错误信息。当成功时候返回空字符串。</returns>
public abstract string Insert(object record, string table = null);
/// <summary>更新记录。</summary>
/// <param name="record">要插入的记录实体。</param>
/// <param name="table">插入到指定表。当不指定时,由 record 类型决定。</param>
/// <returns>错误信息。当成功时候返回空字符串。</returns>
public abstract string Update(IRecord record, string table = null);
/// <summary>获取指定类型的主键,按 Flag 属性筛选。</summary>
/// <param name="model">要查询的类型。</param>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public abstract Result<string[]> Keys(Type model, long flag = 0);
/// <summary>获取指定类型的主键,按 Flag 属性筛选。</summary>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public abstract Result<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 abstract Result<object> Get(Type model, string key, long flag = 0);
/// <summary>获取具有指定 Key 的记录,并要求记录具有指定的 Flag 属性。</summary>
/// <param name="key">目标记录的主键。</param>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public abstract Result<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 abstract Result<object[]> Query(Type model, long flag = 0);
/// <summary>查询所有记录。</summary>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public abstract Result<T[]> Query<T>(long flag = 0) where T : class, IRecord, new();
#endregion
}
}
#endif

8
Apewer.Source/Source/MySql.cs

@ -221,7 +221,7 @@ namespace Apewer.Source
}
using (var ds = new DataSet())
{
using (var da = new MySqlDataAdapter(sql, _connection))
using (var da = new MySqlDataAdapter(command))
{
var name = "table_" + Guid.NewGuid().ToString("n");
da.Fill(ds, name);
@ -541,12 +541,12 @@ namespace Apewer.Source
}
/// <summary></summary>
public Result<object[]> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
public Result<object[]> Query(Type model, string sql, IEnumerable<IDataParameter> parameters = null) => OrmHelper.Query(this, model, sql, parameters);
/// <summary></summary>
public Result<T[]> Query<T>(string sql) where T : class, new()
public Result<T[]> Query<T>(string sql, IEnumerable<IDataParameter> parameters = null) where T : class, new()
{
var query = Query(sql);
var query = Query(sql, parameters);
if (!query.Success) return new Result<T[]>(query.Message);
var records = query.Fill<T>();
query.Dispose();

6
Apewer.Source/Source/SqlClient.cs

@ -560,12 +560,12 @@ namespace Apewer.Source
}
/// <summary>获取按指定语句查询到的所有记录。</summary>
public Result<object[]> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
public Result<object[]> Query(Type model, string sql, IEnumerable<IDataParameter> parameters = null) => OrmHelper.Query(this, model, sql, parameters);
/// <summary>获取按指定语句查询到的所有记录。</summary>
public Result<T[]> Query<T>(string sql) where T : class, new()
public Result<T[]> Query<T>(string sql, IEnumerable<IDataParameter> parameters = null) where T : class, new()
{
var query = Query(sql);
var query = Query(sql, parameters);
if (!query.Success) return new Result<T[]>(query.Message);
var records = query.Fill<T>();
query.Dispose();

528
Apewer.Source/Source/SqlClientThin.cs

@ -0,0 +1,528 @@
#if DEBUG
/* 2021.11.28 */
using Apewer;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Text;
using static Apewer.Source.OrmHelper;
using System.Data.SqlClient;
#if NETFRAMEWORK
using System.Data.Sql;
#endif
namespace Apewer.Source
{
/// <summary></summary>
[Serializable]
public class SqlClientThin : DbClient, IDbClient
{
#region
string _str = null;
/// <summary>使用连接字符串创建数据库连接实例。</summary>
public SqlClientThin(string connectionString, Timeout timeout = null) : base(timeout)
{
_str = connectionString ?? "";
}
/// <summary>使用连接凭据创建数据库连接实例。</summary>
public SqlClientThin(string address, string store, string user, string pass, Timeout timeout = null) : base(timeout)
{
if (timeout == null) timeout = Timeout.Default;
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; ";
else
{
cs += $"user id = {u}; ";
if (!string.IsNullOrEmpty(p)) cs += $"password = {p}; ";
}
cs += $"connection timeout = {timeout.Connect}; ";
_str = cs;
}
/// <summary>为 Ado 创建连接字符串。</summary>
protected override string NewConnectionString() => _str;
/// <summary>为 Ado 创建 IDbConnection 对象。</summary>
protected override IDbConnection NewConnection() => new SqlConnection();
/// <summary>为 Ado 创建 IDbCommand 对象。</summary>
protected override IDbCommand NewCommand() => new SqlCommand();
/// <summary>为 Ado 创建 IDataAdapter 对象。</summary>
protected override IDataAdapter NewDataAdapter(IDbCommand command) => new SqlDataAdapter((SqlCommand)command);
#endregion
#region ORM
/// <summary>查询数据库中的所有表名。</summary>
public override string[] TableNames() => TextColumn("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" });
/// <summary>查询表中的所有列名。</summary>
public override string[] ColumnNames(string tableName) => TextColumn($"select [name] from [syscolumns] where [id] = object_id('{tableName}'); ");
/// <summary>获取列信息。</summary>
public override ColumnInfo[] ColumnsInfo(string tableName)
{
if (tableName.IsEmpty()) throw new ArgumentNullException(nameof(tableName));
var sql = $"select name, xtype, length from syscolumns where id = object_id('{tableName}') ";
using (var query = Query(sql))
{
var ab = new ArrayBuilder<ColumnInfo>();
for (var i = 0; i < query.Rows; i++)
{
var info = new ColumnInfo();
info.Name = query.Text(i, "name");
info.Type = XType(query.Int32(i, "xtype"));
info.Length = query.Int32(i, "length");
ab.Add(info);
}
return ab.Export();
}
}
/// <summary>创建表,当表不存在时创建表,当现存表中缺少模型中属性对应的列时增加列。成功时返回空字符串,发生异常时返回异常信息。</summary>
public override string Initialize(Type model)
{
var structure = TableStructure.Parse(model);
if (structure == null) return "无法解析记录模型。";
// 连接数据库。
var connect = Connect();
if (connect.NotEmpty()) return connect;
// 检查现存表。
var exists = false;
var tables = TableNames();
if (tables.Length > 0)
{
var lower = structure.Name.ToLower();
foreach (var table in tables)
{
if (TextUtility.IsBlank(table)) continue;
if (table.ToLower() == lower)
{
exists = true;
break;
}
}
}
if (exists)
{
// 获取已存在的列名。
var columns = ColumnNames(structure.Name);
if (columns.Length > 0)
{
var lower = new List<string>();
foreach (var column in columns)
{
if (TextUtility.IsBlank(column)) continue;
lower.Add(column.ToLower());
}
columns = lower.ToArray();
}
// 增加列。
foreach (var column in structure.Columns)
{
// 检查 Independent 特性。
if (structure.Independent && column.Independent) continue;
// 去重。
var lower = column.Field.ToLower();
if (columns.Contains(lower)) continue;
var type = Declaration(column);
if (string.IsNullOrEmpty(type)) return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。");
var sql = TextUtility.Merge("alter table [", structure.Name, "] add ", type, "; ");
var execute = Execute(sql);
if (execute.Success == false) return execute.Message;
}
return TextUtility.Empty;
}
else
{
var sqlcolumns = new List<string>();
foreach (var column in structure.Columns)
{
// 检查 Independent 特性。
if (structure.Independent && column.Independent) continue;
var type = Declaration(column);
if (!column.Independent && column.Property.Name == "Key") type = type + " primary key";
if (string.IsNullOrEmpty(type)) return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。");
sqlcolumns.Add(type);
}
var sql = TextUtility.Merge("create table [", structure.Name, "](", string.Join(", ", sqlcolumns.ToArray()), "); ");
var execute = Execute(sql);
if (execute.Success) return TextUtility.Empty;
return execute.Message;
}
}
/// <summary>插入记录。返回错误信息。</summary>
public override string Insert(object record, string table = null)
{
if (record == null) return "参数无效。";
FixProperties(record);
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
if (string.IsNullOrEmpty(table)) table = structure.Name;
if (string.IsNullOrEmpty(table)) return "表名称无效。";
var ps = structure.CreateParameters(record, Parameter);
var psc = ps.Length;
if (psc < 1) return "数据模型不包含字段。";
var names = new List<string>(psc);
var values = new List<string>(psc);
foreach (var column in ps)
{
//names.Add(TextGenerator.Merge("[", column, "]"));
names.Add(TextUtility.Merge(column));
values.Add("@" + column);
}
var sb = new StringBuilder();
sb.Append("insert into [", table, "](", string.Join(", ", names.ToArray()), ") ");
sb.Append("values(", string.Join(", ", values.ToArray()), "); ");
var sql = sb.ToString();
var execute = Execute(sql, ps);
if (execute.Success) return TextUtility.Empty;
return execute.Message;
}
/// <summary>更新记录,实体中的 Key 属性不被更新。返回错误信息。</summary>
/// <remarks>无法更新带有 Independent 特性的模型(缺少 Key 属性)。</remarks>
public override string Update(IRecord record, string table = null)
{
if (record == null) return "参数无效。";
FixProperties(record);
SetUpdated(record);
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
if (structure.Independent) return "无法更新带有 Independent 特性的模型。";
if (string.IsNullOrEmpty(table)) table = structure.Name;
if (string.IsNullOrEmpty(table)) return "表名称无效。";
var ps = structure.CreateParameters(record, Parameter, "_key");
var psc = ps.Length;
if (psc < 1) return "数据模型不包含字段。";
var items = new List<string>();
foreach (var p in ps)
{
var pn = p.ParameterName;
items.Add(TextUtility.Merge("[", pn, "] = @", pn));
}
var key = record.Key.SafeKey();
var sql = TextUtility.Merge("update [", table, "] set ", string.Join(", ", items.ToArray()), " where [_key]='", key, "'; ");
var execute = Execute(sql, ps);
if (execute.Success) return TextUtility.Empty;
return execute.Message;
}
/// <summary>获取记录。</summary>
public override Result<object[]> Query(Type model, long flag = 0) => OrmHelper.Query(this, model, (tn) =>
{
if (flag == 0) return $"select * from [{tn}]; ";
return $"select * from [{tn}] where _flag={flag}; ";
});
/// <summary>获取记录。</summary>
public override Result<T[]> Query<T>(long flag = 0) => OrmHelper.Query<T>(this, (tn) =>
{
if (flag == 0) return $"select * from [{tn}]; ";
return $"select * from [{tn}] where _flag={flag}; ";
});
/// <summary>获取具有指定 Key 的记录。</summary>
public override Result<object> Get(Type model, string key, long flag = 0) => OrmHelper.Get(this, model, key, (tn, sk) =>
{
if (flag == 0) return $"select top 1 * from [{tn}] _key='{sk}'; ";
return $"select top 1 * from [{tn}] where _key='{sk}' and _key='{sk}'; ";
});
/// <summary>获取具有指定 Key 的记录。</summary>
public override Result<T> Get<T>(string key, long flag = 0) => OrmHelper.Get<T>(this, key, (tn, sk) =>
{
if (flag == 0) return $"select top 1 * from [{tn}] _key='{sk}'; ";
return $"select top 1 * from [{tn}] where _key='{sk}' and _key='{sk}'; ";
});
/// <summary>查询有效的 Key 值。</summary>
public override Result<string[]> Keys(Type model, long flag = 0) => OrmHelper.Keys(this, model, (tn) =>
{
if (flag == 0) return $"select _key from [{tn}]; ";
return $"select _key from [{tn}] where _flag={flag}; ";
});
/// <summary>查询有效的 Key 值。</summary>
public override Result<string[]> Keys<T>(long flag = 0) => Keys(typeof(T), flag);
#endregion
#region public static
#if NET20 || NET40
/// <summary>枚举本地网络中服务器的名称。</summary>
public static SqlServerSource[] EnumerateServer()
{
var list = new List<SqlServerSource>();
// 表中列名:ServerName、InstanceName、IsClustered、Version。
using (var query = new Query(SqlDataSourceEnumerator.Instance.GetDataSources()))
{
for (int i = 0; i < query.Rows; i++)
{
var item = new SqlServerSource();
item.ServerName = query.Text(i, "ServerName");
list.Add(item);
}
}
return list.ToArray();
}
#endif
/// <summary>指定的连接凭据是否符合连接要求,默认指定 master 数据库。</summary>
public static bool Proven(string address, string user, string pass) => Proven(address, "master", user, pass);
/// <summary>指定的连接凭据是否符合连接要求。</summary>
public static bool Proven(string address, string store, string user, string pass)
{
var a = string.IsNullOrEmpty(address);
var s = string.IsNullOrEmpty(store);
var u = string.IsNullOrEmpty(user);
var p = string.IsNullOrEmpty(pass);
if (a) return false;
if (s) return false;
if (u && !p) return false;
return true;
}
/// <summary>创建参数。</summary>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidOperationException"></exception>
static SqlParameter Parameter(Parameter parameter)
{
if (parameter == null) throw new InvalidOperationException("参数无效。");
return Parameter(parameter.Name, parameter.Type, parameter.Size, parameter.Value);
}
/// <summary>创建参数。</summary>
public static SqlParameter Parameter(string name, ColumnType type, int size, object value)
{
var vname = TextUtility.Trim(name);
if (TextUtility.IsBlank(vname)) return null;
var vtype = SqlDbType.BigInt;
switch (type)
{
case ColumnType.Bytes:
vtype = SqlDbType.Image;
break;
case ColumnType.Integer:
vtype = SqlDbType.BigInt;
break;
case ColumnType.Float:
vtype = SqlDbType.Float;
break;
case ColumnType.DateTime:
vtype = SqlDbType.DateTime;
break;
case ColumnType.VarChar:
case ColumnType.VarChar191:
case ColumnType.VarCharMax:
vtype = SqlDbType.VarChar;
break;
case ColumnType.NVarChar:
case ColumnType.NVarChar191:
case ColumnType.NVarCharMax:
vtype = SqlDbType.NVarChar;
break;
case ColumnType.Text:
vtype = SqlDbType.Text;
break;
case ColumnType.NText:
vtype = SqlDbType.NText;
break;
default:
throw new InvalidOperationException(TextUtility.Merge("类型 ", type.ToString(), " 不受支持。"));
}
var vsize = size;
switch (type)
{
case ColumnType.VarChar:
vsize = NumberUtility.Restrict(vsize, 0, 8000);
break;
case ColumnType.NVarChar:
vsize = NumberUtility.Restrict(vsize, 0, 4000);
break;
case ColumnType.VarChar191:
case ColumnType.NVarChar191:
vsize = NumberUtility.Restrict(vsize, 0, 191);
break;
default:
vsize = 0;
break;
}
var vvalue = value;
if (vvalue is string && vvalue != null && vsize > 0)
{
vvalue = TextUtility.Left((string)vvalue, vsize);
}
var parameter = new SqlParameter();
parameter.ParameterName = vname;
parameter.SqlDbType = vtype;
parameter.Value = vvalue;
if (vsize > 0) parameter.Size = vsize;
return parameter;
}
/// <summary>创建参数。</summary>
public static SqlParameter Parameter(string name, SqlDbType type, int size, object value)
{
if (value is string && value != null && size > 0)
{
value = TextUtility.Left((string)value, (int)size);
}
var p = new SqlParameter();
p.ParameterName = name ?? "";
p.SqlDbType = type;
p.Size = size;
p.Value = value;
return p;
}
/// <summary>创建参数。</summary>
public static SqlParameter Parameter(string name, SqlDbType type, object value)
{
var p = new SqlParameter();
p.ParameterName = name ?? "";
p.SqlDbType = type;
p.Value = value;
return p;
}
static string Declaration(ColumnAttribute column)
{
var type = TextUtility.Empty;
var vcolumn = column;
var length = Math.Max(0, vcolumn.Length);
switch (vcolumn.Type)
{
case ColumnType.Integer:
type = "bigint";
break;
case ColumnType.Float:
type = "float";
break;
case ColumnType.Bytes:
type = "image";
break;
case ColumnType.DateTime:
type = "datetime";
break;
case ColumnType.VarChar:
type = TextUtility.Merge("varchar(", Math.Min(8000, length).ToString(), ")");
break;
case ColumnType.VarChar191:
type = TextUtility.Merge("varchar(191)");
break;
case ColumnType.VarCharMax:
type = TextUtility.Merge("varchar(max)");
break;
case ColumnType.Text:
type = TextUtility.Merge("text");
break;
case ColumnType.NVarChar:
type = TextUtility.Merge("nvarchar(", Math.Min(4000, length).ToString(), ")");
break;
case ColumnType.NVarChar191:
type = TextUtility.Merge("nvarchar(191)");
break;
case ColumnType.NVarCharMax:
type = TextUtility.Merge("nvarchar(max)");
break;
case ColumnType.NText:
type = TextUtility.Merge("ntext");
break;
default:
return TextUtility.Empty;
}
return TextUtility.Merge("[", vcolumn.Field, "] ", type);
}
static string XType(int xtype)
{
switch (xtype)
{
case 34: return "image";
case 35: return "text";
case 36: return "uniqueidentifier";
case 48: return "tinyint";
case 52: return "smallint";
case 56: return "int";
case 58: return "smalldatetime";
case 59: return "real";
case 60: return "money";
case 61: return "datetime";
case 62: return "float";
case 98: return "sql_variant";
case 99: return "ntext";
case 104: return "bit";
case 106: return "decimal";
case 108: return "numeric";
case 122: return "smallmoney";
case 127: return "bigint";
case 165: return "varbinary";
case 167: return "varchar";
case 173: return "binary";
case 175: return "char";
case 189: return "timestamp";
case 231: return "nvarchar";
case 239: return "nchar";
case 241: return "xml";
}
return null;
}
#endregion
}
}
#endif

6
Apewer.Source/Source/Sqlite.cs

@ -210,7 +210,7 @@ namespace Apewer.Source
}
using (var dataset = new DataSet())
{
using (var da = new SQLiteDataAdapter(sql, _db))
using (var da = new SQLiteDataAdapter(command))
{
var name = "table_" + Guid.NewGuid().ToString("n");
da.Fill(dataset, name);
@ -506,10 +506,10 @@ namespace Apewer.Source
}
/// <summary>获取按指定语句查询到的所有记录。</summary>
public Result<object[]> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
public Result<object[]> Query(Type model, string sql, IEnumerable<IDataParameter> parameters = null) => OrmHelper.Query(this, model, sql, parameters);
/// <summary>获取按指定语句查询到的所有记录。</summary>
public Result<T[]> Query<T>(string sql) where T : class, new()
public Result<T[]> Query<T>(string sql, IEnumerable<IDataParameter> parameters = null) where T : class, new()
{
var query = Query(sql);
if (!query.Success) return new Result<T[]>(query.Message);

8
Apewer.Web/WebSocket/ChatServer.cs

@ -144,10 +144,10 @@ namespace Apewer.WebSocket
_command = new UdpServer();
_command.Port = _commandport;
_command.Excepted += (s, e) => RaiseExcepted(e);
_command.Quitted += (s, e) => RaiseConsole("命令服务已退出。");
_command.Started += (s, e) => RaiseConsole("命令服务已启动。");
_command.Received += CommandReceived;
_command.Excepted += (s, ex) => RaiseExcepted(ex);
_command.Quitted += (s) => RaiseConsole("命令服务已退出。");
_command.Started += (s) => RaiseConsole("命令服务已启动。");
_command.Received += (s, e) => CommandReceived(s, e.IP, e.Port, e.Bytes);
_command.Start();
}
}

25
Apewer/ArrayBuilder.cs

@ -121,6 +121,31 @@ namespace Apewer
foreach (var item in items) Add(item);
}
/// <summary>添加元素。</summary>
/// <param name="buffer">要添加的元素数组。</param>
/// <param name="offset">buffer 的开始位置。</param>
/// <param name="count">buffer 的元素数量。</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public void Add(T[] buffer, int offset, int count)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
var length = buffer.Length;
if (offset < 0 || offset >= length || count < 0) throw new ArgumentOutOfRangeException();
if (offset + count > length) throw new ArgumentOutOfRangeException();
if (_capacity - _count < length)
{
_capacity = _count + length;
var temp = new T[_capacity];
Array.Copy(_array, temp, _count);
_array = temp;
}
Array.Copy(buffer, offset, _array, _count, count);
_count = _capacity;
}
/// <summary>清空所有元素。</summary>
public void Clear()
{

29
Apewer/Network/SocketEndPoint.cs

@ -1,31 +1,30 @@
using Apewer;
using System;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
namespace Apewer.Network
{
/// <summary></summary>
public struct SocketEndPoint
/// <summary>套接字的终端。</summary>
[Serializable]
public sealed class SocketEndPoint
{
internal SocketEndPoint(Socket socket, string ip, int port)
{
Socket = socket;
IP = ip ?? TextUtility.Empty;
Port = NumberUtility.Restrict(port, 0, ushort.MaxValue);
}
/// <summary>对端 IP 地址。</summary>
public string IP { get; set; }
/// <summary></summary>
public Socket Socket { get; }
/// <summary>对端端口。</summary>
public int Port { get; set; }
/// <summary></summary>
public string IP { get; }
public SocketEndPoint() { }
/// <summary></summary>
public int Port { get; }
public SocketEndPoint(string ip, int port)
{
IP = ip;
Port = port;
}
}

35
Apewer/Network/SocketReceived.cs

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Network
{
/// <summary>套接字数据接收模型。</summary>
[Serializable]
public sealed class SocketReceived
{
/// <summary>对端 IP 地址。</summary>
public string IP { get; set; }
/// <summary>对端端口。</summary>
public int Port { get; set; }
/// <summary>对端数据。</summary>
public byte[] Bytes { get; set; }
/// <summary></summary>
public SocketReceived() { }
/// <summary></summary>
public SocketReceived(string ip, int port, byte[] bytes)
{
IP = ip;
Port = port;
Bytes = bytes;
}
}
}

5
Apewer/Internals/TcpBuffer.cs → Apewer/Network/TcpBuffer.cs

@ -3,12 +3,13 @@ using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
namespace Apewer.Internals
namespace Apewer
{
internal class TcpBuffer
{
/// <summary>TCP 传输缓冲区大小。</summary>
// TCP 传输缓冲区大小。
public const int Size = 8192;
public Socket Socket;

129
Apewer/Network/TcpClient.cs

@ -19,33 +19,19 @@ namespace Apewer.Network
#region event
/// <summary>Exception。</summary>
public event Event<Exception> Excepted;
public Event<Exception> Excepted { get; set; }
/// <summary>已发送数据。</summary>
public event Event<byte[]> Sent;
public Event<byte[]> Sent { get; set; }
/// <summary>已接收数据。</summary>
public event Event<byte[]> Received;
public Event<byte[]> Received { get; set; }
/// <summary>已连接。</summary>
public event Event Connected;
public Event Connected { get; set; }
/// <summary>已断开。</summary>
public event Event Closed;
#region raise
private void RaiseConnected() { if (Connected != null) Connected(this, new EventArgs()); }
private void RaiseClosed() { if (Closed != null) Closed(this, new EventArgs()); }
private void RaiseSent(byte[] value) { if (Sent != null) Sent(this, value); }
private void RaiseReceived(byte[] value) { if (Received != null) Received(this, value); }
private void RaiseExcepted(Exception value) { if (Excepted != null) Excepted(this, value); }
#endregion
public Event Closed { get; set; }
#endregion
@ -59,23 +45,25 @@ namespace Apewer.Network
// private AutoResetEvent _are = null;
private bool _break = false;
private int _timeout = 1000;
private int _timeout = 0;
private string _localip, _remoteip;
private int _localport = 0, _remoteport = 0;
private bool _background = true;
/// <summary></summary>
internal object Tag { get; set; }
/// <summary>构造函数。</summary>
public TcpClient()
{
RemoteIP = "127.0.0.1";
RemotePort = 0;
Timeout = 1000;
}
/// <summary>构造函数。</summary>
public TcpClient(string ip, int port, int timeout = 1000)
public TcpClient(string ip, int port, int timeout = 0)
{
RemoteIP = ip;
RemotePort = port;
@ -94,7 +82,7 @@ namespace Apewer.Network
{
_background = value;
try { if (_listener != null) _listener.IsBackground = value; }
catch (Exception ex) { RaiseExcepted(ex); }
catch (Exception ex) { Excepted?.Invoke(this, ex); }
}
}
@ -104,10 +92,10 @@ namespace Apewer.Network
get { return _remoteip; }
set
{
string vip = value;
if (!NetworkUtility.IsIP(vip)) vip = NetworkUtility.Resolve(vip);
if (!NetworkUtility.IsIP(vip)) vip = "127.0.0.1";
_remoteip = vip;
string ip = value;
if (!NetworkUtility.IsIP(ip)) ip = NetworkUtility.Resolve(ip);
if (!NetworkUtility.IsIP(ip)) ip = "127.0.0.1";
_remoteip = ip;
}
}
@ -117,10 +105,10 @@ namespace Apewer.Network
get { return _remoteport; }
set
{
int vport = value;
if (vport < 0) vport = 0;
if (vport > 65535) vport = 65535;
_remoteport = vport;
int port = value;
if (port < 0) port = 0;
if (port > 65535) port = 65535;
_remoteport = port;
}
}
@ -137,10 +125,10 @@ namespace Apewer.Network
get { return _localport; }
private set
{
int vport = value;
if (vport < 0) vport = 0;
if (vport > 65535) vport = 65535;
_localport = vport;
int port = value;
if (port < 0) port = 0;
if (port > 65535) port = 65535;
_localport = port;
}
}
@ -148,18 +136,24 @@ namespace Apewer.Network
public int Timeout
{
get { return _timeout; }
set { _timeout = (value > 0) ? value : 1; }
set { _timeout = (value > 0) ? value : 0; }
}
/// <summary>开始连接,并初始化发送队列。</summary>
public void Start()
public void Start(bool inBackground = false)
{
Close(false);
_queue = new Queue<byte[]>();
if (!inBackground)
{
_provider = new Thread(Provider);
_provider.IsBackground = true;
_provider.Start();
return;
}
Provider();
// _are.WaitOne(1000);
}
@ -173,7 +167,7 @@ namespace Apewer.Network
CloseQueue();
_queue = null;
// _are = null;
if (@event) RaiseClosed();
if (@event) Closed?.Invoke(this);
}
/// <summary>向服务端发送数据。</summary>
@ -187,8 +181,8 @@ namespace Apewer.Network
}
else
{
var vse = new SocketException(10057);
RaiseExcepted(vse);
var ex = new SocketException(10057);
Excepted?.Invoke(this, ex);
return false;
}
}
@ -199,7 +193,7 @@ namespace Apewer.Network
get
{
try { if (_socket != null) return _socket.Connected; }
catch (Exception ex) { RaiseExcepted(ex); }
catch (Exception ex) { Excepted?.Invoke(this, ex); }
return false;
}
}
@ -210,8 +204,8 @@ namespace Apewer.Network
private void Listener()
{
var vb = new byte[TcpBuffer.Size];
var vf = 0;
var buffer = new byte[TcpBuffer.Size];
var length = 0;
while (true)
{
try
@ -219,13 +213,12 @@ namespace Apewer.Network
if (_socket.Poll(50, SelectMode.SelectWrite))
{
_socket.Blocking = true;
vf = _socket.Receive(vb);
if (vf > 0)
length = _socket.Receive(buffer);
if (length > 0)
{
var vms = new MemoryStream(vb);
vms.SetLength(vf);
RaiseReceived(vms.ToArray());
vms.Dispose();
var bytes = new byte[length];
Array.Copy(buffer, bytes, length);
Received?.Invoke(this, bytes);
}
else
{
@ -235,12 +228,12 @@ namespace Apewer.Network
}
catch (SocketException ex)
{
if (ex.ErrorCode != 10060) RaiseExcepted(ex);
if (ex.ErrorCode != 10060) Excepted?.Invoke(this, ex);
if (ex.ErrorCode == 10054) break;
}
catch (Exception ex)
{
RaiseExcepted(ex);
Excepted?.Invoke(this, ex);
// break;
}
}
@ -258,15 +251,15 @@ namespace Apewer.Network
CloseThread(ref _provider);
CloseSocket();
CloseQueue();
RaiseClosed();
Closed?.Invoke(this);
}
private void Provider()
{
_break = false;
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.SendTimeout = _timeout;
_socket.ReceiveTimeout = _timeout;
if (_timeout > 0) _socket.SendTimeout = _timeout;
if (_timeout > 0) _socket.ReceiveTimeout = _timeout;
//_are = new AutoResetEvent(false);
try
@ -277,13 +270,13 @@ namespace Apewer.Network
{
_socket.SendBufferSize = TcpBuffer.Size;
_socket.ReceiveBufferSize = TcpBuffer.Size;
var vrep = (IPEndPoint)_socket.RemoteEndPoint;
var vlep = (IPEndPoint)_socket.LocalEndPoint;
_remoteip = vrep.Address.ToString();
_localip = vlep.Address.ToString();
_localport = vlep.Port;
var rep = (IPEndPoint)_socket.RemoteEndPoint;
var lep = (IPEndPoint)_socket.LocalEndPoint;
_remoteip = rep.Address.ToString();
_localip = lep.Address.ToString();
_localport = lep.Port;
RaiseConnected();
Connected?.Invoke(this);
_listener = new Thread(Listener);
_listener.Start();
// _are.Set();
@ -296,7 +289,7 @@ namespace Apewer.Network
}
catch (Exception ex)
{
RaiseExcepted(ex);
Excepted?.Invoke(this, ex);
}
while ((!_break) && (_socket != null))
@ -304,17 +297,17 @@ namespace Apewer.Network
if (_queue.Count > 0)
{
var vb = _queue.Dequeue();
if (vb.Length > 0)
var bytes = _queue.Dequeue();
if (bytes.Length > 0)
{
try
{
_socket.Send(vb);
RaiseSent(vb);
_socket.Send(bytes);
Sent?.Invoke(this, bytes);
}
catch (Exception ex)
{
RaiseExcepted(ex);
Excepted?.Invoke(this, ex);
}
}
else
@ -334,8 +327,8 @@ namespace Apewer.Network
_socket.Disconnect(false);
_socket.Close();
}
catch (SocketException ex) { RaiseExcepted(ex); }
catch (Exception ex) { RaiseExcepted(ex); }
catch (SocketException ex) { Excepted?.Invoke(this, ex); }
catch (Exception ex) { Excepted?.Invoke(this, ex); }
_socket = null;
}
}

28
Apewer/Internals/TcpInstance.cs → Apewer/Network/TcpInstance.cs

@ -7,7 +7,7 @@ using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Apewer.Internals
namespace Apewer
{
internal class TcpInstance
@ -25,11 +25,11 @@ namespace Apewer.Internals
private int _length = 0;
private byte[] _data;
public TcpInstance(TcpServer argParent, Socket argServer, Socket argClient)
public TcpInstance(TcpServer parent, Socket server, Socket client)
{
_parent = argParent;
_server = argServer;
_client = argClient;
_parent = parent;
_server = server;
_client = client;
_ep = (IPEndPoint)_client.RemoteEndPoint;
_ip = _ep.Address.ToString();
_port = _ep.Port;
@ -47,20 +47,20 @@ namespace Apewer.Internals
catch (Exception argex) { _parent.RaiseExcepted(argex); }
}
private void Callback(IAsyncResult argAr)
private void Callback(IAsyncResult ar)
{
try
{
var vm = (TcpBuffer)argAr.AsyncState;
if (vm.Socket.Connected)
var buffer = (TcpBuffer)ar.AsyncState;
if (buffer.Socket.Connected)
{
_length = vm.Socket.EndReceive(argAr);
_length = buffer.Socket.EndReceive(ar);
if (_length > 0)
{
_data = new byte[_length];
Array.Copy(vm.Buffer, 0, _data, 0, _length);
Array.Copy(buffer.Buffer, 0, _data, 0, _length);
_parent.RaiseReceived(_ip, _port, _data);
vm.Socket.BeginReceive(vm.Buffer, 0, vm.Buffer.Length, SocketFlags.None, Callback, vm);
buffer.Socket.BeginReceive(buffer.Buffer, 0, buffer.Buffer.Length, SocketFlags.None, Callback, buffer);
}
else
{
@ -71,7 +71,7 @@ namespace Apewer.Internals
}
if (_ep != null)
{
_parent.Remove(_ip + ":" + _port.ToString());
_parent.Remove(_ip + "-" + _port.ToString());
_parent.RaiseClosed(_ip, _port);
}
}
@ -81,9 +81,9 @@ namespace Apewer.Internals
if (!string.IsNullOrEmpty(_ip)) _parent.Close(_ip, _port);
}
}
catch (Exception argex)
catch (Exception ex)
{
_parent.RaiseExcepted(argex);
_parent.RaiseExcepted(ex);
if (_client != null)
{
if (_client.Connected) _client.Close();

129
Apewer/Network/TcpProxy.cs

@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Security;
using System.Text;
using System.Threading;
namespace Apewer.Network
{
/// <summary>TCP 端口代理。</summary>
public class TcpProxy
{
IPEndPoint _local;
Socket _listen;
Thread _thread;
int _backlog;
int _port;
Func<IPEndPoint, IPEndPoint> _remote;
/// <summary>本地端口已连接。</summary>
public bool Connected { get => _listen == null ? false : _listen.Connected; }
/// <summary>本地已监听的端口。</summary>
public int Port { get => _port; }
/// <summary>监听本地端口以启动代理。</summary>
/// <param name="local">本地监听端口。</param>
/// <param name="remote">获取要连接的远程端口,无法获取远程端口时应返回 NULL 值。</param>
/// <param name="backlog">挂起连接队列的最大长度</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
/// <exception cref="SocketException"></exception>
/// <exception cref="SecurityException"></exception>
public TcpProxy(IPEndPoint local, Func<IPEndPoint, IPEndPoint> remote, int backlog = 10000)
{
Start(local, remote, backlog);
}
void Start(IPEndPoint local, Func<IPEndPoint, IPEndPoint> remote, int backlog = 10000)
{
if (local == null) throw new ArgumentNullException(nameof(local));
if (remote == null) throw new ArgumentNullException(nameof(remote));
if (backlog < 1) throw new ArgumentOutOfRangeException(nameof(backlog));
_local = local;
_remote = remote;
_backlog = backlog;
var server = new Socket(_local.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
server.Bind(_local);
server.Listen(_backlog);
_listen = server;
if (server.LocalEndPoint is IPEndPoint lep) _port = lep.Port;
_thread = new Thread(Listen);
_thread.IsBackground = true;
_thread.Start();
}
/// <summary>停止代理。</summary>
public void Stop()
{
Close(_listen);
}
void Listen()
{
while (true)
{
if (_listen == null) break;
Socket socket1 = null;
IPEndPoint remote = null;
try
{
socket1 = _listen.Accept();
remote = _remote(socket1.RemoteEndPoint as IPEndPoint);
}
catch { }
if (socket1 == null || remote == null) continue;
Socket socket2 = null;
try
{
socket2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket2.Connect(remote);
ThreadPool.QueueUserWorkItem(Handler, new Socket[] { socket1, socket2 });
ThreadPool.QueueUserWorkItem(Handler, new Socket[] { socket2, socket1 });
}
catch
{
Close(socket1);
Close(socket2);
}
}
}
void Handler(object obj)
{
var tuple = (Socket[])obj;
var src = tuple[0];
var dst = tuple[1];
var buffer = new byte[1024];
while (true)
{
try
{
int count = src.Receive(buffer, buffer.Length, SocketFlags.None);
if (count < 1) break;
dst.Send(buffer, count, SocketFlags.None);
}
catch { break; }
}
Close(src);
Close(dst);
}
static void Close(Socket socket)
{
if (socket == null) return;
try { socket.Close(); } catch { }
}
}
}

130
Apewer/Network/TcpServer.cs

@ -12,28 +12,28 @@ namespace Apewer.Network
{
/// <summary>TCP 服务端。</summary>
public class TcpServer
internal class TcpServer
{
#region event
/// <summary>Exception。</summary>
public event Event<Exception> Excepted;
public Event<Exception> Excepted { get; set; }
/// <summary>服务端已启动。</summary>
public event SocketEndPointEventHandler Started;
public Event<SocketEndPoint> Started { get; set; }
/// <summary>服务端已关闭。</summary>
public event SocketEndPointEventHandler Quitted;
public Event<SocketEndPoint> Quitted { get; set; }
/// <summary>客户端已连接。</summary>
public event SocketEndPointEventHandler Connected;
public Event<SocketEndPoint> Connected { get; set; }
/// <summary>客户端已断开。</summary>
public event SocketEndPointEventHandler Closed;
public Event<SocketEndPoint> Closed { get; set; }
/// <summary>已收到客户端数据。</summary>
public event SocketReceivedEventHandler Received;
public Event<SocketReceived> Received { get; set; }
#region raise
@ -48,33 +48,34 @@ namespace Apewer.Network
{
var ip = _endpoint == null ? "" : _endpoint.Address.ToString();
var port = _endpoint == null ? 0 : _endpoint.Port;
Started(this, ip, port);
Started?.Invoke(this, new SocketEndPoint(ip, port));
}
}
internal void RaiseConnected(string ip, int port)
{
if (Connected != null) Connected(this, ip, port);
Connected?.Invoke(this, new SocketEndPoint(ip, port));
}
internal void RaiseClosed(string ip, int port)
{
if (Closed != null) Closed(this, ip, port);
Closed?.Invoke(this, new SocketEndPoint(ip, port));
}
internal void RaiseQuitted()
{
if (Quitted != null)
var quitted = Quitted;
if (quitted != null)
{
var ip = _endpoint == null ? "" : _endpoint.Address.ToString();
var port = _endpoint == null ? 0 : _endpoint.Port;
Quitted(this, ip, port);
quitted(this, new SocketEndPoint(ip, port));
}
}
internal void RaiseReceived(string ip, int port, byte[] bytes)
{
if (Received != null) Received(this, ip, port, bytes);
Received?.Invoke(this, new SocketReceived(ip, port, bytes));
}
#endregion
@ -123,17 +124,17 @@ namespace Apewer.Network
{
get
{
int vr = _port;
if (vr > 65535) vr = 65535;
if (vr < 0) vr = 0;
return vr;
int port = _port;
if (port > 65535) port = 65535;
if (port < 0) port = 0;
return port;
}
set
{
int vport = value;
if (vport < 0) vport = 0;
if (vport > 65535) vport = 65535;
_port = vport;
int port = value;
if (port < 0) port = 0;
if (port > 65535) port = 65535;
_port = port;
}
}
@ -164,7 +165,7 @@ namespace Apewer.Network
}
/// <summary>启动服务端。</summary>
public bool Start()
public bool Start(bool inBackground = true)
{
try
{
@ -176,20 +177,34 @@ namespace Apewer.Network
_socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.AcceptConnection, 1);
_socket.ReceiveTimeout = Timeout;
_socket.SendTimeout = Timeout;
if (inBackground)
{
_listener = new Thread(Listener);
_listener.IsBackground = Background;
_listener.Start();
_state = _listener.IsAlive;
var vcep = (IPEndPoint)_socket.LocalEndPoint;
_port = vcep.Port;
var cep = (IPEndPoint)_socket.LocalEndPoint;
_port = cep.Port;
RaiseStarted();
return true;
}
else
{
_state = true;
var cep = (IPEndPoint)_socket.LocalEndPoint;
_port = cep.Port;
RaiseStarted();
Listener();
return true;
}
}
catch (Exception ex)
{
_endpoint = null;
RaiseExcepted(ex);
if ((_socket != null) && _socket.Connected) _socket.Close();
_state = false;
Quit();
return false;
}
@ -206,12 +221,12 @@ namespace Apewer.Network
{
if (_client != null)
{
foreach (Socket vcs in _client.Values)
foreach (Socket socket in _client.Values)
{
try
{
var vep = (IPEndPoint)vcs.RemoteEndPoint;
Close(vep.Address.ToString(), vep.Port);
var ep = (IPEndPoint)socket.RemoteEndPoint;
Close(ep.Address.ToString(), ep.Port);
}
catch (Exception ex)
{
@ -224,19 +239,18 @@ namespace Apewer.Network
/// <summary>断开与指定客户端的连接。</summary>
public void Close(string ip, int port)
{
int vport = port;
if (vport < 0) vport = 0;
if (vport > 65535) vport = 65535;
if (port < 0) port = 0;
if (port > 65535) port = 65535;
if (!string.IsNullOrEmpty(ip))
{
var vck = ip + ":" + vport.ToString();
var vcs = Client(ip, vport);
var ck = ip + "-" + port.ToString();
var cs = Client(ip, port);
try
{
if (vcs != null) vcs.Close();
Remove(vck);
RaiseClosed(ip, vport);
if (cs != null) cs.Close();
Remove(ck);
RaiseClosed(ip, port);
}
catch (Exception ex)
{
@ -248,8 +262,8 @@ namespace Apewer.Network
/// <summary>向所有客户端广播数据。</summary>
public void Send(byte[] bytes)
{
var vl = bytes.Length;
if ((_client.Count > 0) && (vl > 0))
var length = bytes.Length;
if ((_client.Count > 0) && (length > 0))
{
foreach (Socket i in _client.Values) Send(bytes, i);
}
@ -258,9 +272,8 @@ namespace Apewer.Network
/// <summary>向指定客户端发送数据。</summary>
public bool Send(byte[] bytes, string ip, int port)
{
int vport = port;
if (vport < 0) vport = 0;
if (vport > 65535) vport = 65535;
if (port < 0) port = 0;
if (port > 65535) port = 65535;
if (string.IsNullOrEmpty(ip)) return false;
return Send(bytes, Client(ip, port));
@ -326,12 +339,12 @@ namespace Apewer.Network
private bool Send(byte[] bytes, Socket client)
{
var vl = bytes.Length;
if ((client != null) && (vl > 0))
var length = bytes.Length;
if ((client != null) && (length > 0))
{
try
{
return (client.Send(bytes, vl, SocketFlags.None) > 0) ? true : false;
return (client.Send(bytes, length, SocketFlags.None) > 0) ? true : false;
}
catch (Exception ex) { RaiseExcepted(ex); }
}
@ -344,18 +357,18 @@ namespace Apewer.Network
{
try
{
var vcs = _socket.Accept();
if (vcs != null)
var socket = _socket.Accept();
if (socket != null)
{
var vep = (IPEndPoint)vcs.RemoteEndPoint;
var vck = vep.Address.ToString() + ":" + vep.Port.ToString();
_client.Add(vck, vcs);
var vci = new TcpInstance(this, _socket, vcs);
var vct = new Thread(vci.Process);
vct.IsBackground = true;
vct.Name = vck;
vct.Start();
RaiseConnected(vep.Address.ToString(), vep.Port);
var ep = (IPEndPoint)socket.RemoteEndPoint;
var key = ep.Address.ToString() + "-" + ep.Port.ToString();
_client.Add(key, socket);
var instance = new TcpInstance(this, _socket, socket);
var thread = new Thread(instance.Process);
thread.IsBackground = true;
thread.Name = key;
thread.Start();
RaiseConnected(ep.Address.ToString(), ep.Port);
}
}
catch (Exception ex)
@ -368,13 +381,12 @@ namespace Apewer.Network
private Socket Client(string ip, int port)
{
int vport = port;
if (vport < 0) vport = 0;
if (vport > 65535) vport = 65535;
if (port < 0) port = 0;
if (port > 65535) port = 65535;
try
{
var vck = ip + ":" + vport.ToString();
if (_client.ContainsKey(vck)) return (Socket)_client[vck];
var ck = ip + "-" + port.ToString();
if (_client.ContainsKey(ck)) return (Socket)_client[ck];
}
catch (Exception ex)
{

22
Apewer/Network/UdpServer.cs

@ -23,16 +23,16 @@ namespace Apewer.Network
private int _port = 0;
/// <summary>Exception。</summary>
public event Event<Exception> Excepted;
public Event<Exception> Excepted { get; set; }
/// <summary>服务端已启动。</summary>
public event EventHandler Started;
public Event Started { get; set; }
/// <summary>服务端已关闭。</summary>
public event EventHandler Quitted;
public Event Quitted { get; set; }
/// <summary>已收到客户端数据。</summary>
public event SocketReceivedEventHandler Received;
public Event<SocketReceived> Received { get; set; }
/// <summary>构造函数。</summary>
public UdpServer()
@ -90,7 +90,7 @@ namespace Apewer.Network
{
_udp.Close();
_udp = null;
if (Quitted != null) Quitted(this, new EventArgs());
Quitted?.Invoke(this);
}
}
@ -100,13 +100,15 @@ namespace Apewer.Network
{
var ep = new IPEndPoint(IPAddress.Any, Port);
_udp = new System.Net.Sockets.UdpClient(ep);
if (Started != null) Started(this, new EventArgs());
Started?.Invoke(this);
while (true)
{
var vbytes = _udp.Receive(ref ep);
if ((Received != null) && (vbytes.Length > 0))
var bytes = _udp.Receive(ref ep);
if ((Received != null) && (bytes.Length > 0))
{
Received(this, ep.Address.ToString(), ep.Port, vbytes);
var ip = ep.Address.ToString();
var port = ep.Port;
Received?.Invoke(this, new SocketReceived(ip, ep.Port, bytes));
}
// Thread.Sleep(1);
}
@ -115,7 +117,7 @@ namespace Apewer.Network
{
if (Excepted != null) Excepted(this, ex);
}
if (Quitted != null) Quitted(this, new EventArgs());
Quitted.Invoke(this);
}
}

21
Apewer/Network/_Delegate.cs

@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
namespace Apewer.Network
{
/// <summary></summary>
public delegate void SocketReceivedEventHandler(object sender, string ip, int port, byte[] bytes);
/// <summary></summary>
public delegate void SocketEndPointEventHandler(object sender, string ip, int port);
///// <summary></summary>
//public delegate void SocketExceptionCallback(object argSender, SocketException value);
///// <summary>返回 Http Progress 对象。</summary>
//public delegate void HttpProgressCallback(object argSender, HttpProgress argValue);
}

58
Apewer/NetworkUtility.cs

@ -255,7 +255,7 @@ namespace Apewer
}
/// <summary>转换 IP 地址格式。</summary>
public static IPEndPoint GetIPEndPoint(EndPoint endpoint)
public static IPEndPoint GetIPEndPoint(System.Net.EndPoint endpoint)
{
try { return (IPEndPoint)endpoint; }
catch { return new IPEndPoint(0, 0); }
@ -323,6 +323,62 @@ namespace Apewer
return HttpClient.SimpleForm(url, form, timeout);
}
/// <summary>获取 HTTP 状态的文本。</summary>
public static string HttpStatusDescription(int code)
{
switch (code)
{
case 100: return "Continue";
case 101: return "Switching Protocols";
case 102: return "Processing";
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-Authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 207: return "Multi-Status";
case 300: return "Multiple Choices";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
case 307: return "Temporary Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Timeout";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Request Entity Too Large";
case 414: return "Request-Uri Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Requested Range Not Satisfiable";
case 417: return "Expectation Failed";
case 422: return "Unprocessable Entity";
case 423: return "Locked";
case 424: return "Failed Dependency";
case 426: return "Upgrade Required"; // RFC 2817
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "Http Version Not Supported";
case 507: return "Insufficient Storage";
default: return null;
}
}
#endregion
#region Port

71
Apewer/RuntimeUtility.cs

@ -734,7 +734,44 @@ namespace Apewer
#endregion
#region Debug or Test
#region Evaluate
/// <summary>评估 Action 的执行时间,单位为毫秒。</summary>
public static long Evaluate(Action action)
{
if (action == null) return 0;
var stopwatch = new Stopwatch();
stopwatch.Start();
action.Invoke();
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
#endregion
#region Console
/// <summary>获取或设置控制台标题。</summary>
public static string Title
{
get { try { return Console.Title; } catch { return null; } }
set { try { Console.Title = value; } catch { } }
}
/// <summary>设置在控制台中按 CTRL + C 时的方法。</summary>
public static void CtrlCancel(Func<bool> exit)
{
const string postfix = " - 按 CTRL + C 可安全退出";
var title = Title ?? "";
if (!title.EndsWith(postfix))
{
title = title + postfix;
Title = title;
}
if (exit == null) return;
try { Console.CancelKeyPress += (s, e) => e.Cancel = !exit(); } catch { }
}
/// <summary>调用所有公共类,创建构造函数。</summary>
/// <param name="assembly">要搜索类的程序集,指定为 NULL 时将获取当前程序集。</param>
@ -744,7 +781,7 @@ namespace Apewer
public static void InvokePublicClass(Assembly assembly = null, bool catchException = false)
{
const string Title = "Invoing Public Class";
Console.Title = Title;
RuntimeUtility.Title = Title;
var before = DateTime.Now;
Logger.Internals.Info(typeof(RuntimeUtility), "Started Invoke.");
@ -756,7 +793,7 @@ namespace Apewer
if (!type.IsPublic) continue;
if (!CanNew(type)) continue;
Logger.Internals.Info(typeof(RuntimeUtility), "Invoke " + type.FullName);
Console.Title = type.FullName;
RuntimeUtility.Title = type.FullName;
if (catchException)
{
@ -774,7 +811,7 @@ namespace Apewer
Activator.CreateInstance(type);
}
}
Console.Title = Title;
RuntimeUtility.Title = Title;
var after = DateTime.Now;
var span = after - before;
@ -788,16 +825,22 @@ namespace Apewer
Logger.Internals.Info(typeof(RuntimeUtility), result);
}
/// <summary>评估 Action 的执行时间,单位为毫秒。</summary>
public static long Evaluate(Action action)
{
if (action == null) return 0;
var stopwatch = new Stopwatch();
stopwatch.Start();
action.Invoke();
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
#endregion
#region System
#if NETSTD || NETCORE
/// <summary>当前操作系统是 Windows。</summary>
public static bool IsWindows { get => System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows); }
/// <summary>当前操作系统是 OS X 或 macOS。</summary>
public static bool IsOSX { get => System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX); }
/// <summary>当前操作系统是 Linux。</summary>
public static bool IsLinux { get => System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux); }
#endif
#endregion

0
Apewer.Source/Source/ColumnInfo.cs → Apewer/Source/ColumnInfo.cs

339
Apewer/Source/DbClient.cs

@ -1,339 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Text;
namespace Apewer.Source
{
/// <summary></summary>
abstract class DbClient
{
/// <summary></summary>
public virtual Logger Logger { get; set; }
#region Connection
DbConnection _conn = null;
string _str = null;
/// <summary></summary>
public Timeout Timeout { get; set; }
/// <summary></summary>
public DbConnection Connection { get => _conn; }
/// <summary></summary>
public bool Online { get => _conn == null ? false : (_conn.State == ConnectionState.Open); }
/// <summary>连接字符串。</summary>
public string ConnectionString { get => _str; }
/// <summary></summary>
public virtual string Connect()
{
if (_conn == null)
{
_str = GetConnectionString();
_conn = NewConnection();
_conn.ConnectionString = _str;
}
else
{
if (_conn.State == ConnectionState.Open) return null;
}
try
{
_conn.Open();
switch (_conn.State)
{
case ConnectionState.Open: return null;
default: return $"连接失败,当前处于 {_conn.State} 状态。";
}
}
catch (Exception ex)
{
Logger.Error(this, "Connect", ex, _conn.ConnectionString);
Close();
return ex.Message;
}
}
/// <summary></summary>
public void Close()
{
if (_conn != null)
{
if (_transaction != null)
{
if (_autocommit) Commit();
else Rollback();
}
_conn.Close();
_conn.Dispose();
_conn = null;
}
}
/// <summary></summary>
public void Dispose() { Close(); }
#endregion
#region Transaction
private DbTransaction _transaction = null;
private bool _autocommit = false;
/// <summary>启动事务。</summary>
public string Begin(bool commit = true) => Begin(commit, null);
/// <summary>启动事务。</summary>
public string Begin(bool commit, Class<System.Data.IsolationLevel> isolation)
{
if (Connect() != null) return "未连接。";
if (_transaction != null) return "存在已启动的事务,无法再次启动。";
try
{
_transaction = isolation ? _conn.BeginTransaction(isolation.Value) : _conn.BeginTransaction();
_autocommit = commit;
return null;
}
catch (Exception ex)
{
Logger.Error(this, "Commit", ex.Message());
return ex.Message();
}
}
/// <summary>提交事务。</summary>
public string Commit()
{
if (_transaction == null) return "事务不存在。";
try
{
_transaction.Commit();
RuntimeUtility.Dispose(_transaction);
_transaction = null;
return null;
}
catch (Exception ex)
{
RuntimeUtility.Dispose(_transaction);
_transaction = null;
Logger.Error(this, "Commit", ex.Message());
return ex.Message();
}
}
/// <summary>从挂起状态回滚事务。</summary>
public string Rollback()
{
if (_transaction == null) return "事务不存在。";
try
{
_transaction.Rollback();
RuntimeUtility.Dispose(_transaction);
_transaction = null;
return null;
}
catch (Exception ex)
{
RuntimeUtility.Dispose(_transaction);
_transaction = null;
Logger.Error(this, "Rollback", ex.Message());
return ex.Message();
}
}
#endregion
#region ADO
/// <summary>查询。</summary>
public IQuery Query(string sql) => Query(sql, null);
/// <summary>查询。</summary>
public IQuery Query(string sql, IEnumerable<IDataParameter> parameters)
{
if (TextUtility.IsBlank(sql)) return new Query(false, "语句无效。");
var connected = Connect();
if (connected.NotEmpty()) return new Query(false, connected);
try
{
using (var command = NewCommand())
{
command.Connection = _conn;
if (Timeout != null) command.CommandTimeout = Timeout.Query;
command.CommandText = sql;
if (parameters != null)
{
foreach (var parameter in parameters)
{
if (parameter != null) command.Parameters.Add(parameter);
}
}
using (var ds = new DataSet())
{
using (var da = NewDataAdapter(sql))
{
var name = "table_" + Guid.NewGuid().ToString("n");
da.Fill(ds, name);
var table = ds.Tables[name];
return new Query(table, true);
}
}
}
}
catch (Exception exception)
{
Logger.Error(this, "Query", exception, sql);
return new Query(exception);
}
}
/// <summary>执行。</summary>
public IExecute Execute(string sql) => Execute(sql, null);
/// <summary>执行单条 Transact-SQL 语句,并加入参数。</summary>
public IExecute Execute(string sql, IEnumerable<IDataParameter> parameters)
{
if (TextUtility.IsBlank(sql)) return Example.InvalidExecuteStatement;
var connected = Connect();
if (connected.NotEmpty()) return new Execute(false, connected);
var inTransaction = _transaction != null;
if (!inTransaction) Begin();
try
{
using (var command = NewCommand())
{
command.Connection = _conn;
command.Transaction = (DbTransaction)_transaction;
if (Timeout != null) command.CommandTimeout = Timeout.Execute;
command.CommandText = sql;
if (parameters != null)
{
foreach (var parameter in parameters)
{
if (parameter != null) command.Parameters.Add(parameter);
}
}
var rows = command.ExecuteNonQuery();
if (!inTransaction) Commit(); // todo 此处应该检查事务提交产生的错误。
return new Execute(true, rows);
}
}
catch (Exception exception)
{
Logger.Error(this, "Execute", exception, sql);
if (!inTransaction) Rollback();
return new Execute(exception);
}
}
#endregion
#region ORM - Query
/// <summary>查询记录。</summary>
/// <param name="model">记录模型。</param>
/// <param name="sql">SQL 语句。</param>
public Result<object[]> Query(Type model, string sql)
{
if (_conn == null) return new Result<object[]>("连接无效。");
if (model == null) return new Result<object[]>("数据模型类型无效。");
if (string.IsNullOrEmpty(sql)) return new Result<object[]>("SQL 语句无效。");
var query = Query(sql);
var result = null as Result<object[]>;
if (query.Success)
{
try
{
var array = OrmHelper.Fill(query, model);
result = new Result<object[]>(array);
}
catch (Exception ex)
{
result = new Result<object[]>(ex);
}
}
else
{
result = new Result<object[]>(query.Message);
}
query.Dispose();
return result;
}
/// <summary></summary>
public Result<T[]> Query<T>(string sql) where T : class, new()
{
var query = Query(sql);
if (!query.Success) return new Result<T[]>(query.Message);
var records = query.Fill<T>();
query.Dispose();
var result = new Result<T[]>(records);
return result;
}
#endregion
#region Static
/// <summary>对文本转义,符合 SQL 安全性。可根据字段类型限制 UTF-8 字节数,默认为 0 时不限制字节数。</summary>
protected static string Escape(string text, int bytes = 0)
{
if (text.IsEmpty()) return "";
var t = text ?? "";
t = t.Replace("\\", "\\\\");
t = t.Replace("'", "\\'");
t = t.Replace("\n", "\\n");
t = t.Replace("\r", "\\r");
t = t.Replace("\b", "\\b");
t = t.Replace("\t", "\\t");
t = t.Replace("\f", "\\f");
if (bytes > 5)
{
if (t.Bytes(Encoding.UTF8).Length > bytes)
{
while (true)
{
t = t.Substring(0, t.Length - 1);
if (t.Bytes(Encoding.UTF8).Length <= (bytes - 4)) break;
}
t = t + " ...";
}
}
return t;
}
#endregion
/// <summary></summary>
protected abstract string GetConnectionString();
/// <summary></summary>
protected abstract DbConnection NewConnection();
/// <summary></summary>
protected abstract DbDataAdapter NewDataAdapter(string sql);
/// <summary></summary>
protected abstract DbCommand NewCommand();
}
}

75
Apewer/Source/IDbAdo.cs

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
namespace Apewer.Source
{
/// <summary>数据库客户端。</summary>
public interface IDbAdo : IDisposable
{
#region Connection
/// <summary>获取连接。</summary>
IDbConnection Connection { get; }
/// <summary>数据库当前在线,表示连接可用。</summary>
bool Online { get; }
/// <summary>连接数据库,若未连接则尝试连接,返回错误信息。</summary>
string Connect();
#endregion
#region ADO
/// <summary>查询。</summary>
IQuery Query(string statement);
/// <summary>查询。</summary>
IQuery Query(string statement, IEnumerable<IDataParameter> parameters);
/// <summary>执行。</summary>
IExecute Execute(string statement);
/// <summary>执行。</summary>
IExecute Execute(string statement, IEnumerable<IDataParameter> parameters);
// /// <summary>获取当前的事务对象。</summary>
// IDbTransaction Transaction { get; }
#endregion
#region Transaction
/// <summary>
/// <para>启动事务,可指定事务锁定行为。</para>
/// <para>Chaos<br />无法覆盖隔离级别更高的事务中的挂起的更改。</para>
/// <para>ReadCommitted<br />在正在读取数据时保持共享锁,以避免脏读,但是在事务结束之前可以更改数据,从而导致不可重复的读取或幻像数据。</para>
/// <para>ReadUncommitted<br />可以进行脏读,意思是说,不发布共享锁,也不接受独占锁。</para>
/// <para>RepeatableRead<br />在查询中使用的所有数据上放置锁,以防止其他用户更新这些数据。 防止不可重复的读取,但是仍可以有幻像行。</para>
/// <para>Serializable<br />在 System.Data.DataSet 上放置范围锁,以防止在事务完成之前由其他用户更新行或向数据集中插入行。</para>
/// <para>Snapshot<br />通过在一个应用程序正在修改数据时存储另一个应用程序可以读取的相同数据版本来减少阻止。 表示您无法从一个事务中看到在其他事务中进行的更改,即便重新查询也是如此。</para>
/// <para>Unspecified = -1<br />正在使用与指定隔离级别不同的隔离级别,但是无法确定该级别。</para>
/// </summary>
/// <param name="commit">在连接的生命周期结束时未结束事务,指定 TRUE 将自动提交,指定 FALSE 将自动回滚。</param>
/// <param name="isolation">指定事务锁定行为,不指定时将使用默认值。</param>
string Begin(bool commit = false, Class<IsolationLevel> isolation = null);
/// <summary>提交事务。</summary>
/// <remarks>异常常见于事务已经提交或连接已断开。</remarks>
/// <returns>提交失败时返回错误信息,成功时返回 NULL 值。</returns>
string Commit();
/// <summary>从挂起状态回滚事务。</summary>
/// <remarks>异常常见于事务已经提交、已回滚或连接已断开。</remarks>
/// <returns>提交失败时返回错误信息,成功时返回 NULL 值。</returns>
string Rollback();
#endregion
}
}

10
Apewer/Source/IDbClient.cs

@ -6,7 +6,13 @@ using System.Data;
namespace Apewer.Source
{
/// <summary>数据库引擎接口。</summary>
public interface IDbClient : IDisposable, IDbClientBase, IDbClientAdo, IDbClientOrm { }
/// <summary>数据库客户端。</summary>
public interface IDbClient : IDisposable, IDbAdo, IDbOrm
{
/// <summary>获取或设置日志记录器。</summary>
Logger Logger { get; set; }
}
}

72
Apewer/Source/IDbClientAdo.cs

@ -1,72 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
namespace Apewer.Source
{
/// <summary>数据库访问接口。</summary>
public interface IDbClientAdo : IDisposable
{
#region Connection
/// <summary>获取连接。</summary>
IDbConnection Connection { get; }
/// <summary>数据库当前在线,表示连接可用。</summary>
bool Online { get; }
/// <summary>连接数据库,若未连接则尝试连接,返回错误信息。</summary>
string Connect();
#endregion
#region SQL
/// <summary>查询。</summary>
IQuery Query(string statement);
/// <summary>查询。</summary>
IQuery Query(string statement, IEnumerable<IDataParameter> parameters);
/// <summary>执行。</summary>
IExecute Execute(string statement);
/// <summary>执行。</summary>
IExecute Execute(string statement, IEnumerable<IDataParameter> parameters);
// /// <summary>获取当前的事务对象。</summary>
// IDbTransaction Transaction { get; }
#endregion
#region Transaction
// /// <summary>启动事务。</summary>
// /// <param name="isolation">事务锁定:默认为快照方式,在完成提交前,其它连接无法获取当前事务挂起的更改。</param>
// /// <param name="commit">当关闭连接时,提交或回滚未处理的事务。</param>
// /// <remarks>当存在已经启动的事务时,无法再次启动(返回 NULL 值)。</remarks>
// string Begin(IsolationLevel isolation = IsolationLevel.Snapshot, bool commit = true);
/// <summary>启动事务。</summary>
/// <param name="commit">当关闭连接时,提交或回滚未处理的事务。</param>
/// <remarks>当存在已经启动的事务时,无法再次启动(返回 NULL 值)。</remarks>
string Begin(bool commit = true);
/// <summary>提交事务。</summary>
/// <remarks>异常常见于事务已经提交或连接已断开。</remarks>
/// <returns>提交失败时返回错误信息,成功时返回 NULL 值。</returns>
string Commit();
/// <summary>从挂起状态回滚事务。</summary>
/// <remarks>异常常见于事务已经提交、已回滚或连接已断开。</remarks>
/// <returns>提交失败时返回错误信息,成功时返回 NULL 值。</returns>
string Rollback();
#endregion
}
}

17
Apewer/Source/IDbClientBase.cs

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Source
{
/// <summary>数据库引擎接口。</summary>
public interface IDbClientBase : IDisposable
{
/// <summary>获取或设置日志记录器。</summary>
Logger Logger { get; set; }
}
}

15
Apewer/Source/IDbClientOrm.cs → Apewer/Source/IDbOrm.cs

@ -1,14 +1,17 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
namespace Apewer.Source
{
/// <summary>数据库引擎支持 ORM 访问。</summary>
public interface IDbClientOrm
/// <summary>数据库客户端。</summary>
public interface IDbOrm
{
#region Orm
/// <summary>初始化指定类型,以创建表或增加字段。</summary>
/// <param name="model">要初始化的类型。</param>
/// <returns>错误信息。当成功时候返回空字符串。</returns>
@ -53,11 +56,13 @@ namespace Apewer.Source
/// <summary>使用指定语句查询,获取查询结果。</summary>
/// <param name="model">目标记录的类型。</param>
/// <param name="sql">要执行的 SQL 语句。</param>
public Result<object[]> Query(Type model, string sql);
/// <param name="parameters">为 SQL 命令提供参数。</param>
public Result<object[]> Query(Type model, string sql, IEnumerable<IDataParameter> parameters = null);
/// <summary>使用指定语句查询,获取查询结果。</summary>
/// <param name="sql">要执行的 SQL 语句。</param>
public Result<T[]> Query<T>(string sql) where T : class, new();
/// <param name="parameters">为 SQL 命令提供参数。</param>
public Result<T[]> Query<T>(string sql, IEnumerable<IDataParameter> parameters = null) where T : class, new();
/// <summary>查询所有记录。</summary>
/// <param name="model">目标记录的类型。</param>
@ -68,6 +73,8 @@ namespace Apewer.Source
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<T[]> Query<T>(long flag = 0) where T : class, IRecord, new();
#endregion
}
}

4
Apewer/Source/IRecordMoment.cs

@ -14,6 +14,10 @@ namespace Apewer.Source
/// <summary>记录的更新时间,每次对此记录执行 UPDATE 时应更新此值为当前系统时间,字段长度不应超过 255 个字符。</summary>
string Updated { get; set; }
/// <summary>获取 Created 和 Updated 属性的值。</summary>
string GenerateMoment();
}
}

3
Apewer/Source/IRecordStamp.cs

@ -15,6 +15,9 @@ namespace Apewer.Source
/// <summary>记录的更新时间,默认为本地时间。</summary>
long Updated { get; set; }
/// <summary>获取 Created 和 Updated 属性的值。</summary>
long GenerateStamp();
}
}

37
Apewer/Source/OrmHelper.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using static Apewer.NumberUtility;
@ -195,12 +196,13 @@ namespace Apewer.Source
/// <param name="database">数据库对象。</param>
/// <param name="model">记录模型。</param>
/// <param name="sql">SQL 语句。</param>
public static Result<object[]> Query(IDbClientAdo database, Type model, string sql)
/// <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))
using (var query = database.Query(sql, parameters))
{
if (query == null) return new Result<object[]>("查询实例无效。");
if (query.Table == null)
@ -230,14 +232,14 @@ namespace Apewer.Source
/// <param name="database">数据库对象。</param>
/// <param name="model">记录模型。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
public static Result<object[]> Query(IDbClientAdo database, Type model, Func<string, string> sqlGetter)
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).Name;
if (string.IsNullOrEmpty(tableName)) return new Result<object[]>("表名无效。");
return Query(database, model, sqlGetter(tableName));
return Query(database, model, sqlGetter(tableName), null);
}
catch (Exception ex)
{
@ -249,14 +251,14 @@ namespace Apewer.Source
/// <typeparam name="T">记录模型。</typeparam>
/// <param name="database">数据库对象。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
public static Result<T[]> Query<T>(IDbClientAdo database, Func<string, string> sqlGetter) where T : class, new() => As<object, T>(Query(database, typeof(T), sqlGetter));
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(IDbClientAdo database, Type model, string key, Func<string, string, string> sqlGetter)
public static Result<object> Get(IDbAdo database, Type model, string key, Func<string, string, string> sqlGetter)
{
if (sqlGetter == null) return new Result<object>("SQL 语句获取函数无效。");
@ -292,13 +294,13 @@ namespace Apewer.Source
/// <param name="database">数据库对象。</param>
/// <param name="key">主键。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名和主键值。</param>
public static Result<T> Get<T>(IDbClientAdo database, string key, Func<string, string, string> sqlGetter) where T : class, IRecord, new() => As<object, T>(Get(database, typeof(T), key, sqlGetter));
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(IDbClientAdo database, Type model, Func<string, string> sqlGetter)
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[]>("模型类型无效。");
@ -359,7 +361,7 @@ namespace Apewer.Source
/// <typeparam name="T">记录模型。</typeparam>
/// <param name="database">数据库对象。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
public static Result<string[]> Keys<T>(IDbClientAdo database, Func<string, string> sqlGetter) where T : IRecord
public static Result<string[]> Keys<T>(IDbAdo database, Func<string, string> sqlGetter) where T : IRecord
{
return Keys(database, typeof(T), sqlGetter);
}
@ -378,17 +380,17 @@ namespace Apewer.Source
if (string.IsNullOrEmpty(key.Key)) key.ResetKey();
}
var now = DateTime.Now;
if (record is IRecordMoment moment)
{
if (string.IsNullOrEmpty(moment.Created)) moment.Created = now.Lucid();
if (string.IsNullOrEmpty(moment.Updated)) moment.Updated = now.Lucid();
var now = moment.GenerateMoment();
if (string.IsNullOrEmpty(moment.Created)) moment.Created = now;
if (string.IsNullOrEmpty(moment.Updated)) moment.Updated = now;
}
if (record is IRecordStamp stamp)
{
var utc = ClockUtility.UtcStamp;
if (stamp.Created == 0L) stamp.Created = now.Stamp();
if (stamp.Updated == 0L) stamp.Updated = now.Stamp();
var now = stamp.GenerateStamp();
if (stamp.Created == 0L) stamp.Created = now;
if (stamp.Updated == 0L) stamp.Updated = now;
}
}
@ -397,16 +399,15 @@ namespace Apewer.Source
public static bool SetUpdated(object record)
{
if (record == null) return false;
var now = DateTime.Now;
var setted = false;
if (record is IRecordMoment moment)
{
moment.Updated = now.Lucid();
moment.Updated = moment.GenerateMoment();
setted = true;
}
if (record is IRecordStamp stamp)
{
stamp.Updated = now.Stamp();
stamp.Updated = stamp.GenerateStamp();
setted = true;
}
return setted;

19
Apewer/Source/Query.cs

@ -305,7 +305,7 @@ namespace Apewer.Source
private static ObjectDisposedException DisposedException { get { return new ObjectDisposedException(typeof(Query).FullName); } }
/// <summary>简单查询:取结果中第 0 列所有单元格的文本形式,可指定查询后关闭服务器连接,返回结果中不包含无效文本。</summary>
public static string[] SimpleColumn(IDbClientAdo database, string statement, bool dispose = false)
public static string[] SimpleColumn(IDbAdo database, string statement, bool dispose = false)
{
if (database == null) return new string[0];
var ab = new ArrayBuilder<string>();
@ -329,7 +329,7 @@ namespace Apewer.Source
}
/// <summary>简单查询:取结果中第 0 行、第 0 列单元格中的文本,可指定查询后关闭服务器连接。</summary>
public static string SimpleCell(IDbClientAdo database, string statement, bool dispose = false)
public static string SimpleCell(IDbAdo database, string statement, bool dispose = false)
{
if (database == null) return "";
var query = database.Query(statement);
@ -345,19 +345,12 @@ namespace Apewer.Source
internal static string Text(object value)
{
var result = Constant.EmptyString;
if (value != null)
if (value != null && !value.Equals(DBNull.Value))
{
if (!value.Equals(DBNull.Value))
{
try
{
result = value.ToString();
}
finally { }
}
if (value is string str) return str;
try { return value.ToString() ?? ""; } catch { }
}
return result;
return "";
}
internal static Class<DateTime> DateTime(object value)

4
Apewer/Source/Timeout.cs

@ -39,8 +39,8 @@ namespace Apewer.Source
set { _execute = (value >= 0) ? value : 0; }
}
/// <summary>默认超时设置:连接 10000、查询 60000,执行 60000。</summary>
public static Timeout Default { get => new Timeout(10000, 60000, 60000); }
/// <summary>默认超时设置:连接 5000、查询 60000,执行 60000。</summary>
public static Timeout Default { get => new Timeout(5000, 60000, 60000); }
}

45
Apewer/SystemUtility.cs

@ -1,45 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer
{
/// <summary>系统实用工具。</summary>
public class SystemUtility
{
#if NETSTD || NETCORE
/// <summary>当前操作系统是 Windows。</summary>
public static bool IsWindows { get => System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows); }
/// <summary>当前操作系统是 OS X 或 macOS。</summary>
public static bool IsOSX { get => System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX); }
/// <summary>当前操作系统是 Linux。</summary>
public static bool IsLinux { get => System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux); }
#endif
/// <summary></summary>
public static void SetConsoleCtrlCancel(Func<bool> exit)
{
const string postfix = " - 按 CTRL + C 可安全退出";
var title = Console.Title;
if (!title.EndsWith(postfix))
{
title = title + postfix;
Console.Title = title;
}
if (exit == null) return;
Console.CancelKeyPress += (s, e) =>
{
e.Cancel = !exit();
};
}
}
}

17
Apewer/_Delegates.cs

@ -22,11 +22,26 @@ namespace Apewer
// public delegate TResult Func<in T, out TResult>(T arg);
/// <summary></summary>
public delegate void Event(object sender, object argument);
public delegate void Event(object sender);
/// <summary></summary>
public delegate void Event<T>(object sender, T argument);
/// <summary></summary>
public delegate void Event<T1, T2>(object sender, T1 arg1, T2 arg2);
/// <summary></summary>
public delegate void Event<T1, T2, T3>(object sender, T1 arg1, T2 arg2, T3 arg3);
/// <summary></summary>
public delegate void Event<T1, T2, T3, T4>(object sender, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
/// <summary></summary>
public delegate void Event<T1, T2, T3, T4, T5>(object sender, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
/// <summary></summary>
public delegate void Event<T1, T2, T3, T4, T5, T6>(object sender, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
/// <summary>数组回调。</summary>
public delegate T ArrayCallback<T>(object sender, params T[] arguments);

6
Apewer/_Extensions.cs

@ -15,6 +15,12 @@ using System.Text;
public static class Extensions_Apewer
{
/// <summary>是 NULL 值。</summary>
public static bool IsNull(this object @this) => @this == null || @this.Equals(DBNull.Value);
/// <summary>不是 NULL 值。</summary>
public static bool NotNull(this object @this) => @this != null && !@this.Equals(DBNull.Value);
#region Class Utility
/// <summary>克隆对象,创建新对象。可指定包含对象的属性和字段。</summary>

8
ChangeLog.md

@ -1,6 +1,14 @@

### 最新提交
### 6.5.6
- Global:增加 IsNull 和 NotNull 扩展方法;
- ArrayBuilder:添加了 Add 重载方法;
- Network:新增 TcpProxy 类;
- RuntimeUtility:屏蔽 Console.Title 抛出的异常;
- Source:IRecordMoment 和 IRecordStamp 不再生成值,将由实现类提供;
- Source:修正 Query 方法中查询超时不生效的问题。
### 6.5.5
- Source:调整了 SqlClient 的查询超时设置。

Loading…
Cancel
Save