Browse Source

Apewer-6.4.1

dev
王厅 4 years ago
parent
commit
d23612141a
  1. 12
      Apewer.Source/Apewer.Source.csproj
  2. 31
      Apewer.Source/Internals/TextHelper.cs
  3. 274
      Apewer.Source/Source/Access.cs
  4. 402
      Apewer.Source/Source/MySql.cs
  5. 643
      Apewer.Source/Source/SqlClient.cs
  6. 26
      Apewer.Source/Source/SqlServerSouce.cs
  7. 420
      Apewer.Source/Source/Sqlite.cs
  8. 12
      Apewer.Web/Internals/ApiHelper.cs
  9. 2
      Apewer.Web/Web/ApiEntries.cs
  10. 8
      Apewer.Web/Web/ApiProcessor.cs
  11. 6
      Apewer.Web/Web/ApiProgram.cs
  12. 31
      Apewer/ArrayBuilder.cs
  13. 39
      Apewer/ClockUtility.cs
  14. 100
      Apewer/Json.cs
  15. 2
      Apewer/Network/HttpClient.cs
  16. 16
      Apewer/Result.cs
  17. 45
      Apewer/RuntimeUtility.cs
  18. 167
      Apewer/Source/ColumnAttribute.cs
  19. 4
      Apewer/Source/ColumnType.cs
  20. 26
      Apewer/Source/Example.cs
  21. 78
      Apewer/Source/Execute.cs
  22. 14
      Apewer/Source/HttpRecord.cs
  23. 23
      Apewer/Source/IDatabaseBase.cs
  24. 21
      Apewer/Source/IDatabaseExecute.cs
  25. 21
      Apewer/Source/IDatabaseQuery.cs
  26. 2
      Apewer/Source/IDbClient.cs
  27. 72
      Apewer/Source/IDbClientAdo.cs
  28. 17
      Apewer/Source/IDbClientBase.cs
  29. 14
      Apewer/Source/IDbClientOrm.cs
  30. 6
      Apewer/Source/IExecute.cs
  31. 37
      Apewer/Source/IQuery.cs
  32. 6
      Apewer/Source/IRecord.cs
  33. 198
      Apewer/Source/OrmHelper.cs
  34. 20
      Apewer/Source/Parameter.cs
  35. 418
      Apewer/Source/Query.cs
  36. 20
      Apewer/Source/Record.cs
  37. 81
      Apewer/Source/TableAttribute.cs
  38. 408
      Apewer/Source/TableStructure.cs
  39. 4
      Apewer/Source/Timeout.cs
  40. 6
      Apewer/StringPairs.cs
  41. 9
      Apewer/TextUtility.cs
  42. 4
      Apewer/Web/ApiOptions.cs
  43. 6
      Apewer/Web/ApiUtility.cs
  44. 20
      Apewer/Web/DefaultController.cs
  45. 5
      Apewer/_Common.props
  46. 51
      Apewer/_Extensions.cs
  47. 10
      ChangeLog.md

12
Apewer.Source/Apewer.Source.csproj

@ -27,6 +27,18 @@
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.4.1" />
<PackageReference Include="System.Data.SqlClient" Version="4.4.3" />
<PackageReference Include="System.Security.Permissions" Version="4.4.1" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.4.0" />
</ItemGroup>
<!-- .NET Core 3.1 -->
<PropertyGroup>
<DefineConstants Condition="'$(TargetFramework)'=='netcoreapp3.1'">$(DefineConstants);MYSQL_6_10;</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netcoreapp3.1'">
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.4.1" />
<PackageReference Include="System.Data.SqlClient" Version="4.4.3" />
<PackageReference Include="System.Security.Permissions" Version="4.4.1" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.4.0" />
</ItemGroup>

31
Apewer.Source/Internals/TextHelper.cs

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Internals
{
internal static class TextHelper
{
public static StringPairs ParseConnectionString(string connectionString)
{
var sp = new StringPairs();
if (string.IsNullOrEmpty(connectionString)) return sp;
var split = connectionString.Split(";");
foreach (var item in split)
{
var equal = item.IndexOf("=");
if (equal < 0) continue;
var left = item.Substring(0, equal).ToTrim();
var right = item.Substring(equal + 1).ToTrim();
if (left.IsEmpty() || right.IsEmpty()) continue;
sp.Add(left, right);
}
return sp;
}
}
}

274
Apewer.Source/Source/Access.cs

@ -24,59 +24,34 @@ namespace Apewer.Source
#if NETFRAMEWORK
public partial class Access : IDatabaseBase, IDatabaseQuery, IDatabaseExecute, IDisposable
public partial class Access : IDbClientBase, IDbClientAdo, IDisposable
{
/// <summary>创建 Access 类的新实例。</summary>
public static Access Jet4() => new Access(AccessHelper.JetOleDB4);
/// <summary>创建 Access 类的新实例。</summary>
public static Access Ace12() => new Access(AccessHelper.AceOleDB12);
#region 属性、构造函数和 Dispose。
private OleDbConnection _connection = null;
internal string Provider { get; set; }
#region 连接
/// <summary>构造函数。</summary>
internal Access(string provider)
{
Provider = provider;
Timeout = new Timeout();
}
/// <summary>释放资源。</summary>
public void Dispose() => Close();
#endregion
#region 日志。
string _connstr = null;
OleDbConnection _connection = null;
Timeout _timeout = null;
/// <summary>获取或设置日志记录。</summary>
public Logger Logger { get; set; }
private void LogError(string action, Exception ex, string addtion)
/// <summary>获取或设置超时。</summary>
public Timeout Timeout { get => _timeout; }
/// <summary>构造函数。</summary>
public Access(string connectrionString, Timeout timeout)
{
var logger = Logger;
if (logger != null) logger.Error(this, "Access", action, ex.GetType().FullName, ex.Message, addtion);
_connstr = connectrionString;
_timeout = timeout ?? Timeout.Default;
}
#endregion
#region 连接
#region 连接
/// <summary>获取或设置数据库文件的路径。</summary>
public string Path { get; set; }
/// <summary>Microsoft Access System Database。</summary>
public string Josd { get; set; }
/// <summary>获取或设置用于连接数据库的密码。</summary>
public string Pass { get; set; }
/// <summary>获取或设置超时。</summary>
public Timeout Timeout { get; set; }
/// <summary>获取当前的 OldDbConnection 对象。</summary>
public IDbConnection Connection { get => _connection; }
/// <summary>数据库是否已经连接。</summary>
public bool Online
@ -95,11 +70,10 @@ namespace Apewer.Source
/// <returns>是否已连接。</returns>
public bool Connect()
{
var cs = GenerateConnectionString();
if (_connection == null)
{
_connection = new OleDbConnection();
_connection.ConnectionString = cs;
_connection.ConnectionString = _connstr;
}
else
{
@ -110,9 +84,9 @@ namespace Apewer.Source
_connection.Open();
if (_connection.State == ConnectionState.Open) return true;
}
catch (Exception argException)
catch (Exception ex)
{
LogError("Connect", argException, cs);
Logger.Error(nameof(Access), "Connect", ex, _connstr);
Close();
}
return false;
@ -123,35 +97,91 @@ namespace Apewer.Source
{
if (_connection != null)
{
if (_transaction != null)
{
if (_autocommit) Commit();
else Rollback();
}
_connection.Close();
_connection.Dispose();
_connection = null;
}
}
/// <summary>获取或设置连接字符串。</summary>
private string GenerateConnectionString()
{
if (!File.Exists(Path)) return null;
/// <summary>释放资源。</summary>
public void Dispose() => Close();
var sb = new StringBuilder();
#endregion
sb.Append("provider=", Provider, "; ");
#region Transaction
if (!string.IsNullOrEmpty(Path)) sb.Append("data source=", Path, "; ");
private IDbTransaction _transaction = null;
private bool _autocommit = false;
if (string.IsNullOrEmpty(Pass)) sb.Append("persist security info=false; ");
else sb.Append("jet oledb:database password=\"", Pass, "\"; ");
/// <summary>启动事务。</summary>
public string Begin(bool commit = true) => Begin(commit, null);
// Microsoft Access Workgroup Information
if (!string.IsNullOrEmpty(Josd)) sb.Append("jet oledb:system database=", Josd, "; ");
/// <summary>启动事务。</summary>
public string Begin(bool commit, Class<IsolationLevel> isolation)
{
if (!Connect()) return "未连接。";
if (_transaction != null) return "存在已启动的事务,无法再次启动。";
try
{
_transaction = isolation ? _connection.BeginTransaction(isolation.Value) : _connection.BeginTransaction();
_autocommit = commit;
return null;
}
catch (Exception ex)
{
Logger.Error(nameof(Access), "Begin", ex.Message());
return ex.Message();
}
}
return sb.ToString();
/// <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(nameof(Access), "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(nameof(Access), "Rollback", ex.Message);
return ex.Message();
}
}
#endregion
#region 查询和执行。
#region 查询和执行
/// <summary>使用 SQL 语句进行查询。</summary>
public IQuery Query(string sql) => Query(sql, null);
@ -161,43 +191,40 @@ namespace Apewer.Source
{
if (sql.IsBlank()) return Example.InvalidQueryStatement;
const string table = "queryresult";
var connected = Connect();
if (!connected) return Example.InvalidQueryConnection;
var query = new Query();
try
{
var command = new OleDbCommand();
command.Connection = _connection;
command.CommandTimeout = Timeout.Query;
command.CommandText = sql;
if (parameters != null)
using (var command = new OleDbCommand())
{
foreach (var p in parameters)
command.Connection = _connection;
command.CommandTimeout = Timeout.Query;
command.CommandText = sql;
if (parameters != null)
{
if (p != null) command.Parameters.Add(p);
foreach (var p in parameters)
{
if (p != null) command.Parameters.Add(p);
}
}
}
using (var ds = new DataSet())
{
using (var da = new OleDbDataAdapter(sql, _connection))
using (var ds = new DataSet())
{
da.Fill(ds, table);
query.Table = ds.Tables[table];
using (var da = new OleDbDataAdapter(sql, _connection))
{
const string name = "result";
da.Fill(ds, name);
var table = ds.Tables[name];
return new Query(table);
}
}
}
command.Dispose();
query.Success = true;
}
catch (Exception exception)
{
LogError("Query", exception, sql);
query.Success = false;
query.Exception = exception;
Logger.Error(nameof(Access), "Query", exception, sql);
return new Query(exception);
}
return query;
}
/// <summary>执行 SQL 语句。</summary>
@ -211,40 +238,35 @@ namespace Apewer.Source
var connected = Connect();
if (!connected) return Example.InvalidExecuteConnection;
var execute = new Execute();
using (var transaction = _connection.BeginTransaction())
var inTransaction = _transaction != null;
if (!inTransaction) Begin();
try
{
try
using (var command = new OleDbCommand())
{
using (var command = new OleDbCommand())
command.Connection = _connection;
command.Transaction = (OleDbTransaction)_transaction;
command.CommandTimeout = Timeout.Execute;
command.CommandText = sql;
if (parameters != null)
{
command.Connection = _connection;
command.Transaction = transaction;
command.CommandTimeout = Timeout.Execute;
command.CommandText = sql;
if (parameters != null)
foreach (var parameter in parameters)
{
foreach (var parameter in parameters)
{
if (parameter == null) continue;
command.Parameters.Add(parameter);
}
if (parameter == null) continue;
command.Parameters.Add(parameter);
}
execute.Rows += command.ExecuteNonQuery();
transaction.Commit();
}
execute.Success = true;
}
catch (Exception exception)
{
LogError("Execute", exception, sql);
try { transaction.Rollback(); } catch { }
execute.Success = false;
execute.Exception = exception;
var rows = command.ExecuteNonQuery();
if (!inTransaction) Commit(); // todo 此处应该检查事务提交产生的错误。
return new Execute(true, rows);
}
}
return execute;
catch (Exception exception)
{
Logger.Error(nameof(Access), "Execute", exception, sql);
if (!inTransaction) Rollback();
return new Execute(exception);
}
}
#endregion
@ -280,20 +302,41 @@ namespace Apewer.Source
#endregion
#region protected
/// <summary>获取或设置连接字符串。</summary>
internal protected static string GenerateCS(string provider, string path, string pass, string jo)
{
if (!File.Exists(path)) return null;
var sb = new StringBuilder();
sb.Append("provider=", provider, "; ");
if (!string.IsNullOrEmpty(path)) sb.Append("data source=", path, "; ");
if (string.IsNullOrEmpty(pass)) sb.Append("persist security info=false; ");
else sb.Append("jet oledb:database password=\"", pass, "\"; ");
// Microsoft Access Workgroup Information
if (!string.IsNullOrEmpty(jo)) sb.Append("jet oledb:system database=", jo, "; ");
return sb.ToString();
}
#endregion
}
/// <summary>使用 Microsoft.Jet.OLEDB.4.0 访问 Access 97 - 2003 数据库文件。</summary>
public class AccessJet4 : Access
{
/// <summary>创建 Access 类的新实例。</summary>
public AccessJet4() : base(AccessHelper.JetOleDB4) { }
const string JetOleDB4 = "microsoft.jet.oledb.4.0";
/// <summary>创建 Access 类的新实例。</summary>
public AccessJet4(string path) : base(AccessHelper.JetOleDB4)
{
Path = path;
}
public AccessJet4(string path, string pass = null, string jo = null, Timeout timeout = null)
: base(GenerateCS(JetOleDB4, path, pass, jo), timeout) { }
}
@ -301,14 +344,11 @@ namespace Apewer.Source
public class AccessAce12 : Access
{
/// <summary>创建 Access 类的新实例。</summary>
public AccessAce12() : base(AccessHelper.AceOleDB12) { }
const string AceOleDB12 = "microsoft.ace.oledb.12.0";
/// <summary>创建 Access 类的新实例。</summary>
public AccessAce12(string path) : base(AccessHelper.AceOleDB12)
{
Path = path;
}
public AccessAce12(string path, string pass = null, string jo = null, Timeout timeout = null)
: base(GenerateCS(AceOleDB12, path, pass, jo), timeout) { }
}

402
Apewer.Source/Source/MySql.cs

@ -1,75 +1,61 @@
#if MYSQL_6_9 || MYSQL_6_10
/* 2021.09.23 */
/* 2021.10.14 */
using Externals.MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Net;
using System.Text;
using System.Transactions;
namespace Apewer.Source
{
/// <summary></summary>
public class MySql : IDatabase
public class MySql : IDbClient
{
#region fields & properties
#region 基础
private const string EmptyString = TextUtility.Empty;
private Timeout _timeout = null;
private string _connectionstring = null;
private MySqlConnection _connection = null;
private Timeout _timeout = new Timeout();
private string _address = EmptyString;
private string _store = EmptyString;
private string _user = "root";
private string _pass = EmptyString;
/// <summary></summary>
public string Address { get { return _address; } set { _address = TextUtility.AntiInject(value); } }
/// <summary></summary>
public string Store { get { return _store; } set { _store = TextUtility.AntiInject(value); } }
/// <summary></summary>
public string User { get { return _user; } set { _user = TextUtility.AntiInject(value); } }
/// <summary></summary>
public string Pass { get { return _pass; } set { _pass = TextUtility.AntiInject(value); } }
/// <summary>获取或设置日志记录。</summary>
public Logger Logger { get; set; }
/// <summary></summary>
public Timeout Timeout { get { return _timeout; } set { _timeout = value; } }
/// <summary>超时设定。</summary>
public Timeout Timeout { get => _timeout; }
/// <summary></summary>
public bool Online
/// <summary>创建实例。</summary>
public MySql(string connnectionString, Timeout timeout = default)
{
get
{
if (_connection == null) return false;
return _connection.State == ConnectionState.Open;
}
_connectionstring = connnectionString;
_timeout = timeout ?? Timeout.Default;
}
/// <summary></summary>
public MySql() { }
/// <summary>获取当前的 MySqlConnection 对象。</summary>
public IDbConnection Connection { get => _connection; }
/// <summary></summary>
public MySql(string address, string store, string user, string pass = null)
/// <summary>构建连接字符串以创建实例。</summary>
public MySql(string address, string store, string user, string pass, Timeout timeout = null)
{
Address = address;
Store = store;
User = user;
Pass = pass;
_timeout = timeout ?? Timeout.Default;
var a = TextUtility.AntiInject(address);
var s = TextUtility.AntiInject(store);
var u = TextUtility.AntiInject(user);
var p = TextUtility.AntiInject(pass);
var cs = $"server={a}; database={s}; uid={u}; pwd={p}; ";
_connectionstring = cs;
_storename = new Class<string>(s);
}
#endregion
#region 日志。
/// <summary>获取或设置日志记录。</summary>
public Logger Logger { get; set; }
private void LogError(string action, Exception ex, string addtion)
{
var logger = Logger;
@ -78,12 +64,15 @@ namespace Apewer.Source
#endregion
#region methods
#region Connection
private string CombineString()
{
return TextUtility.Merge("server=", _address, "; database=", _store, "; uid=", _user, "; pwd=", _pass, ";");
}
private MySqlConnection _connection = null;
/// <summary></summary>
public bool Online { get => _connection == null ? false : (_connection.State == ConnectionState.Open); }
/// <summary>连接字符串。</summary>
public string ConnectionString { get => _connectionstring; }
/// <summary></summary>
public bool Connect()
@ -91,7 +80,7 @@ namespace Apewer.Source
if (_connection == null)
{
_connection = new MySqlConnection();
_connection.ConnectionString = CombineString();
_connection.ConnectionString = _connectionstring;
}
else
{
@ -120,6 +109,11 @@ namespace Apewer.Source
{
if (_connection != null)
{
if (_transaction != null)
{
if (_autocommit) Commit();
else Rollback();
}
_connection.Close();
_connection.Dispose();
_connection = null;
@ -129,48 +123,117 @@ namespace Apewer.Source
/// <summary></summary>
public void Dispose() { Close(); }
#endregion
#region Transaction
private IDbTransaction _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()) return "未连接。";
if (_transaction != null) return "存在已启动的事务,无法再次启动。";
try
{
_transaction = isolation ? _connection.BeginTransaction(isolation.Value) : _connection.BeginTransaction();
_autocommit = commit;
return null;
}
catch (Exception ex)
{
Logger.Error(nameof(MySql), "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(nameof(MySql), "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(nameof(MySql), "Rollback", ex.Message());
return ex.Message();
}
}
#endregion
#region SQL
/// <summary></summary>
public IQuery Query(string sql, IEnumerable<IDataParameter> parameters)
{
if (sql.IsBlank()) return Example.InvalidQueryStatement;
const string table = "queryresult";
var connected = Connect();
if (!connected) return Example.InvalidQueryConnection;
var query = new Query();
try
{
var command = new MySqlCommand();
command.Connection = _connection;
command.CommandTimeout = _timeout.Query;
command.CommandText = sql;
if (parameters != null)
using (var command = new MySqlCommand())
{
foreach (var p in parameters)
command.Connection = _connection;
command.CommandTimeout = _timeout.Query;
command.CommandText = sql;
if (parameters != null)
{
if (p != null) command.Parameters.Add(p);
foreach (var p in parameters)
{
if (p != null) command.Parameters.Add(p);
}
}
}
using (var ds = new DataSet())
{
using (var da = new MySqlDataAdapter(sql, _connection))
using (var ds = new DataSet())
{
da.Fill(ds, table);
query.Table = ds.Tables[table];
using (var da = new MySqlDataAdapter(sql, _connection))
{
const string name = "result";
da.Fill(ds, name);
var table = ds.Tables[name];
return new Query(table);
}
}
}
command.Dispose();
query.Success = true;
}
catch (Exception exception)
{
LogError("Query", exception, sql);
query.Success = false;
query.Exception = exception;
Logger.Error(nameof(MySql), "Query", exception, sql);
return new Query(exception);
}
return query;
}
/// <summary></summary>
@ -181,37 +244,35 @@ namespace Apewer.Source
var connected = Connect();
if (!connected) return Example.InvalidExecuteConnection;
var transaction = _connection.BeginTransaction();
var execute = new Execute();
var inTransaction = _transaction != null;
if (!inTransaction) Begin();
try
{
var command = new MySqlCommand();
command.Connection = _connection;
command.Transaction = transaction;
command.CommandTimeout = _timeout.Execute;
command.CommandText = sql;
if (parameters != null)
using (var command = new MySqlCommand())
{
foreach (var parameter in parameters)
command.Connection = _connection;
command.Transaction = (MySqlTransaction)_transaction;
command.CommandTimeout = _timeout.Execute;
command.CommandText = sql;
if (parameters != null)
{
if (parameter == null) continue;
command.Parameters.Add(parameter);
foreach (var parameter in parameters)
{
if (parameter == null) continue;
command.Parameters.Add(parameter);
}
}
var rows = command.ExecuteNonQuery();
if (!inTransaction) Commit(); // todo 此处应该检查事务提交产生的错误。
return new Execute(true, rows);
}
execute.Rows += command.ExecuteNonQuery();
transaction.Commit();
command.Dispose();
execute.Success = true;
}
catch (Exception exception)
{
LogError("Execute", exception, sql);
try { transaction.Rollback(); } catch { }
execute.Success = false;
execute.Exception = exception;
Logger.Error(nameof(MySql), "Execute", exception, sql);
if (!inTransaction) Rollback();
return new Execute(exception);
}
try { transaction.Dispose(); } catch { }
return execute;
}
/// <summary></summary>
@ -241,29 +302,38 @@ namespace Apewer.Source
#region ORM
private List<string> FirstColumn(string sql)
private Class<string> _storename = null;
private string StoreName()
{
if (_storename) return _storename.Value;
_storename = new Class<string>(Internals.TextHelper.ParseConnectionString(_connectionstring).GetValue("database"));
return _storename.Value ?? "";
}
private string[] FirstColumn(string sql)
{
using (var query = Query(sql) as Query) return query.ReadColumn();
}
/// <summary></summary>
public List<string> TableNames()
public string[] TableNames()
{
var sql = TextUtility.Merge("select table_name from information_schema.tables where table_schema='", _store, "' and table_type='base table';");
var sql = TextUtility.Merge("select table_name from information_schema.tables where table_schema='", StoreName(), "' and table_type='base table';");
return FirstColumn(sql);
}
/// <summary></summary>
public List<string> ViewNames()
public string[] ViewNames()
{
var sql = TextUtility.Merge("select table_name from information_schema.tables where table_schema='", _store, "' and table_type='view';");
var sql = TextUtility.Merge("select table_name from information_schema.tables where table_schema='", StoreName(), "' and table_type='view';");
return FirstColumn(sql);
}
/// <summary></summary>
public List<string> ColumnNames(string table)
public string[] ColumnNames(string table)
{
var sql = TextUtility.Merge("select column_name from information_schema.columns where table_schema='", _store, "' and table_name='", TextUtility.AntiInject(table), "';");
var sql = TextUtility.Merge("select column_name from information_schema.columns where table_schema='", StoreName(), "' and table_name='", TextUtility.AntiInject(table), "';");
return FirstColumn(sql);
}
@ -273,9 +343,9 @@ namespace Apewer.Source
// 检查现存表。
var exists = false;
var tables = TableNames();
if (tables.Count > 0)
if (tables.Length > 0)
{
var lower = structure.Table.ToLower();
var lower = structure.Name.ToLower();
foreach (var table in tables)
{
if (TextUtility.IsBlank(table)) continue;
@ -289,10 +359,10 @@ namespace Apewer.Source
if (exists)
{
var columns = ColumnNames(structure.Table);
if (columns.Count > 0)
var columns = ColumnNames(structure.Name);
if (columns.Length > 0)
{
var lower = new List<string>(columns.Count);
var lower = new List<string>(columns.Length);
var added = 0;
foreach (var column in columns)
{
@ -301,10 +371,10 @@ namespace Apewer.Source
added++;
}
lower.Capacity = added;
columns = lower;
columns = lower.ToArray();
}
var sqlsb = new StringBuilder();
foreach (var column in structure.Columns.Values)
foreach (var column in structure.Columns)
{
// 检查 Independent 特性。
if (structure.Independent && column.Independent) continue;
@ -317,7 +387,7 @@ namespace Apewer.Source
if (type.IsEmpty()) return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。");
// alter table `_record` add column `_index` bigint;
sqlsb.Append("alter table `", structure.Table, "` add column ", type, "; ");
sqlsb.Append("alter table `", structure.Name, "` add column ", type, "; ");
}
var sql = sqlsb.ToString();
return sql;
@ -326,14 +396,11 @@ namespace Apewer.Source
{
// create table _record (`_index` bigint, `_key` varchar(255), `_text` longtext) engine=innodb default charset=utf8mb4
var columns = new List<string>(structure.Columns.Count);
var columns = new List<string>(structure.Columns.Length);
var columnsAdded = 0;
var primarykey = null as string;
foreach (var kvp in structure.Columns)
foreach (var column in structure.Columns)
{
var property = kvp.Key;
var column = kvp.Value;
// 检查 Independent 特性。
if (structure.Independent && column.Independent) continue;
@ -344,10 +411,10 @@ namespace Apewer.Source
columnsAdded++;
// 主键。
if (property == "Key") primarykey = column.Field;
if (column.Property.Name == "Key") primarykey = column.Field;
}
columns.Capacity = columnsAdded;
var table = structure.Table;
var table = structure.Name;
var joined = string.Join(", ", columns);
// 设置主键。
@ -370,22 +437,21 @@ namespace Apewer.Source
{
if (model == null)
{
sql = "";
sql = null;
return "指定的类型无效。";
}
var structure = null as TableStructure;
try { structure = TableStructure.ParseModel(model); }
catch (Exception exception)
var structure = TableStructure.Parse(model);
if (structure == null)
{
sql = "";
return exception.Message;
sql = null;
return "无法解析记录模型。";
}
// 连接数据库。
if (!Connect())
{
sql = "";
sql = null;
return "连接数据库失败。";
}
@ -393,7 +459,7 @@ namespace Apewer.Source
if (sql.NotEmpty())
{
var execute = Execute(sql);
if (!execute.Success) return execute.Error;
if (!execute.Success) return execute.Message;
}
return null;
}
@ -407,62 +473,56 @@ namespace Apewer.Source
/// <summary></summary>
public string Initialize(Record model) => (model == null) ? "参数无效。" : Initialize(model.GetType());
/// <summary>插入记录。成功时候返回空字符串,发生异常时返回异常信息。</summary>
/// <summary>插入记录。返回错误信息。</summary>
public string Insert(IRecord record)
{
if (record == null) return "参数无效。";
record.FixProperties();
var structure = null as TableStructure;
try { structure = TableStructure.ParseModel(record); }
catch (Exception exception) { return exception.Message; }
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
var parameters = structure.CreateDataParameters(record, CreateDataParameter);
var sql = GenerateInsertStatement(structure.Table, parameters);
var parameters = structure.CreateParameters(record, CreateDataParameter);
var sql = GenerateInsertStatement(structure.Name, parameters);
var execute = Execute(sql, parameters);
if (execute.Success) return TextUtility.Empty;
return execute.Error;
return execute.Message;
}
/// <summary>
/// <para>更新记录,实体中的 Created 和 Key 属性不被更新。成功时返回空字符串,发生异常时返回异常信息。</para>
/// <para>无法更新拥有 Independent 特性的模型。</para>
/// </summary>
/// <summary>更新记录,实体中的 Key 属性不被更新。返回错误信息。</summary>
/// <remarks>无法更新带有 Independent 特性的模型(缺少 Key 属性)。</remarks>
public string Update(IRecord record)
{
if (record == null) return "参数无效。";
record.FixProperties();
record.SetUpdated();
var structure = null as TableStructure;
try { structure = TableStructure.ParseModel(record); }
catch (Exception exception) { return exception.Message; }
// 检查 Independent 特性。
if (structure.Independent) return "无法更新拥有 Independent 特性的模型。";
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
if (structure.Independent) return "无法更新带有 Independent 特性的模型。";
var parameters = structure.CreateDataParameters(record, CreateDataParameter, "_created", "_key");
var parameters = structure.CreateParameters(record, CreateDataParameter, "_key");
var sql = GenerateUpdateStatement(structure, record.Key, parameters);
var execute = Execute(sql, parameters);
if (execute.Success) return TextUtility.Empty;
return execute.Error;
return execute.Message;
}
/// <summary></summary>
public Result<List<IRecord>> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
public Result<IRecord[]> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
/// <summary></summary>
public Result<List<T>> Query<T>(string sql) where T : class, IRecord, new() => OrmHelper.Query<T>(this, sql);
public Result<T[]> Query<T>(string sql) where T : class, IRecord, new() => OrmHelper.Query<T>(this, sql);
/// <summary>获取所有记录。Flag 为 0 时将忽略 Flag 条件。</summary>
public Result<List<IRecord>> Query(Type model, long flag = 0) => OrmHelper.Query(this, model, (tn) =>
public Result<IRecord[]> 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>获取所有记录。Flag 为 0 时将忽略 Flag 条件。</summary>
public Result<List<T>> Query<T>(long flag = 0) where T : class, IRecord, new() => OrmHelper.Query<T>(this, (tn) =>
public Result<T[]> Query<T>(long flag = 0) where T : class, IRecord, new() => OrmHelper.Query<T>(this, (tn) =>
{
if (flag == 0) return $"select * from `{tn}`; ";
return $"select * from `{tn}` where `_flag`={flag}; ";
@ -472,22 +532,20 @@ namespace Apewer.Source
/// <param name="model">填充的记录模型。</param>
/// <param name="skip">要跳过的记录数,可用最小值为 0。</param>
/// <param name="count">要获取的记录数,可用最小值为 1。</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public Result<List<IRecord>> Query(Type model, int skip, int count)
public Result<IRecord[]> Query(Type model, int skip, int count)
{
if (skip < 0) return new Result<List<IRecord>>(new ArgumentOutOfRangeException(nameof(skip)));
if (count < 1) return new Result<List<IRecord>>(new ArgumentOutOfRangeException(nameof(count)));
if (skip < 0) return new Result<IRecord[]>("参数 skip 超出了范围。");
if (count < 1) return new Result<IRecord[]>("参数 count 超出了范围。");
return OrmHelper.Query(this, model, (tn) => $"select * from `{tn}` limit {skip}, {count}; ");
}
/// <summary>获取记录。</summary>
/// <param name="skip">要跳过的记录数,可用最小值为 0。</param>
/// <param name="count">要获取的记录数,可用最小值为 1。</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public Result<List<T>> Query<T>(int skip, int count) where T : class, IRecord, new()
public Result<T[]> Query<T>(int skip, int count) where T : class, IRecord, new()
{
if (skip < 0) return new Result<List<T>>(new ArgumentOutOfRangeException(nameof(skip)));
if (count < 1) return new Result<List<T>>(new ArgumentOutOfRangeException(nameof(count)));
if (skip < 0) return new Result<T[]>("参数 skip 超出了范围。");
if (count < 1) return new Result<T[]>("参数 count 超出了范围。");
return OrmHelper.Query<T>(this, (tn) => $"select * from `{tn}` limit {skip}, {count}; ");
}
@ -506,14 +564,14 @@ namespace Apewer.Source
});
/// <summary>>获取指定类型的主键,按 Flag 属性筛选。</summary>
public Result<List<string>> Keys(Type model, long flag = 0) => OrmHelper.Keys(this, model, (tn) =>
public 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>>获取指定类型的主键,按 Flag 属性筛选。</summary>
public Result<List<string>> Keys<T>(long flag = 0) where T : class, IRecord, new() => Keys(typeof(T), flag);
public Result<string[]> Keys<T>(long flag = 0) where T : class, IRecord, new() => Keys(typeof(T), flag);
/// <summary>对表添加列,返回错误信息。</summary>
/// <typeparam name="T">记录类型。</typeparam>
@ -521,12 +579,14 @@ namespace Apewer.Source
/// <param name="type">字段类型。</param>
/// <param name="length">字段长度,仅对 VarChar 和 NVarChar 类型有效。</param>
/// <returns></returns>
public string AddColumn<T>(string column, ColumnType type, int length = 0) where T : Record
public string AddColumn<T>(string column, ColumnType type, int length = 0) where T : class, IRecord
{
var columnName = SafeColumn(column);
if (columnName.IsEmpty()) return "列名无效。";
var tableName = TableStructure.ParseTable(typeof(T)).Name;
var ta = TableAttribute.Parse(typeof(T));
if (ta == null) return "无法解析记录模型。";
var tableName = ta.Name;
var columeType = "";
switch (type)
@ -547,9 +607,9 @@ namespace Apewer.Source
case ColumnType.NVarChar:
columeType = $"varchar({length})";
break;
case ColumnType.VarChar255:
case ColumnType.NVarChar255:
columeType = "varchar(255)";
case ColumnType.VarChar191:
case ColumnType.NVarChar191:
columeType = "varchar(191)";
break;
case ColumnType.VarCharMax:
case ColumnType.NVarCharMax:
@ -563,7 +623,7 @@ namespace Apewer.Source
var sql = $"alter table `{tableName}` add {columnName} {columeType}; ";
var execute = Execute(sql) as Execute;
var error = execute.Error;
var error = execute.Message;
return error;
}
@ -650,10 +710,10 @@ namespace Apewer.Source
dbtype = MySqlDbType.DateTime;
break;
case ColumnType.VarChar:
case ColumnType.VarChar255:
case ColumnType.VarChar191:
case ColumnType.VarCharMax:
case ColumnType.NVarChar:
case ColumnType.NVarChar255:
case ColumnType.NVarChar191:
case ColumnType.NVarCharMax:
dbtype = MySqlDbType.VarChar;
break;
@ -671,9 +731,9 @@ namespace Apewer.Source
case ColumnType.NVarChar:
size = NumberUtility.Restrict(size, 0, 65535);
break;
case ColumnType.VarChar255:
case ColumnType.NVarChar255:
size = NumberUtility.Restrict(size, 0, 255);
case ColumnType.VarChar191:
case ColumnType.NVarChar191:
size = NumberUtility.Restrict(size, 0, 191);
break;
default:
size = 0;
@ -725,8 +785,8 @@ namespace Apewer.Source
case ColumnType.VarChar:
type = TextUtility.Merge("varchar(", Math.Max(65535, length).ToString(), ")");
break;
case ColumnType.VarChar255:
type = TextUtility.Merge("varchar(255)");
case ColumnType.VarChar191:
type = TextUtility.Merge("varchar(191)");
break;
case ColumnType.VarCharMax:
type = TextUtility.Merge("varchar(max)");
@ -737,8 +797,8 @@ namespace Apewer.Source
case ColumnType.NVarChar:
type = TextUtility.Merge("varchar(", Math.Min(65535, length).ToString(), ")");
break;
case ColumnType.NVarChar255:
type = TextUtility.Merge("varchar(255)");
case ColumnType.NVarChar191:
type = TextUtility.Merge("varchar(191)");
break;
case ColumnType.NVarCharMax:
type = TextUtility.Merge("varchar(65535)");
@ -844,7 +904,7 @@ namespace Apewer.Source
{
var result = TextUtility.Empty;
var table = TextUtility.AntiInject(structure.Table, 255);
var table = TextUtility.AntiInject(structure.Name, 255);
if (TextUtility.IsEmpty(table)) return result;
var safekey = TextUtility.AntiInject(key, 255);
@ -885,7 +945,7 @@ namespace Apewer.Source
if (structure == null) throw new ArgumentNullException("structure");
if (key == null) throw new ArgumentNullException("key");
var table = TextUtility.AntiInject(structure.Table, 255);
var table = TextUtility.AntiInject(structure.Name, 255);
if (TextUtility.IsBlank(table)) throw new ArgumentException("表名无效。", "structure");
var safekey = TextUtility.AntiInject(key, 255);

643
Apewer.Source/Source/SqlServer.cs → Apewer.Source/Source/SqlClient.cs

@ -1,6 +1,4 @@
#if NETFRAMEWORK
/* 2021.09.23 */
/* 2021.10.14 */
using Apewer;
using Apewer.Source;
@ -8,88 +6,74 @@ using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Sql;
using System.Data.SqlClient;
using System.Net;
using System.Text;
#if NETFRAMEWORK
using System.Data.Sql;
#else
#endif
namespace Apewer.Source
{
/// <summary>用于快速连接 Microsoft SQL Server 数据库的辅助。</summary>
/// <summary></summary>
[Serializable]
public class SqlClinet : IDatabase
public class SqlClient : IDbClient
{
#region 变量定义。
private SqlConnection _db = null;
#region 变量、构造函数
private Timeout _timeout;
private Timeout _timeout = null;
private string _connectionstring = "";
private string _address = "";
private string _store = "";
private string _user = "";
private string _pass = "";
#endregion
/// <summary>获取或设置日志记录。</summary>
public Logger Logger { get; set; }
#region 构造函数。
/// <summary>创建空参数的数据库连接实例。</summary>
public SqlClinet()
{
_timeout = Timeout.Default;
}
/// <summary>超时设定。</summary>
public Timeout Timeout { get => _timeout; }
/// <summary>使用连接字符串创建数据库连接实例。</summary>
public SqlClinet(string connectionString)
public SqlClient(string connectionString, Timeout timeout = null)
{
_timeout = Timeout.Default;
_timeout = timeout ?? Timeout.Default;
_connectionstring = connectionString ?? "";
}
/// <summary>使用连接凭据创建数据库连接实例。</summary>
/// <param name="address">服务器地址。</param>
/// <param name="store">数据库名称。</param>
public SqlClinet(string address, string store)
{
_timeout = Timeout.Default;
_address = address ?? "";
_store = store ?? "";
UpdateConnectString();
}
public SqlClient(string address, string store, string user, string pass, Timeout timeout = null)
{
if (timeout == null) timeout = Timeout.Default;
/// <summary>使用连接凭据创建数据库连接实例。</summary>
/// <param name="address">服务器地址。</param>
/// <param name="store">数据库名称。</param>
/// <param name="user">用户名。</param>
/// <param name="pass">密码。</param>
public SqlClinet(string address, string store, string user, string pass)
{
_timeout = Timeout.Default;
_address = address ?? "";
_store = store ?? "";
_user = user ?? "";
_pass = pass ?? "";
UpdateConnectString();
var a = TextUtility.AntiInject(address);
var s = TextUtility.AntiInject(store);
var u = TextUtility.AntiInject(user);
var p = TextUtility.AntiInject(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}; ";
_timeout = timeout ?? Timeout.Default;
_connectionstring = cs;
}
#endregion
#region 日志。
/// <summary>获取或设置日志记录。</summary>
public Logger Logger { get; set; }
#region Ado - Connection
private void LogError(string action, Exception ex, string addtion)
{
var logger = Logger;
if (logger != null) logger.Error(this, "SQL Server", action, ex.GetType().FullName, ex.Message, addtion);
}
private SqlConnection _db = null;
#endregion
/// <summary>连接字符串。</summary>
public string ConnectionString { get => _connectionstring; }
#region 实现接口。
/// <summary>获取当前的 SqlConnection 对象。</summary>
public IDbConnection Connection { get => _db; }
/// <summary>数据库是否已经连接。</summary>
public bool Online
@ -107,7 +91,7 @@ namespace Apewer.Source
if (_db == null)
{
_db = new SqlConnection();
_db.ConnectionString = ConnectionString;
_db.ConnectionString = _connectionstring;
}
else
{
@ -124,7 +108,7 @@ namespace Apewer.Source
}
catch (Exception ex)
{
LogError("Connection", ex, _db.ConnectionString);
Logger.Error(nameof(SqlClient), "Connection", ex, _db.ConnectionString);
Close();
return false;
}
@ -135,6 +119,11 @@ namespace Apewer.Source
{
if (_db != null)
{
if (_transaction != null)
{
if (_autocommit) Commit();
else Rollback();
}
_db.Close();
_db.Dispose();
_db = null;
@ -142,16 +131,80 @@ namespace Apewer.Source
}
/// <summary>关闭连接,释放对象所占用的系统资源,并清除连接信息。</summary>
public void Dispose()
{
Close();
_connectionstring = "";
_address = "";
_store = "";
_user = "";
_pass = "";
public void Dispose() => Close();
#endregion
#region Ado - Transaction
private IDbTransaction _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<IsolationLevel> isolation)
{
if (!Connect()) return "未连接。";
if (_transaction != null) return "存在已启动的事务,无法再次启动。";
try
{
_transaction = isolation ? _db.BeginTransaction(isolation.Value) : _db.BeginTransaction();
_autocommit = commit;
return null;
}
catch (Exception ex)
{
Logger.Error(nameof(SqlClient), "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(nameof(SqlClient), "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(nameof(SqlClient), "Rollback", ex.Message);
return ex.Message();
}
}
#endregion
#region Ado - SQL
/// <summary>查询。</summary>
public IQuery Query(string sql) => Query(sql, null);
@ -159,44 +212,40 @@ namespace Apewer.Source
public IQuery Query(string sql, IEnumerable<IDataParameter> parameters)
{
if (TextUtility.IsBlank(sql)) return Example.InvalidQueryStatement;
const string tablename = "queryresult";
var connected = Connect();
if (!connected) return Example.InvalidQueryConnection;
var query = new Query();
try
{
var command = new SqlCommand();
command.Connection = _db;
command.CommandTimeout = Timeout.Query;
command.CommandText = sql;
if (parameters != null)
using (var command = new SqlCommand())
{
foreach (var parameter in parameters)
command.Connection = _db;
command.CommandTimeout = _timeout.Query;
command.CommandText = sql;
if (parameters != null)
{
if (parameter != null) command.Parameters.Add(parameter);
foreach (var parameter in parameters)
{
if (parameter != null) command.Parameters.Add(parameter);
}
}
}
using (var dataset = new DataSet())
{
using (var dataadapter = new SqlDataAdapter(sql, _db))
using (var ds = new DataSet())
{
dataadapter.Fill(dataset, tablename);
query.Table = dataset.Tables[tablename];
using (var da = new SqlDataAdapter(sql, _db))
{
const string name = "resule";
da.Fill(ds, name);
var table = ds.Tables[name];
return new Query(table, true);
}
}
}
command.Dispose();
query.Success = true;
}
catch (Exception exception)
{
LogError("Query", exception, sql);
query.Success = false;
query.Exception = exception;
Logger.Error(nameof(SqlClient), "Query", exception, sql);
return new Query(exception);
}
return query;
}
/// <summary>执行。</summary>
@ -210,119 +259,42 @@ namespace Apewer.Source
var connected = Connect();
if (!connected) return Example.InvalidExecuteConnection;
var transaction = _db.BeginTransaction();
var execute = new Execute();
var inTransaction = _transaction != null;
if (!inTransaction) Begin();
try
{
var command = new SqlCommand();
command.Connection = _db;
command.Transaction = transaction;
command.CommandTimeout = Timeout.Execute;
command.CommandText = sql;
if (parameters != null)
using (var command = new SqlCommand())
{
foreach (var parameter in parameters)
command.Connection = _db;
command.Transaction = (SqlTransaction)_transaction;
command.CommandTimeout = _timeout.Execute;
command.CommandText = sql;
if (parameters != null)
{
if (parameter != null) command.Parameters.Add(parameter);
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);
}
execute.Rows += command.ExecuteNonQuery();
transaction.Commit();
command.Dispose();
execute.Success = true;
}
catch (Exception exception)
{
try { transaction.Rollback(); } catch { }
LogError("Execute", exception, sql);
execute.Success = false;
execute.Exception = exception;
Logger.Error(nameof(SqlClient), "Execute", exception, sql);
if (!inTransaction) Rollback();
return new Execute(exception);
}
try { transaction.Dispose(); } catch { }
return execute;
}
#endregion
#region 属性。
/// <summary>获取当前的 SqlConnection 对象。</summary>
public SqlConnection Connection
{
get { return _db; }
}
/// <summary>获取或设置连接字符串。</summary>
public string ConnectionString
{
get { return _connectionstring; }
set { _connectionstring = value ?? ""; _address = ""; _store = ""; _user = ""; _pass = ""; }
}
/// <summary>获取或设置数据库服务器的地址。</summary>
public string Address
{
get { return _address; }
set { _address = value ?? ""; UpdateConnectString(); }
}
/// <summary>获取或设置数据库名称。</summary>
public string Store
{
get { return _store; }
set { _store = value ?? ""; UpdateConnectString(); }
}
/// <summary>获取或设置用于连接数据库服务器的用户名,为空则使用 Windows 用户登录。</summary>
public string User
{
get { return _user; }
set { _user = value ?? ""; UpdateConnectString(); }
}
/// <summary>获取或设置用于连接数据库服务器的密码。</summary>
public string Pass
{
get { return _pass; }
set { _pass = value ?? ""; UpdateConnectString(); }
}
/// <summary>获取或设置超时。</summary>
public Timeout Timeout
{
get { return _timeout; }
set { _timeout = value; }
}
#endregion
#region 方法。
/// <summary>指定连接凭据后,是否符合连接要求。</summary>
public bool Proven()
{
return Proven(_address, _store, _user, _pass);
}
private void UpdateConnectString()
{
_connectionstring = "";
_connectionstring += "data source = " + _address + "; ";
_connectionstring += "initial catalog = " + _store + "; ";
if (string.IsNullOrEmpty(User))
{
_connectionstring += "integrated security = sspi; ";
}
else
{
_connectionstring += "user id = " + _user + "; ";
if (!string.IsNullOrEmpty(_pass)) _connectionstring += "password = " + _pass + "; ";
}
_connectionstring += "connection timeout = " + Timeout.Connect.ToString() + ";";
}
#region ORM
/// <summary>查询数据库中的所有表名。</summary>
public List<string> TableNames()
public string[] TableNames()
{
var list = new List<string>();
if (Connect())
@ -337,11 +309,11 @@ namespace Apewer.Source
}
query.Dispose();
}
return list;
return list.ToArray();
}
/// <summary>查询数据库实例中的所有数据库名。</summary>
public List<string> StoreNames()
public string[] StoreNames()
{
var list = new List<string>();
if (Connect())
@ -360,11 +332,11 @@ namespace Apewer.Source
}
query.Dispose();
}
return list;
return list.ToArray();
}
/// <summary>查询表中的所有列名。</summary>
public List<string> ColumnNames(string tableName)
public string[] ColumnNames(string tableName)
{
var list = new List<string>();
if (Connect())
@ -380,21 +352,17 @@ namespace Apewer.Source
}
query.Dispose();
}
return list;
return list.ToArray();
}
/// <summary>创建表,当表不存在时创建表,当现存表中缺少模型中属性对应的列时增加列。成功时返回空字符串,发生异常时返回异常信息。</summary>
public string Initialize<T>() where T : class, IRecord, new() => Initialize(typeof(T));
/// <summary>创建表,当表不存在时创建表,当现存表中缺少模型中属性对应的列时增加列。成功时返回空字符串,发生异常时返回异常信息。</summary>
public string Initialize(Record model) => model == null ? "参数无效。" : Initialize(model);
/// <summary>创建表,当表不存在时创建表,当现存表中缺少模型中属性对应的列时增加列。成功时返回空字符串,发生异常时返回异常信息。</summary>
public string Initialize(Type model)
{
var structure = null as TableStructure;
try { structure = TableStructure.ParseModel(model); }
catch (Exception exception) { return exception.Message; }
var structure = TableStructure.Parse(model);
if (structure == null) return "无法解析记录模型。";
// 连接数据库。
if (!Connect()) return "连接数据库失败。";
@ -402,9 +370,9 @@ namespace Apewer.Source
// 检查现存表。
var exists = false;
var tables = TableNames();
if (tables.Count > 0)
if (tables.Length > 0)
{
var lower = structure.Table.ToLower();
var lower = structure.Name.ToLower();
foreach (var table in tables)
{
if (TextUtility.IsBlank(table)) continue;
@ -419,8 +387,8 @@ namespace Apewer.Source
if (exists)
{
// 获取已存在的列名。
var columns = ColumnNames(structure.Table);
if (columns.Count > 0)
var columns = ColumnNames(structure.Name);
if (columns.Length > 0)
{
var lower = new List<string>();
foreach (var column in columns)
@ -428,11 +396,11 @@ namespace Apewer.Source
if (TextUtility.IsBlank(column)) continue;
lower.Add(column.ToLower());
}
columns = lower;
columns = lower.ToArray();
}
// 增加列。
foreach (var column in structure.Columns.Values)
foreach (var column in structure.Columns)
{
// 检查 Independent 特性。
if (structure.Independent && column.Independent) continue;
@ -444,100 +412,83 @@ namespace Apewer.Source
var type = GetColumnDeclaration(column);
if (type == TextUtility.Empty) return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。");
var sql = TextUtility.Merge("alter table [", structure.Table, "] add ", type, "; ");
var sql = TextUtility.Merge("alter table [", structure.Name, "] add ", type, "; ");
var execute = Execute(sql);
if (execute.Success == false) return execute.Error;
if (execute.Success == false) return execute.Message;
}
return TextUtility.Empty;
}
else
{
var sqlcolumns = new List<string>();
foreach (var kvp in structure.Columns)
foreach (var column in structure.Columns)
{
var property = kvp.Key;
var column = kvp.Value;
// 检查 Independent 特性。
if (structure.Independent && column.Independent) continue;
var type = GetColumnDeclaration(column);
if (!column.Independent && property == "Key") type = type + " primary key";
if (!column.Independent && column.Property.Name == "Key") type = type + " primary key";
if (type == TextUtility.Empty) return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。");
sqlcolumns.Add(type);
}
var sql = TextUtility.Merge("create table [", structure.Table, "](", string.Join(", ", sqlcolumns.ToArray()), "); ");
var sql = TextUtility.Merge("create table [", structure.Name, "](", string.Join(", ", sqlcolumns.ToArray()), "); ");
var execute = Execute(sql);
if (execute.Success) return TextUtility.Empty;
return execute.Error;
return execute.Message;
}
}
/// <summary>插入记录。成功时候返回空字符串,发生异常时返回异常信息。</summary>
/// <summary>插入记录。返回错误信息。</summary>
public string Insert(IRecord record)
{
if (record == null) return "参数无效。";
var type = record.GetType();
record.FixProperties();
var structure = null as TableStructure;
try { structure = TableStructure.ParseModel(record); }
catch (Exception exception) { return exception.Message; }
var parameters = structure.CreateDataParameters(record, CreateDataParameter);
var sql = GenerateInsertStatement(structure.Table, parameters);
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
var parameters = structure.CreateParameters(record, CreateDataParameter);
var sql = GenerateInsertStatement(structure.Name, parameters);
var execute = Execute(sql, parameters);
if (execute.Success) return TextUtility.Empty;
return execute.Error;
return execute.Message;
}
/// <summary>
/// <para>更新记录,实体中的 Created 和 Key 属性不被更新。成功时返回空字符串,发生异常时返回异常信息。</para>
/// <para>无法更新拥有 Independent 特性的模型。</para>
/// </summary>
/// <summary>更新记录,实体中的 Key 属性不被更新。返回错误信息。</summary>
/// <remarks>无法更新带有 Independent 特性的模型(缺少 Key 属性)。</remarks>
public string Update(IRecord record)
{
if (record == null) return "参数无效。";
var type = record.GetType();
record.FixProperties();
record.SetUpdated();
var structure = null as TableStructure;
try { structure = TableStructure.ParseModel(record); }
catch (Exception exception) { return exception.Message; }
// 检查 Independent 特性。
if (structure.Independent) return "无法更新拥有 Independent 特性的模型。";
var parameters = structure.CreateDataParameters(record, CreateDataParameter, "_created", "_key");
var sql = GenerateUpdateStatement(structure.Table, record.Key, parameters);
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
if (structure.Independent) return "无法更新带有 Independent 特性的模型。";
var parameters = structure.CreateParameters(record, CreateDataParameter, "_key");
var sql = GenerateUpdateStatement(structure.Name, record.Key, parameters);
var execute = Execute(sql, parameters);
if (execute.Success) return TextUtility.Empty;
return execute.Error;
return execute.Message;
}
/// <summary>获取按指定语句查询到的所有记录。</summary>
public Result<List<IRecord>> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
public Result<IRecord[]> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
/// <summary>获取按指定语句查询到的所有记录。</summary>
public Result<List<T>> Query<T>(string sql) where T : class, IRecord, new() => OrmHelper.Query<T>(this, sql);
public Result<T[]> Query<T>(string sql) where T : class, IRecord, new() => OrmHelper.Query<T>(this, sql);
/// <summary>获取记录。</summary>
public Result<List<IRecord>> Query(Type model, long flag = 0) => OrmHelper.Query(this, model, (tn) =>
public Result<IRecord[]> 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 Result<List<T>> Query<T>(long flag = 0) where T : class, IRecord, new() => OrmHelper.Query<T>(this, (tn) =>
public Result<T[]> Query<T>(long flag = 0) where T : class, IRecord, new() => OrmHelper.Query<T>(this, (tn) =>
{
if (flag == 0) return $"select * from [{tn}]; ";
return $"select * from [{tn}] where _flag={flag}; ";
@ -558,66 +509,55 @@ namespace Apewer.Source
});
/// <summary>查询有效的 Key 值。</summary>
public Result<List<string>> Keys(Type model, long flag = 0) => OrmHelper.Keys(this, model, (tn) =>
public 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 Result<List<string>> Keys<T>(long flag = 0) where T : class, IRecord, new() => Keys(typeof(T), flag);
public Result<string[]> Keys<T>(long flag = 0) where T : class, IRecord, new() => Keys(typeof(T), flag);
#endregion
#region 静态方法。
#region public static
#if NETFRAMEWORK
private static string GetColumnDeclaration(ColumnAttribute column)
/// <summary>枚举本地网络中服务器的名称。</summary>
public static SqlServerSource[] EnumerateServer()
{
var type = TextUtility.Empty;
var vcolumn = column;
var length = Math.Max(0, vcolumn.Length);
switch (vcolumn.Type)
var list = new List<SqlServerSource>();
// 表中列名:ServerName、InstanceName、IsClustered、Version。
using (var query = new Query(SqlDataSourceEnumerator.Instance.GetDataSources()))
{
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.VarChar255:
type = TextUtility.Merge("varchar(255)");
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.NVarChar255:
type = TextUtility.Merge("nvarchar(255)");
break;
case ColumnType.NVarCharMax:
type = TextUtility.Merge("nvarchar(max)");
break;
case ColumnType.NText:
type = TextUtility.Merge("ntext");
break;
default:
return TextUtility.Empty;
for (int i = 0; i < query.Rows; i++)
{
var item = new SqlServerSource();
item.ServerName = query.Text(i, "ServerName");
list.Add(item);
}
}
return TextUtility.Merge("[", vcolumn.Field, "] ", type);
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>
@ -651,12 +591,12 @@ namespace Apewer.Source
vtype = SqlDbType.DateTime;
break;
case ColumnType.VarChar:
case ColumnType.VarChar255:
case ColumnType.VarChar191:
case ColumnType.VarCharMax:
vtype = SqlDbType.VarChar;
break;
case ColumnType.NVarChar:
case ColumnType.NVarChar255:
case ColumnType.NVarChar191:
case ColumnType.NVarCharMax:
vtype = SqlDbType.VarChar;
break;
@ -679,9 +619,9 @@ namespace Apewer.Source
case ColumnType.NVarChar:
vsize = NumberUtility.Restrict(vsize, 0, 4000);
break;
case ColumnType.VarChar255:
case ColumnType.NVarChar255:
vsize = NumberUtility.Restrict(vsize, 0, 255);
case ColumnType.VarChar191:
case ColumnType.NVarChar191:
vsize = NumberUtility.Restrict(vsize, 0, 191);
break;
default:
vsize = 0;
@ -728,51 +668,60 @@ namespace Apewer.Source
return p;
}
///// <summary>枚举本地网络中服务器的名称。</summary>
//public static List<string> EnumerateServer()
//{
// // 表中列名:ServerName、InstanceName、IsClustered、Version。
// var table = SqlDataSourceEnumerator.Instance.GetDataSources();
// var query = new Query();
// query.Success = table != null;
// query.Table = table;
// var list = new List<string>();
// for (int i = 0; i < query.Rows; i++)
// {
// var sn = query.Text(i, "ServerName");
// if (!string.IsNullOrEmpty(sn)) list.Add(sn);
// }
// query.Dispose();
// return list;
//}
/// <summary>指定的连接凭据是否符合连接要求。</summary>
public static bool Proven(SqlClinet sqlserver)
{
return Proven(sqlserver._address, sqlserver._store, sqlserver._user, sqlserver._pass);
}
#endregion
/// <summary>指定的连接凭据是否符合连接要求,默认指定 master 数据库。</summary>
public static bool Proven(string address, string user, string pass) => Proven(address, "master", user, pass);
#region private
/// <summary>指定的连接凭据是否符合连接要求。</summary>
public static bool Proven(string address, string store, string user, string pass)
static string GetColumnDeclaration(ColumnAttribute column)
{
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;
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(255)");
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);
}
#endregion
#region Linq Utility
private static string GetParameterName(string parameter)
static string GetParameterName(string parameter)
{
var name = TextUtility.AntiInject(parameter, 255);
if (name.StartsWith("@") && name.Length > 1)
@ -782,7 +731,7 @@ namespace Apewer.Source
return name;
}
private static string GetParameterName(IDataParameter parameter)
static string GetParameterName(IDataParameter parameter)
{
var name = TextUtility.Empty;
if (parameter != null)
@ -792,7 +741,7 @@ namespace Apewer.Source
return name;
}
private static List<string> GetParametersNames(IEnumerable<IDataParameter> parameters)
static string[] GetParametersNames(IEnumerable<IDataParameter> parameters)
{
var columns = new List<string>();
if (parameters != null)
@ -805,10 +754,10 @@ namespace Apewer.Source
columns.Add(name);
}
}
return columns;
return columns.ToArray();
}
private static string GenerateInsertStatement(string table, List<string> columns)
static string GenerateInsertStatement(string table, string[] columns)
{
var result = TextUtility.Empty;
var vtable = TextUtility.AntiInject(table, 255);
@ -835,7 +784,7 @@ namespace Apewer.Source
return result;
}
private static string GenerateUpdateStatement(string table, string key, List<string> columns)
static string GenerateUpdateStatement(string table, string key, string[] columns)
{
var result = TextUtility.Empty;
var vtable = TextUtility.AntiInject(table, 255);
@ -858,14 +807,14 @@ namespace Apewer.Source
/// <summary>生成 INSERT INTO 语句。表名必须有效,无有效参数时将获取空结果。</summary>
/// <exception cref="System.ArgumentException"></exception>
/// <exception cref="System.ArgumentNullException"></exception>
private static string GenerateInsertStatement(string table, IEnumerable<IDataParameter> parameters)
static string GenerateInsertStatement(string table, IEnumerable<IDataParameter> parameters)
{
if (table == null) throw new ArgumentNullException(nameof(table));
var tableName = TextUtility.AntiInject(table, 255);
if (TextUtility.IsBlank(tableName)) throw new ArgumentException("表名无效。", nameof(table));
var vcolumns = GetParametersNames(parameters);
if (vcolumns.Count < 1) return TextUtility.Empty;
if (vcolumns.Length < 1) return TextUtility.Empty;
return GenerateInsertStatement(tableName, vcolumns);
}
@ -873,7 +822,7 @@ namespace Apewer.Source
/// <summary>生成 UPDATE 语句,键字段名为“_key”。表名必须有效,键值必须有效,无有效参数时将获取空结果。</summary>
/// <exception cref="System.ArgumentException"></exception>
/// <exception cref="System.ArgumentNullException"></exception>
private static string GenerateUpdateStatement(string table, string key, IEnumerable<IDataParameter> parameters)
static string GenerateUpdateStatement(string table, string key, IEnumerable<IDataParameter> parameters)
{
if (table == null) throw new ArgumentNullException(nameof(table));
var t = TextUtility.AntiInject(table, 255);
@ -884,7 +833,7 @@ namespace Apewer.Source
if (TextUtility.IsBlank(k)) throw new ArgumentException("键值无效。", nameof(table));
var columes = GetParametersNames(parameters);
if (columes.Count < 1) return TextUtility.Empty;
if (columes.Length < 1) return TextUtility.Empty;
return GenerateUpdateStatement(t, k, columes);
}
@ -894,5 +843,3 @@ namespace Apewer.Source
}
}
#endif

26
Apewer.Source/Source/SqlServerSouce.cs

@ -0,0 +1,26 @@
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; }
}
}

420
Apewer.Source/Source/Sqlite.cs

@ -1,8 +1,9 @@
/* 2021.09.23 */
/* 2021.10.14 */
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SQLite;
using System.Text;
//using Mono.Data.Sqlite;
@ -11,73 +12,50 @@ namespace Apewer.Source
{
/// <summary>用于快速连接 SQLite 数据库的辅助。</summary>
public class Sqlite : IDatabase
public class Sqlite : IDbClient
{
#region 变量定义。
#region 基础
private SQLiteConnection _db = null;
private object _locker = new object();
private Timeout _timeout = new Timeout();
private Timeout _timeout = null;
private string _connstring = "";
private string _path = "";
private string _pass = "";
private byte[] _passdata = BytesUtility.Empty;
#endregion
#region this
private void VarInit(string path, Timeout timeout, string pass, byte[] passData)
{
_path = path ?? "";
_passdata = (passData == null) ? BytesUtility.Empty : passData;
_pass = pass ?? "";
_timeout = timeout;
}
/// <summary>连接内存。</summary>
public Sqlite() => VarInit(Memory, new Timeout(), null, null);
/// <summary>连接指定文件。</summary>
public Sqlite(string path) => VarInit(path, new Timeout(), null, null);
/// <summary>连接指定文件。</summary>
private Sqlite(string path, byte[] passData) => VarInit(path, new Timeout(), null, passData);
/// <summary>连接指定文件。</summary>
public Sqlite(string path, string pass) => VarInit(path, new Timeout(), pass, null);
/// <summary>连接指定文件。</summary>
public Sqlite(string path, Timeout timeout) => VarInit(path, timeout, null, null);
#endregion
#region 日志。
private object _locker = new object();
/// <summary>获取或设置日志记录。</summary>
public Logger Logger { get; set; }
private void LogError(string action, Exception ex, string addtion)
{
var logger = Logger;
if (logger != null) logger.Error(this, "SQLite", action, ex.GetType().FullName, ex.Message, addtion);
}
/// <summary>超时设定。</summary>
public Timeout Timeout { get => _timeout; }
private void LogError(string action, string message)
/// <summary>创建连接实例。</summary>
/// <remarks>注意:<br />- 构造函数不会创建不存在的文件;<br />- 参数 path 为文件路径,指定为空时将使用 :memory: 作为路径连接内存。</remarks>
public Sqlite(string path = null, string pass = null, Timeout timeout = null)
{
var logger = Logger;
if (logger != null) logger.Error(this, "SQLite", action, message);
_timeout = timeout ?? Timeout.Default;
_path = path.IsEmpty() ? Memory : path;
_pass = pass;
if (pass.IsEmpty()) _connstring = $"data source='{_path}'; password={_pass}; version=3; ";
else _connstring = $"data source='{_path}'; password={_pass}; version=3; ";
}
#endregion
#region 实现接口。
#region 连接
/// <summary>数据库是否已经连接。</summary>
private SQLiteConnection _db = null;
/// <summary>数据库已经连接。</summary>
public bool Online { get => _db != null && _db.State == ConnectionState.Open; }
/// <summary>连接字符串。</summary>
public string ConnectionString { get => _connstring; }
/// <summary>获取当前的 SQLiteConnection 对象。</summary>
public IDbConnection Connection { get => _db; }
/// <summary>连接数据库,若未连接则尝试连接。</summary>
/// <returns>是否已连接。</returns>
public bool Connect()
@ -86,10 +64,6 @@ namespace Apewer.Source
{
_db = new SQLiteConnection();
_db.ConnectionString = ConnectionString;
//if (string.IsNullOrEmpty(_connstring) && string.IsNullOrEmpty(_pass) && (_passdata.Length > 0))
//{
// _db.SetPassword(_pass);
//}
}
else
{
@ -106,7 +80,7 @@ namespace Apewer.Source
}
catch (Exception ex)
{
LogError("Connection", ex, _db.ConnectionString);
Logger.Error(nameof(Sqlite), "Connection", ex, _db.ConnectionString);
Close();
return false;
}
@ -117,6 +91,11 @@ namespace Apewer.Source
{
if (_db != null)
{
if (_transaction != null)
{
if (_autocommit) Commit();
else Rollback();
}
lock (_locker)
{
_db.Dispose();
@ -128,6 +107,78 @@ namespace Apewer.Source
/// <summary>关闭连接,释放对象所占用的系统资源,并清除连接信息。</summary>
public void Dispose() { Close(); }
#endregion
#region Transaction
private IDbTransaction _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<IsolationLevel> isolation)
{
if (!Connect()) return "未连接。";
if (_transaction != null) return "存在已启动的事务,无法再次启动。";
try
{
_transaction = isolation ? _db.BeginTransaction(isolation.Value) : _db.BeginTransaction();
_autocommit = commit;
return null;
}
catch (Exception ex)
{
Logger.Error(nameof(Sqlite), "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(nameof(Sqlite), "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(nameof(Sqlite), "Rollback", ex.Message());
return ex.Message();
}
}
#endregion
#region SQL
/// <summary>查询。</summary>
public IQuery Query(string sql) => Query(sql, null);
@ -136,43 +187,41 @@ namespace Apewer.Source
{
if (string.IsNullOrEmpty(sql)) return Example.InvalidQueryStatement;
const string table = "result";
var connected = Connect();
if (!connected) return Example.InvalidQueryConnection;
var query = new Query();
try
{
var command = new SQLiteCommand();
command.Connection = _db;
command.CommandTimeout = Timeout.Query;
command.CommandText = sql;
if (parameters != null)
using (var command = new SQLiteCommand())
{
foreach (var p in parameters)
command.Connection = _db;
command.CommandTimeout = _timeout.Query;
command.CommandText = sql;
if (parameters != null)
{
if (p != null) command.Parameters.Add(p);
foreach (var p in parameters)
{
if (p != null) command.Parameters.Add(p);
}
}
}
using (var dataset = new DataSet())
{
using (var da = new SQLiteDataAdapter(sql, _db))
using (var dataset = new DataSet())
{
da.Fill(dataset, table);
query.Table = dataset.Tables[table];
using (var da = new SQLiteDataAdapter(sql, _db))
{
const string name = "result";
da.Fill(dataset, name);
var table = dataset.Tables[name];
return new Query(table);
}
}
}
command.Dispose();
query.Success = true;
}
catch (Exception ex)
{
LogError("Query", ex, sql);
query.Success = false;
query.Exception = ex;
Logger.Error(nameof(Sqlite), "Query", ex, sql);
return new Query(ex);
}
return query;
}
/// <summary>执行单条 Transact-SQL 语句。</summary>
@ -188,36 +237,34 @@ namespace Apewer.Source
lock (_locker)
{
var transaction = _db.BeginTransaction();
var execute = new Execute();
var inTransaction = _transaction != null;
if (!inTransaction) Begin();
try
{
var command = new SQLiteCommand();
command.Connection = _db;
command.Transaction = transaction;
command.CommandTimeout = Timeout.Execute;
command.CommandText = sql;
if (parameters != null)
using (var command = new SQLiteCommand())
{
foreach (var p in parameters)
command.Connection = _db;
command.Transaction = (SQLiteTransaction)_transaction;
command.CommandTimeout = _timeout.Execute;
command.CommandText = sql;
if (parameters != null)
{
if (p != null) command.Parameters.Add(p);
foreach (var p in parameters)
{
if (p != null) command.Parameters.Add(p);
}
}
var rows = command.ExecuteNonQuery();
if (!inTransaction) Commit(); // todo 此处应该检查事务提交产生的错误。
return new Execute(true, rows);
}
execute.Rows += command.ExecuteNonQuery();
transaction.Commit();
command.Dispose();
execute.Success = true;
}
catch (Exception ex)
{
try { transaction.Rollback(); } catch { }
LogError("Execute", ex, sql);
execute.Success = false;
execute.Exception = ex;
Logger.Error(nameof(Sqlite), "Execute", ex, sql);
if (!inTransaction) Rollback();
return new Execute(ex);
}
try { transaction.Dispose(); } catch { }
return execute;
}
}
@ -225,72 +272,12 @@ namespace Apewer.Source
#region 属性。
/// <summary>获取当前的 SQLiteConnection 对象。</summary>
public IDbConnection Connection { get => _db; }
/// <summary>获取或设置超时。</summary>
public Timeout Timeout { get => _timeout; set => _timeout = value; }
/// <summary>获取或设置连接字符串,连接字符串非空时将忽略 Path 属性。数据库在线时无法设置。</summary>
public string ConnectionString
{
get
{
if (string.IsNullOrEmpty(_connstring))
{
var temp = new StringBuilder();
temp.Append("data source='", _path, "'; version=3; ");
if (!string.IsNullOrEmpty(_pass)) temp.Append("password=", _pass, "; ");
return temp.ToString();
}
else return _connstring;
}
set
{
if (Online) return;
_connstring = string.IsNullOrEmpty(value) ? "" : value;
}
}
/// <summary>获取或设置数据库路径(文件或内存)。数据库在线时无法设置。</summary>
public string Path
{
get { return _path; }
set
{
if (Online) return;
_path = string.IsNullOrEmpty(value) ? "" : value;
}
}
/// <summary>获取或设置数据库密码。数据库在线时无法设置。</summary>
public string Password
{
get { return _pass; }
set
{
if (Online) return;
_pass = string.IsNullOrEmpty(value) ? "" : value;
}
}
/// <summary>获取或设置数据库密码。数据库在线时无法设置。</summary>
private byte[] PasswordData
{
get { return _passdata; }
set
{
if (Online) return;
_passdata = (value == null) ? BytesUtility.Empty : value;
}
}
/// <summary>保存当前数据库到文件,若文件已存在则将重写文件。</summary>
public bool Save(string path, string pass = null)
{
if (!StorageUtility.CreateFile(path, 0, true))
{
LogError("Save", TextUtility.Merge("创建文件 ", path, " 失败。"));
Logger.Error(nameof(Sqlite), "Save", TextUtility.Merge("创建文件 ", path, " 失败。"));
return false;
}
@ -311,10 +298,10 @@ namespace Apewer.Source
#endregion
#region ORM
#region ORM
/// <summary>查询数据库中的所有表名。</summary>
public List<string> TableNames()
public string[] TableNames()
{
var list = new List<string>();
if (Connect())
@ -329,11 +316,11 @@ namespace Apewer.Source
}
query.Dispose();
}
return list;
return list.ToArray();
}
/// <summary>查询数据库中的所有视图名。</summary>
public List<string> ViewNames()
public string[] ViewNames()
{
var list = new List<string>();
if (Connect())
@ -348,11 +335,11 @@ namespace Apewer.Source
}
query.Dispose();
}
return list;
return list.ToArray();
}
/// <summary>查询表中的所有列名。</summary>
public List<string> ColumnNames(string table)
public string[] ColumnNames(string table)
{
var list = new List<string>();
if (Connect())
@ -369,7 +356,7 @@ namespace Apewer.Source
}
}
}
return list;
return list.ToArray();
}
/// <summary>创建表,不修改已存在表。成功时返回空字符串,发生异常时返回异常信息。</summary>
@ -381,9 +368,8 @@ namespace Apewer.Source
/// <summary>创建表,不修改已存在表。当现存表中缺少模型中属性对应的列时增加列。成功时返回空字符串,发生异常时返回异常信息。</summary>
public string Initialize(Type model)
{
var structure = null as TableStructure;
try { structure = TableStructure.ParseModel(model); }
catch (Exception exception) { return exception.Message; }
var structure = TableStructure.Parse(model);
if (structure == null) return "无法解析记录模型。";
// 连接数据库。
if (!Connect()) return "连接数据库失败。";
@ -391,9 +377,9 @@ namespace Apewer.Source
// 检查现存表。
var exists = false;
var tables = TableNames();
if (tables.Count > 0)
if (tables.Length > 0)
{
var lower = structure.Table.ToLower();
var lower = structure.Name.ToLower();
foreach (var table in tables)
{
if (TextUtility.IsBlank(table)) continue;
@ -412,73 +398,69 @@ namespace Apewer.Source
else
{
var sqlcolumns = new List<string>();
foreach (var column in structure.Columns.Values)
foreach (var column in structure.Columns)
{
var type = GetColumnDeclaration(column);
if (type == TextUtility.Empty) return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。");
sqlcolumns.Add(type);
}
var sql = TextUtility.Merge("create table [", structure.Table, "](", TextUtility.Join(", ", sqlcolumns), "); ");
var sql = TextUtility.Merge("create table [", structure.Name, "](", TextUtility.Join(", ", sqlcolumns), "); ");
var execute = Execute(sql);
if (execute.Success) return TextUtility.Empty;
return execute.Error;
return execute.Message;
}
}
/// <summary>插入记录。成功时候返回空字符串,发生异常时返回异常信息。</summary>
/// <summary>插入记录。返回错误信息。</summary>
public string Insert(IRecord record)
{
if (record == null) return "参数无效。";
record.FixProperties();
var structure = null as TableStructure;
try { structure = TableStructure.ParseModel(record); }
catch (Exception exception) { return exception.Message; }
var parameters = structure.CreateDataParameters(record, CreateDataParameter);
var sql = GenerateInsertStatement(structure.Table, (IEnumerable<IDataParameter>)parameters);
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
var parameters = structure.CreateParameters(record, CreateDataParameter);
var sql = GenerateInsertStatement(structure.Name, (IEnumerable<IDataParameter>)parameters);
var execute = Execute(sql, parameters);
if (execute.Success && execute.Rows > 0) return TextUtility.Empty;
return execute.Error;
return execute.Message;
}
/// <summary>更新记录,实体中的 Created 和 Key 属性不被更新。成功时返回空字符串,发生异常时返回异常信息。</summary>
/// <summary>更新记录,实体中的 Key 属性不被更新。返回错误信息。</summary>
/// <remarks>无法更新带有 Independent 特性的模型(缺少 Key 属性)。</remarks>
public string Update(IRecord record)
{
if (record == null) return "参数无效。";
record.FixProperties();
record.SetUpdated();
var structure = null as TableStructure;
try { structure = TableStructure.ParseModel(record); }
catch (Exception exception) { return exception.Message; }
var parameters = structure.CreateDataParameters(record, CreateDataParameter, "_created", "_key");
var sql = GenerateUpdateStatement(structure.Table, record.Key, parameters);
var structure = TableStructure.Parse(record.GetType());
if (structure == null) return "无法解析记录模型。";
if (structure.Independent) return "无法更新带有 Independent 特性的模型。";
var parameters = structure.CreateParameters(record, CreateDataParameter, "_key");
var sql = GenerateUpdateStatement(structure.Name, record.Key, parameters);
var execute = Execute(sql, parameters);
if (execute.Success && execute.Rows > 0) return TextUtility.Empty;
return execute.Error;
return execute.Message;
}
/// <summary>获取按指定语句查询到的所有记录。</summary>
public Result<List<IRecord>> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
public Result<IRecord[]> Query(Type model, string sql) => OrmHelper.Query(this, model, sql);
/// <summary>获取按指定语句查询到的所有记录。</summary>
public Result<List<T>> Query<T>(string sql) where T : class, IRecord, new() => OrmHelper.Query<T>(this, sql);
public Result<T[]> Query<T>(string sql) where T : class, IRecord, new() => OrmHelper.Query<T>(this, sql);
/// <summary>查询多条记录。</summary>
public Result<List<IRecord>> Query(Type model, long flag = 0) => OrmHelper.Query(this, model, (tn) =>
public Result<IRecord[]> 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 Result<List<T>> Query<T>(long flag = 0) where T : class, IRecord, new() => OrmHelper.Query<T>(this, (tn) =>
public Result<T[]> Query<T>(long flag = 0) where T : class, IRecord, new() => OrmHelper.Query<T>(this, (tn) =>
{
if (flag == 0) return $"select * from [{tn}]; ";
return $"select * from [{tn}] where _flag={flag}; ";
@ -499,14 +481,14 @@ namespace Apewer.Source
});
/// <summary>获取指定类型的主键,按 Flag 属性筛选。</summary>
public Result<List<string>> Keys(Type model, long flag = 0) => OrmHelper.Keys(this, model, (tn) =>
public Result<string[]> Keys(Type model, long flag = 0) => OrmHelper.Keys(this, model, (tn) =>
{
if (flag == 0) return $"select _key from [{tn}] where _flag={flag}; ";
return $"select _key from [{tn}]; ";
});
/// <summary>>获取指定类型的主键,按 Flag 属性筛选。</summary>
public Result<List<string>> Keys<T>(long flag = 0) where T : class, IRecord, new() => Keys(typeof(T), flag);
public Result<string[]> Keys<T>(long flag = 0) where T : class, IRecord, new() => Keys(typeof(T), flag);
#endregion
@ -553,13 +535,13 @@ namespace Apewer.Source
case ColumnType.Float:
return "float";
case ColumnType.VarChar:
case ColumnType.VarChar255:
case ColumnType.VarChar191:
case ColumnType.VarCharMax:
return "varchar";
case ColumnType.Text:
return "text";
case ColumnType.NVarChar:
case ColumnType.NVarChar255:
case ColumnType.NVarChar191:
case ColumnType.NVarCharMax:
return "nvarchar";
case ColumnType.NText:
@ -585,9 +567,11 @@ namespace Apewer.Source
type = "real";
break;
case ColumnType.VarChar:
case ColumnType.VarChar255:
type = TextUtility.Merge("varchar(", length, ")");
break;
case ColumnType.VarChar191:
type = TextUtility.Merge("varchar(191)");
break;
case ColumnType.VarCharMax:
type = TextUtility.Merge("varchar(255)");
break;
@ -595,9 +579,11 @@ namespace Apewer.Source
type = TextUtility.Merge("text");
break;
case ColumnType.NVarChar:
case ColumnType.NVarChar255:
type = TextUtility.Merge("nvarchar(", length, ")");
break;
case ColumnType.NVarChar191:
type = TextUtility.Merge("nvarchar(191)");
break;
case ColumnType.NVarCharMax:
type = TextUtility.Merge("nvarchar(255)");
break;
@ -636,9 +622,11 @@ namespace Apewer.Source
case ColumnType.NVarChar:
s = NumberUtility.Restrict(s, 0, 4000);
break;
case ColumnType.VarChar255:
case ColumnType.VarChar191:
case ColumnType.NVarChar191:
s = NumberUtility.Restrict(s, 0, 191);
break;
case ColumnType.VarCharMax:
case ColumnType.NVarChar255:
case ColumnType.NVarCharMax:
s = NumberUtility.Restrict(s, 0, 255);
break;
@ -742,7 +730,22 @@ namespace Apewer.Source
#endregion
#region ORM
#region 生成 SQL 语句
/// <summary>生成 INSERT INTO 语句。表名必须有效,无有效参数时将获取空结果。</summary>
/// <exception cref="System.ArgumentException"></exception>
/// <exception cref="System.ArgumentNullException"></exception>
public static string GenerateInsertStatement(string table, IEnumerable<IDataParameter> parameters)
{
if (table == null) throw new ArgumentNullException(nameof(table));
var t = TextUtility.AntiInject(table, 255);
if (TextUtility.IsBlank(t)) throw new ArgumentException("表名无效。", nameof(table));
var cs = GetParametersNames(parameters);
if (cs.Count < 1) return TextUtility.Empty;
return GenerateInsertStatement(t, cs);
}
private static string GetParameterName(string parameter)
{
@ -807,21 +810,6 @@ namespace Apewer.Source
return r;
}
/// <summary>生成 INSERT INTO 语句。表名必须有效,无有效参数时将获取空结果。</summary>
/// <exception cref="System.ArgumentException"></exception>
/// <exception cref="System.ArgumentNullException"></exception>
public static string GenerateInsertStatement(string table, IEnumerable<IDataParameter> parameters)
{
if (table == null) throw new ArgumentNullException(nameof(table));
var t = TextUtility.AntiInject(table, 255);
if (TextUtility.IsBlank(t)) throw new ArgumentException("表名无效。", nameof(table));
var cs = GetParametersNames(parameters);
if (cs.Count < 1) return TextUtility.Empty;
return GenerateInsertStatement(t, cs);
}
private static string GenerateUpdateStatement(string table, string key, List<string> columns)
{
var result = TextUtility.Empty;

12
Apewer.Web/Internals/ApiHelper.cs

@ -301,7 +301,7 @@ namespace Apewer.Internals
#region Response
static StringPairs MergeHeaders(ApiOptions options, ApiResponse response)
static StringPairs PrepareHeaders(ApiOptions options, ApiResponse response)
{
var merged = new StringPairs();
if (options != null)
@ -322,6 +322,12 @@ namespace Apewer.Internals
{
merged.Add("X-Content-Type-Options", "nosniff");
}
// 用于客户端,当前页面使用 HTTPS 时,将资源升级为 HTTPS。
if (options.UpgradeHttps)
{
merged.Add("Content-Security-Policy", "upgrade-insecure-requests");
}
}
if (response != null)
{
@ -397,7 +403,7 @@ namespace Apewer.Internals
var preOutput = provider.PreWrite();
if (!string.IsNullOrEmpty(preOutput)) return;
var headers = MergeHeaders(options, null);
var headers = PrepareHeaders(options, null);
foreach (var header in headers) provider.SetHeader(header.Key, header.Value);
provider.SetCache(0);
@ -414,7 +420,7 @@ namespace Apewer.Internals
if (!string.IsNullOrEmpty(preOutput)) return;
// 设置头。
var headers = MergeHeaders(options, null);
var headers = PrepareHeaders(options, null);
foreach (var header in headers) provider.SetHeader(header.Key, header.Value);
var model = response.Model;

2
Apewer.Web/Web/ApiEntries.cs

@ -72,7 +72,7 @@ namespace Apewer.Web
public static ApiEntries From(Assembly assembly)
{
if (assembly == null) return null;
var types = RuntimeUtility.GetTypes(assembly, true);
var types = RuntimeUtility.GetTypes(assembly, false);
var dict = new Dictionary<string, ApiApplication>();
foreach (var type in types)
{

8
Apewer.Web/Web/ApiProcessor.cs

@ -261,6 +261,14 @@ namespace Apewer.Web
return;
}
// 未知类型,尝试 Json 类型。
var json = result as Json;
if (json != null)
{
response.Data = json;
return;
}
// 未知返回类型,无法明确输出格式,忽略。
}
else

6
Apewer.Web/Web/ApiProgram.cs

@ -17,13 +17,13 @@ namespace Apewer.Web
private static ApiInvoker _invoker = new ApiInvoker() { Logger = new Logger(), Options = new ApiOptions() };
/// <summary>API 选项。</summary>
protected static ApiOptions Options { get => _invoker.Options; }
public static ApiOptions Options { get => _invoker.Options; }
/// <summary>日志记录器。</summary>
protected static Logger Logger { get => _invoker.Logger; }
public static Logger Logger { get => _invoker.Logger; }
/// <summary>获取或设置 API 入口。</summary>
protected static ApiEntries Entries { get => _invoker.Entries; set => _invoker.Entries = value; }
public static ApiEntries Entries { get => _invoker.Entries; set => _invoker.Entries = value; }
private Action _initializer = null;

31
Apewer/ArrayBuilder.cs

@ -6,7 +6,7 @@ namespace Apewer
{
/// <summary>数组构建器。</summary>
public class ArrayBuilder<T>
public sealed class ArrayBuilder<T>
{
private T[] _array;
@ -35,6 +35,25 @@ namespace Apewer
if (_count > 0) Array.Copy(old._array, _array, _count);
}
/// <summary>获取或设置指定位置的元素,索引器范围为 [0, Length)。</summary>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public T this[int index]
{
get
{
if (index < 0 || index >= _count) throw new ArgumentOutOfRangeException("索引超出了当前数组的范围。");
return _array[index];
}
set
{
if (index < 0 || index >= _count) throw new ArgumentOutOfRangeException("索引超出了当前数组的范围。");
_array[index] = value;
}
}
/// <summary>缓冲区的容量。</summary>
public int Capacity { get => _capacity; }
/// <summary>当前的元素数量。</summary>
public int Length { get => _count; }
@ -95,6 +114,13 @@ namespace Apewer
_count += length;
}
/// <summary>添加多个元素。</summary>
public void Add(IEnumerable<T> items)
{
if (items == null) return;
foreach (var item in items) Add(item);
}
/// <summary>清空所有元素。</summary>
public void Clear()
{
@ -138,6 +164,9 @@ namespace Apewer
/// <summary>克隆当前实例,生成新实例。</summary>
public ArrayBuilder<T> Clone() => new ArrayBuilder<T>(this);
/// <summary>使用 Export 方法实现从 ArrayBuilder&lt;T&gt; 到 T[] 的隐式转换。</summary>
public static implicit operator T[](ArrayBuilder<T> instance) => instance == null ? null : instance.Export();
}
}

39
Apewer/ClockUtility.cs

@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
namespace Apewer
@ -104,7 +105,7 @@ namespace Apewer
/// <summary>从毫秒时间戳获取 DateTime 对象。发生异常且不允许异常时将返回 1970-01-01 00:00:00.000。</summary>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static DateTime FromStamp(long stamp, bool exceptable = true)
public static DateTime FromStamp(long stamp, bool throwException = true)
{
try
{
@ -113,13 +114,47 @@ namespace Apewer
}
catch
{
if (exceptable) throw new ArgumentOutOfRangeException();
if (throwException) throw new ArgumentOutOfRangeException();
return Origin;
}
}
#endregion
#region Text
/// <summary>解析文本,获取 DateTime 对象。</summary>
public static Class<DateTime> FromText(string text)
{
var str = text;
if (string.IsNullOrEmpty(str)) return null;
var utc = false;
var lower = str.ToLower();
if (lower.EndsWith(" utc"))
{
utc = true;
str = str.Substring(0, str.Length - 4);
}
DateTime dt;
if (!DateTime.TryParse(str, out dt))
{
if (!str.Contains("-") && DateTime.TryParseExact(str, "yyyy-M-d", null, DateTimeStyles.None, out dt))
{
if (!str.Contains("/") && DateTime.TryParseExact(str, "yyyy/M/d", null, DateTimeStyles.None, out dt))
{
return null;
}
}
}
if (utc) dt = new DateTime(dt.Ticks, DateTimeKind.Utc);
return new Class<DateTime>(dt);
}
#endregion
#region Lucid & Compact
/// <summary>表示当前本地时间的文本,显示为易于阅读的格式。</summary>

100
Apewer/Json.cs

@ -276,32 +276,32 @@ namespace Apewer
#region Private Get
private List<Json> PrivateGetProperties { get { return GetProperties(); } }
private Json[] PrivateGetProperties { get { return GetProperties(); } }
private List<Json> PrivateGetValues { get { return GetValues(); } }
private Json[] PrivateGetValues { get { return GetValues(); } }
private List<Json> PrivateGetObjects { get { return GetObjects(); } }
private Json[] PrivateGetObjects { get { return GetObjects(); } }
private List<Json> PrivateGetItems { get { return GetItems(); } }
private Json[] PrivateGetItems { get { return GetItems(); } }
#endregion
#region Object : Get/Set
/// <summary>获取所有类型为 Property 的子项。</summary>
public List<Json> GetProperties()
public Json[] GetProperties()
{
var list = new List<Json>();
var ab = new ArrayBuilder<Json>();
if (_jobject != null)
{
var children = _jobject.Children();
foreach (var child in children)
{
var json = new Json(child);
list.Add(json);
ab.Add(json);
}
}
return list;
return ab.Export();
}
/// <summary>当前实例类型为 Object 时搜索属性,失败时返回 Null。</summary>
@ -690,51 +690,51 @@ namespace Apewer
#region Array
/// <summary>获取所有类型为 Value 的子项。</summary>
public List<Json> GetValues()
public Json[] GetValues()
{
var list = new List<Json>();
var ab = new ArrayBuilder<Json>();
if (_jarray != null)
{
var children = _jarray.Children<JValue>();
foreach (var child in children)
{
var json = new Json(child);
list.Add(json);
ab.Add(json);
}
}
return list;
return ab.Export();
}
/// <summary>获取所有类型为 Object 的子项。</summary>
public List<Json> GetObjects()
public Json[] GetObjects()
{
var list = new List<Json>();
var ab = new ArrayBuilder<Json>();
if (_jarray != null)
{
var children = _jarray.Children<JObject>();
foreach (var child in children)
{
var json = new Json(child);
list.Add(json);
ab.Add(json);
}
}
return list;
return ab.Export();
}
/// <summary>获取 Array 中的所有元素。</summary>
public List<Json> GetItems()
public Json[] GetItems()
{
var list = new List<Json>();
var ab = new ArrayBuilder<Json>();
if (_jarray != null)
{
var children = _jarray.Children();
foreach (var child in children)
{
var json = new Json(child);
list.Add(json);
ab.Add(json);
}
}
return list;
return ab.Export();
}
/// <summary>当前实例类型为 Array 时添加 Null 元素。</summary>
@ -1116,8 +1116,7 @@ namespace Apewer
public static Json From(IList entity, bool lower = false, int depth = -1, bool force = false)
{
if (entity == null) return null;
var recursive = new List<object>();
recursive.Add(entity);
var recursive = new object[] { entity };
return From(entity, lower, recursive, depth, force);
}
@ -1129,8 +1128,7 @@ namespace Apewer
public static Json From(IDictionary entity, bool lower = false, int depth = -1, bool force = false)
{
if (entity == null) return null;
var recursive = new List<object>();
recursive.Add(entity);
var recursive = new object[] { entity };
return From(entity, lower, recursive, depth, force);
}
@ -1146,12 +1144,11 @@ namespace Apewer
public static Json From(object entity, bool lower = false, int depth = -1, bool force = false)
{
if (entity == null) return null;
var recursive = new List<object>();
recursive.Add(entity);
var recursive = new object[] { entity };
return From(entity, lower, recursive, depth, force);
}
private static Json From(IList list, bool lower, List<object> previous, int depth, bool force)
private static Json From(IList list, bool lower, object[] previous, int depth, bool force)
{
if (list == null) return null;
if (list is IToJson) return ((IToJson)list).ToJson();
@ -1187,13 +1184,13 @@ namespace Apewer
if (recursively) continue;
// 处理 Type 对象。
if (value.GetType().Equals(typeof(Type)) && (previous.Count > 2))
if (value.GetType().Equals(typeof(Type)) && (previous.Length > 2))
{
value = ((Type)value).FullName;
}
// 处理 Assembly 对象。
if (value.GetType().Equals(typeof(Assembly)) && (previous.Count > 2))
if (value.GetType().Equals(typeof(Assembly)) && (previous.Length > 2))
{
value = ((Assembly)value).FullName;
}
@ -1210,10 +1207,10 @@ namespace Apewer
else if (value is Json) { json.AddItem(value as Json); }
else
{
if ((depth < 0) || (0 < depth && previous.Count < depth))
if ((depth < 0) || (0 < depth && previous.Length < depth))
{
var recursive = new List<object>();
recursive.AddRange(previous);
var recursive = new ArrayBuilder<object>();
recursive.Add(previous);
recursive.Add(value);
if (value is IDictionary) { json.AddItem(From(value as IDictionary, lower, recursive, depth, force)); }
@ -1231,7 +1228,7 @@ namespace Apewer
return json;
}
private static Json From(IDictionary dictionary, bool lower, List<object> previous, int depth, bool force)
private static Json From(IDictionary dictionary, bool lower, object[] previous, int depth, bool force)
{
if (dictionary == null) return null;
if (dictionary is IToJson) return ((IToJson)dictionary).ToJson();
@ -1286,13 +1283,13 @@ namespace Apewer
if (value != null)
{
// 处理 Type 对象。
if (value.GetType().Equals(typeof(Type)) && (previous.Count > 2))
if (value.GetType().Equals(typeof(Type)) && (previous.Length > 2))
{
value = ((Type)value).FullName;
}
// 处理 Assembly 对象。
if (value.GetType().Equals(typeof(Assembly)) && (previous.Count > 2))
if (value.GetType().Equals(typeof(Assembly)) && (previous.Length > 2))
{
value = ((Assembly)value).FullName;
}
@ -1310,10 +1307,10 @@ namespace Apewer
else if (value is Json) { json.SetProperty(field, value as Json); }
else
{
if ((depth < 0) || (0 < depth && previous.Count < depth))
if ((depth < 0) || (0 < depth && previous.Length < depth))
{
var recursive = new List<object>();
recursive.AddRange(previous);
var recursive = new ArrayBuilder<object>();
recursive.Add(previous);
recursive.Add(value);
if (value is IDictionary) { json.SetProperty(field, From(value as IDictionary, lower, recursive, depth, force)); }
@ -1333,7 +1330,7 @@ namespace Apewer
return json;
}
private static Json From(object entity, bool lower, List<object> previous, int depth, bool force)
private static Json From(object entity, bool lower, object[] previous, int depth, bool force)
{
if (entity == null) return null;
if (entity is IToJson) return ((IToJson)entity).ToJson();
@ -1348,7 +1345,7 @@ namespace Apewer
}
if (entity is Json) { if (lower) Lower(entity as Json); return entity as Json; }
else if (entity is String) { return From((String)entity, lower); }
else if (entity is String) { return From((String)entity); }
else if (entity is IDictionary) { return From(entity as IDictionary, (bool)lower); }
else if (entity is IList) { return From(entity as IList, (bool)lower); }
@ -1395,13 +1392,13 @@ namespace Apewer
if (checker != null && !checker.WithPropertyInJson(entity, property, value)) continue;
// 处理 Type 对象。
if (getter.ReturnType.Equals(typeof(Type)) && (previous.Count > 2))
if (getter.ReturnType.Equals(typeof(Type)) && (previous.Length > 2))
{
value = ((Type)value).FullName;
}
// 处理 Assembly 对象。
if (getter.ReturnType.Equals(typeof(Assembly)) && (previous.Count > 2))
if (getter.ReturnType.Equals(typeof(Assembly)) && (previous.Length > 2))
{
value = ((Assembly)value).FullName;
}
@ -1461,8 +1458,8 @@ namespace Apewer
return (T)entity;
}
/// <summary>将 Json 数组填充到列表,失败时返回 NULL 值。</summary>
internal static List<T> Array<T>(Json json, bool ignoreCase = true, string ignoreCharacters = null, bool force = false) where T : class, new()
/// <summary>将 Json 填充到数组列表,失败时返回 NULL 值。</summary>
internal static T[] Array<T>(Json json, bool ignoreCase = true, string ignoreCharacters = null, bool force = false) where T : class, new()
{
if (json == null) return null;
if (json._jtoken == null) return null;
@ -1470,7 +1467,7 @@ namespace Apewer
var list = new List<T>();
Array(list, json, ignoreCase, ignoreCharacters, force);
return list;
return list.ToArray();
}
/// <summary></summary>
@ -1480,7 +1477,7 @@ namespace Apewer
if (json.TokenType != JTokenType.Object) return;
var jps = json.GetProperties();
if (jps.Count < 1) return;
if (jps.Length < 1) return;
var etype = entity.GetType();
var eps = etype.GetProperties();
@ -1998,6 +1995,7 @@ namespace Apewer
{
if (type == null) return false;
if (type.Equals(typeof(object))) return false;
var sas = type.GetCustomAttributes(typeof(SerializableAttribute), inherit);
if (sas != null && sas.Length > 0) return true;
@ -2055,7 +2053,7 @@ namespace Apewer
/// <param name="json">将要反序列化的 JSON 字符串。</param>
/// <param name="returnNullOnError">发生错误时返回 NULL 值,设置为 FALSE 时返回空 List&lt;<typeparamref name="T"/>&gt; 对象。</param>
/// <returns></returns>
public static List<T> DeserializeList<T>(string json, bool returnNullOnError = false) where T : class
public static T[] DeserializeArray<T>(string json, bool returnNullOnError = false) where T : class
{
try
{
@ -2065,14 +2063,12 @@ namespace Apewer
{
using (var jtr = new JsonTextReader(sr))
{
@object = serializer.Deserialize(jtr, typeof(List<T>));
@object = serializer.Deserialize(jtr, typeof(T[]));
}
}
var list = @object as List<T>;
if (list == null) list = new List<T>();
return list;
return (@object as T[]) ?? new T[0];
}
catch { return returnNullOnError ? null : new List<T>(); }
catch { return returnNullOnError ? null : new T[0]; }
}
#endif

2
Apewer/Network/HttpClient.cs

@ -16,7 +16,7 @@ namespace Apewer.Network
public class HttpClient
{
private string _key = TextUtility.NewGuid();
private string _key = TextUtility.Guid();
internal bool _locked = false;
private TextSet _properties = new TextSet(true);

16
Apewer/Result.cs

@ -87,10 +87,10 @@ namespace Apewer
public Result(int code, string message = null) : base(code, message) { }
/// <summary>创建实例:Value = Default。</summary>
public Result(Exception exception, int code = 0) : base(Stringify(exception), code) { }
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, Stringify(exception)) { }
public Result(int code, Exception exception = null) : base(code, RuntimeUtility.Message(exception)) { }
private void Set(T value)
{
@ -98,18 +98,6 @@ namespace Apewer
_has = typeof(T).IsValueType ? true : (value != null);
}
private static string Stringify(Exception exception)
{
if (exception == null) return null;
var message = exception.Message;
if (string.IsNullOrEmpty(message))
{
var type = exception.GetType().FullName;
message = $"包含了无效消息的 {type}。";
}
return message;
}
#region 运算符。
/// <summary>含有实体对象。</summary>

45
Apewer/RuntimeUtility.cs

@ -351,7 +351,7 @@ namespace Apewer
// 忽略 System.Object。
var quantum = typeof(object);
if (@base.Equals(quantum)) return true;
if (child.Equals(quantum)) return true;
if (child.Equals(quantum)) return false;
// 循环判断基类。
var current = child;
@ -368,12 +368,12 @@ namespace Apewer
}
/// <summary></summary>
public static Type[] GetTypes(Assembly assembly, bool onlyExperted = false)
public static Type[] GetTypes(Assembly assembly, bool onlyExported = false)
{
if (assembly == null) return null;
try
{
return onlyExperted ? assembly.GetExportedTypes() : assembly.GetTypes();
return onlyExported ? assembly.GetExportedTypes() : assembly.GetTypes();
}
catch { }
return new Type[0];
@ -421,6 +421,22 @@ namespace Apewer
return false;
}
/// <summary>在程序集中枚举派生类型,可自定义检查器。</summary>
public static Type[] DerivedTypes(Type baseType, Assembly assembly, Func<Type, bool> checker)
{
if (baseType == null) return new Type[0];
if (assembly == null) return new Type[0];
var types = GetTypes(assembly);
var list = new List<Type>(types.Length);
foreach (var type in types)
{
if (!IsInherits(type, baseType)) continue;
if (checker != null && !checker(type)) continue;
list.Add(type);
}
return list.ToArray();
}
#endregion
#region Collect & Dispose
@ -763,6 +779,29 @@ namespace Apewer
#endregion
#region Exception
internal static string Message(Exception ex)
{
if (ex == null) return null;
try
{
var message = ex.Message;
if (!string.IsNullOrEmpty(message)) return message;
var typeName = ex.GetType().FullName;
message = $"异常 <{typeName}> 包含空消息。";
return message;
}
catch
{
var typeName = ex.GetType().FullName;
return $"获取 <{typeName}> 的消息时再次发生了异常。";
}
}
#endregion
}
}

167
Apewer/Source/ColumnAttribute.cs

@ -7,35 +7,37 @@ using System.Text;
namespace Apewer.Source
{
/// <summary>数据库中的列,类型默认为 NVarChar(191),错误类型将修正为 NText。</summary>
/// <summary>数据库中的列,类型默认为 NVarChar(191),错误类型将修正为默认类型。</summary>
/// <remarks>注意:当一个数据模型中存在多个相同的 Field 时,将只有第一个被保留。</remarks>
[Serializable]
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class ColumnAttribute : Attribute
{
private PropertyInfo _property = null;
internal string PropertyName = null;
private string _field = "";
private int _length = 0;
private ColumnType _type;
private bool _independent = false;
private bool _valid = true;
/// <exception cref="System.ArgumentException"></exception>
private void Init(string field, ColumnType type, int length, bool underline)
private void Init(string field, ColumnType type, int length)
{
_field = string.IsNullOrEmpty(field) ? "" : TableStructure.RestrictName(field, underline);
if (string.IsNullOrEmpty(field)) field = TableStructure.RestrictName(field, string.IsNullOrEmpty(field));
_field = string.IsNullOrEmpty(field) ? "" : TableStructure.RestrictName(field, string.IsNullOrEmpty(field));
_type = type;
switch (type)
{
case ColumnType.VarChar:
case ColumnType.NVarChar:
if (length < 1) throw new ArgumentException("最大长度无效。");
_length = length;
_length = length < 1 ? 191 : length;
break;
case ColumnType.VarChar255:
case ColumnType.NVarChar255:
_length = 255;
case ColumnType.VarChar191:
case ColumnType.NVarChar191:
_length = 191;
break;
default:
_length = length;
@ -44,48 +46,135 @@ namespace Apewer.Source
}
/// <summary>使用自动的列名称。当类型为 VarChar 或 NVarChar 时必须指定长度。</summary>
/// <exception cref="System.ArgumentException"></exception>
public ColumnAttribute(ColumnType type = ColumnType.NVarChar, int length = 191) => Init(null, type, length, true);
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
public ColumnAttribute(ColumnType type = ColumnType.NVarChar191, int length = 191) => Init(null, type, length);
/// <summary>使用指定的列名称。当类型为 VarChar 或 NVarChar 时必须指定长度。</summary>
/// <exception cref="System.ArgumentException"></exception>
public ColumnAttribute(string field, ColumnType type = ColumnType.NVarChar, int length = 191) => Init(field, type, length, false);
internal ColumnAttribute(string field, ColumnType type, int length, bool underline) => Init(field, type, length, underline);
/// <summary>属性。</summary>
public PropertyInfo Property
{
get => _property;
internal set => _property = value;
}
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
public ColumnAttribute(string field, ColumnType type = ColumnType.NVarChar191, int length = 191) => Init(field, type, length);
/// <summary>字段名。</summary>
public string Field
{
get => _field;
set => _field = value;
}
public string Field { get => _field; }
/// <summary>指定字段的最大长度。</summary>
public int Length
{
get => _length;
set => _length = value;
}
public int Length { get => _length; }
/// <summary>字段类型。</summary>
public ColumnType Type
public ColumnType Type { get => _type; }
#region 附加
/// <summary>此特性有效。</summary>
public bool Valid { get => _valid; }
/// <summary>Independent 特性。</summary>
public bool Independent { get => _independent; }
/// <summary>使用此特性的属性。</summary>
public PropertyInfo Property { get => _property; }
#endregion
/// <summary>解析列特性。</summary>
/// <remarks>注意:此方法不再抛出异常,当不存在正确的列特性时将返回 NULL 值</remarks>
public static ColumnAttribute Parse(Type type, PropertyInfo property, TableAttribute ta)
{
get => _type;
set => _type = value;
if (type == null || property == null || ta == null) return null;
// 属性带有 Independent 特性。
if (property.Contains<IndependentAttribute>()) return null;
// 检查 ColumnAttribute。
ColumnAttribute ca;
{
var cas = property.GetCustomAttributes(typeof(ColumnAttribute), false);
if (cas.LongLength < 1L)
{
if (!ta.AllProperties) return null;
ca = new ColumnAttribute();
}
else ca = (ColumnAttribute)cas[0];
}
// 检查属性方法。
var getter = property.GetGetMethod(false);
var setter = property.GetSetMethod(false);
if (getter == null || getter.IsStatic) return null;
if (setter == null || setter.IsStatic) return null;
// 检查列名称。
if (TextUtility.IsBlank(ca.Field)) ca._field = "_" + property.Name;
// 类型兼容。
var pt = property.PropertyType;
if (pt.Equals(typeof(byte[]).FullName)) ca._type = ColumnType.Bytes;
else if (pt.Equals(typeof(Byte))) ca._type = ColumnType.Integer;
else if (pt.Equals(typeof(SByte))) ca._type = ColumnType.Integer;
else if (pt.Equals(typeof(Int16))) ca._type = ColumnType.Integer;
else if (pt.Equals(typeof(UInt16))) ca._type = ColumnType.Integer;
else if (pt.Equals(typeof(Int32))) ca._type = ColumnType.Integer;
else if (pt.Equals(typeof(UInt32))) ca._type = ColumnType.Integer;
else if (pt.Equals(typeof(Int64))) ca._type = ColumnType.Integer;
else if (pt.Equals(typeof(Single))) ca._type = ColumnType.Float;
else if (pt.Equals(typeof(Double))) ca._type = ColumnType.Float;
else if (pt.Equals(typeof(Decimal))) ca._type = ColumnType.Float;
else if (pt.Equals(typeof(DateTime))) ca._type = ColumnType.DateTime;
else if (pt.Equals(typeof(String)))
{
switch (ca.Type)
{
case ColumnType.Bytes:
case ColumnType.Integer:
case ColumnType.Float:
case ColumnType.DateTime:
//throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 的类型不受支持。"));
ca._type = ColumnType.NVarChar;
ca._length = 191;
break;
}
}
else
{
ca._type = ColumnType.NVarChar191;
ca._length = 191;
}
ca._property = property;
ca.PropertyName = property.Name;
if (ca.PropertyName == "Key" || ca.PropertyName == "Flag") ca._independent = true;
return ca;
}
/// <summary>Independent 特性。</summary>
public bool Independent
/// <summary>对列特性排序,Key 和 Flag 将始终排在前部。</summary>
public static ColumnAttribute[] Sort(ColumnAttribute[] columns, bool sort = false)
{
get => _independent;
internal set => _independent = value;
var total = columns.Length;
var key = null as ColumnAttribute;
var flag = null as ColumnAttribute;
var temp = new List<ColumnAttribute>(total);
for (var i = 0; i < total; i++)
{
var ca = columns[i];
if (ca == null) continue;
var pn = ca.Property.Name;
if (pn == "Key") key = ca;
else if (pn == "Flag") flag = ca;
else temp.Add(ca);
}
if (sort && temp.Count > 0) temp.Sort((a, b) => a._field.CompareTo(b._field));
if (key == null && flag == null) return temp.ToArray();
total = 0;
if (key != null) total += 1;
if (flag != null) total += 1;
total += temp.Count;
var sorted = new List<ColumnAttribute>(total);
if (key != null) sorted.Add(key);
if (flag != null) sorted.Add(flag);
sorted.AddRange(temp);
return sorted.ToArray();
}
}

4
Apewer/Source/ColumnType.cs

@ -26,7 +26,7 @@ namespace Apewer.Source
VarChar,
/// <summary>长可变长度的字符串(System.String),最多 255 个字符。</summary>
VarChar255,
VarChar191,
/// <summary>可变长度的字符串(System.String)。</summary>
VarCharMax,
@ -38,7 +38,7 @@ namespace Apewer.Source
NVarChar,
/// <summary>可变长度的字符串(System.String),最多 255 个字符。</summary>
NVarChar255,
NVarChar191,
/// <summary>可变长度的字符串(System.String)。</summary>
NVarCharMax,

26
Apewer/Source/Example.cs

@ -9,37 +9,23 @@ namespace Apewer.Source
public class Example
{
private static IExecute CreateExecuteError(string error)
{
var execute = new Execute();
execute.Error = error;
return execute;
}
private static IQuery CreateQueryError(string error)
{
var query = new Query();
query.Error = error;
return query;
}
/// <summary></summary>
public static IExecute InvalidExecuteConnection => CreateExecuteError("连接无效。");
public static IExecute InvalidExecuteConnection => new Execute(false, "连接无效。");
/// <summary></summary>
public static IExecute InvalidExecuteStatement => CreateExecuteError("语句无效。");
public static IExecute InvalidExecuteStatement => new Execute(false, "语句无效。");
/// <summary></summary>
public static IExecute InvalidExecuteParameters => CreateExecuteError("参数无效。");
public static IExecute InvalidExecuteParameters => new Execute(false, "参数无效。");
/// <summary></summary>
public static IQuery InvalidQueryConnection => CreateQueryError("连接无效。");
public static IQuery InvalidQueryConnection => new Query(false, "连接无效。");
/// <summary></summary>
public static IQuery InvalidQueryStatement => CreateQueryError("语句无效。");
public static IQuery InvalidQueryStatement => new Query(false, "语句无效。");
/// <summary></summary>
public static IQuery InvalidQueryParameters => CreateQueryError("参数无效。");
public static IQuery InvalidQueryParameters => new Query(false, "参数无效。");
}

78
Apewer/Source/Execute.cs

@ -5,71 +5,57 @@ namespace Apewer.Source
{
/// <summary>数据库引擎的执行结果。</summary>
public class Execute : IExecute
public class Execute : IExecute, IToJson
{
private bool _success = false;
private int _rows = 0;
private string _error = "";
private string _message = "";
private Exception _exception = null;
private int _rows = 0;
/// <summary>语句执行成功。</summary>
public bool Success
{
get { return _success; }
set { _success = value; }
}
public bool Success { get => _success; }
/// <summary>受影响的行数。</summary>
public int Rows { get => _rows; }
/// <summary>执行失败时的异常。</summary>
public Exception Exception
/// <summary>消息。</summary>
public string Message { get => _message; }
/// <summary>创建实例。</summary>
public Execute(bool success, string message)
{
get { return _exception; }
set { _exception = value; }
_success = false;
_message = message;
}
/// <summary>受影响的行数。</summary>
public int Rows
/// <summary>创建实例。</summary>
public Execute(bool success, int rows)
{
get { return _rows; }
set { _rows = value; }
_success = success;
_rows = rows;
}
/// <summary>错误信息。</summary>
public string Error
/// <summary>创建实例,Exception 为 NULL 时候成功,非 NULL 时为失败。</summary>
public Execute(Exception exception)
{
get
{
if (!string.IsNullOrEmpty(_error))
{
return _error;
}
else
{
if (_exception != null)
{
try
{
return _exception.Message;
}
catch
{
return _exception.GetType().FullName;
}
}
}
return "";
}
set { _error = value ?? ""; }
_success = exception == null;
_message = RuntimeUtility.Message(exception);
}
/// <summary>消息。</summary>
public string Message
#region IToJson
/// <summary>转换为 Json 对象。</summary>
public Json ToJson()
{
get { return _message ?? ""; }
set { _message = value ?? ""; }
var jsonObject = Json.NewObject();
jsonObject.SetProperty("success", _success);
jsonObject.SetProperty("message", _message);
jsonObject.SetProperty("rows", _rows);
return jsonObject;
}
#endregion
}
}

14
Apewer/Source/HttpRecord.cs

@ -16,7 +16,7 @@ namespace Apewer.Source
private TextSet _ts = new TextSet(true);
/// <summary>NVarChar255</summary>
[Column("_url_md5", ColumnType.NVarChar255)]
[Column("_url_md5", ColumnType.NVarChar191)]
public string UrlMd5 { get { return _ts["UrlMd5"]; } set { _ts["UrlMd5"] = value; } }
/// <summary>NText</summary>
@ -24,7 +24,7 @@ namespace Apewer.Source
public string UrlText { get { return _ts["UrlText"]; } set { _ts["UrlText"] = value; } }
/// <summary>NVarChar255</summary>
[Column("_http_code", ColumnType.NVarChar255)]
[Column("_http_code", ColumnType.NVarChar191)]
public string HttpCode { get { return _ts["HttpCode"]; } set { _ts["HttpCode"] = value; } }
/// <summary>NText</summary>
@ -36,15 +36,15 @@ namespace Apewer.Source
public string HttpException { get { return _ts["HttpException"]; } set { _ts["HttpException"] = value; } }
/// <summary>NVarChar255</summary>
[Column("_client_ip", ColumnType.NVarChar255)]
[Column("_client_ip", ColumnType.NVarChar191)]
public string ClientIp { get { return _ts["ClientIp"]; } set { _ts["ClientIp"] = value; } }
/// <summary>NVarChar255</summary>
[Column("_request_beginning", ColumnType.NVarChar255)]
[Column("_request_beginning", ColumnType.NVarChar191)]
public string RequestBeginning { get { return _ts["RequestBeginning"]; } set { _ts["RequestBeginning"] = value; } }
/// <summary>NVarChar255</summary>
[Column("_request_ending", ColumnType.NVarChar255)]
[Column("_request_ending", ColumnType.NVarChar191)]
public string RequestEnding { get { return _ts["RequestEnding"]; } set { _ts["RequestEnding"] = value; } }
/// <summary>NText</summary>
@ -60,11 +60,11 @@ namespace Apewer.Source
public string RequestText { get { return _ts["RequestText"]; } set { _ts["RequestText"] = value; } }
/// <summary>NVarChar255</summary>
[Column("_response_beginning", ColumnType.NVarChar255)]
[Column("_response_beginning", ColumnType.NVarChar191)]
public string ResponseBeginning { get { return _ts["ResponseBeginning"]; } set { _ts["ResponseBeginning"] = value; } }
/// <summary>NVarChar255</summary>
[Column("_response_ending", ColumnType.NVarChar255)]
[Column("_response_ending", ColumnType.NVarChar191)]
public string ResponseEnding { get { return _ts["ResponseEnding"]; } set { _ts["ResponseEnding"] = value; } }
/// <summary>NText</summary>

23
Apewer/Source/IDatabaseBase.cs

@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Source
{
/// <summary>数据库引擎接口。</summary>
public interface IDatabaseBase : IDisposable
{
/// <summary>获取或设置日志记录器。</summary>
Logger Logger { get; set; }
/// <summary>数据库当前在线,表示连接可用。</summary>
bool Online { get; }
/// <summary>连接数据库,若未连接则尝试连接,获取连接成功的状态。</summary>
bool Connect();
}
}

21
Apewer/Source/IDatabaseExecute.cs

@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
namespace Apewer.Source
{
/// <summary>数据库引擎接口。</summary>
public interface IDatabaseExecute
{
/// <summary>执行。</summary>
IExecute Execute(string statement);
/// <summary>执行。</summary>
IExecute Execute(string statement, IEnumerable<IDataParameter> parameters);
}
}

21
Apewer/Source/IDatabaseQuery.cs

@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
namespace Apewer.Source
{
/// <summary>数据库引擎接口。</summary>
public interface IDatabaseQuery
{
/// <summary>查询。</summary>
IQuery Query(string statement);
/// <summary>查询。</summary>
IQuery Query(string statement, IEnumerable<IDataParameter> parameters);
}
}

2
Apewer/Source/IDatabase.cs → Apewer/Source/IDbClient.cs

@ -7,6 +7,6 @@ namespace Apewer.Source
{
/// <summary>数据库引擎接口。</summary>
public interface IDatabase : IDisposable, IDatabaseBase, IDatabaseQuery, IDatabaseExecute, IDatabaseOrm { }
public interface IDbClient : IDisposable, IDbClientBase, IDbClientAdo, IDbClientOrm { }
}

72
Apewer/Source/IDbClientAdo.cs

@ -0,0 +1,72 @@
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>
bool 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

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

14
Apewer/Source/IDatabaseOrm.cs → Apewer/Source/IDbClientOrm.cs

@ -6,7 +6,7 @@ namespace Apewer.Source
{
/// <summary>数据库引擎支持 ORM 访问。</summary>
public interface IDatabaseOrm
public interface IDbClientOrm
{
/// <summary>初始化指定类型,以创建表或增加字段。</summary>
@ -31,11 +31,11 @@ namespace Apewer.Source
/// <summary>获取指定类型的主键,按 Flag 属性筛选。</summary>
/// <param name="model">要查询的类型。</param>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<List<string>> Keys(Type model, long flag = 0);
public Result<string[]> Keys(Type model, long flag = 0);
/// <summary>获取指定类型的主键,按 Flag 属性筛选。</summary>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<List<string>> Keys<T>(long flag = 0) where T : class, IRecord, new();
public Result<string[]> Keys<T>(long flag = 0) where T : class, IRecord, new();
/// <summary>获取具有指定 Key 的记录,并要求记录具有指定的 Flag 属性。</summary>
/// <param name="model">目标记录的类型。</param>
@ -51,20 +51,20 @@ namespace Apewer.Source
/// <summary>使用指定语句查询,获取查询结果。</summary>
/// <param name="model">目标记录的类型。</param>
/// <param name="sql">要执行的 SQL 语句。</param>
public Result<List<IRecord>> Query(Type model, string sql);
public Result<IRecord[]> Query(Type model, string sql);
/// <summary>使用指定语句查询,获取查询结果。</summary>
/// <param name="sql">要执行的 SQL 语句。</param>
public Result<List<T>> Query<T>(string sql) where T : class, IRecord, new();
public Result<T[]> Query<T>(string sql) where T : class, IRecord, new();
/// <summary>查询所有记录。</summary>
/// <param name="model">目标记录的类型。</param>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<List<IRecord>> Query(Type model, long flag = 0);
public Result<IRecord[]> Query(Type model, long flag = 0);
/// <summary>查询所有记录。</summary>
/// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
public Result<List<T>> Query<T>(long flag = 0) where T : class, IRecord, new();
public Result<T[]> Query<T>(long flag = 0) where T : class, IRecord, new();
}

6
Apewer/Source/IExecute.cs

@ -15,15 +15,9 @@ namespace Apewer.Source
/// <summary>受影响的行数。</summary>
int Rows { get; }
/// <summary>错误消息。</summary>
string Error { get; }
/// <summary>消息。</summary>
string Message { get; }
/// <summary>执行失败时的异常。</summary>
Exception Exception { get; }
}
}

37
Apewer/Source/IQuery.cs

@ -15,24 +15,12 @@ namespace Apewer.Source
/// <summary>语句执行成功。</summary>
bool Success { get; }
/// <summary>错误信息。</summary>
string Error { get; }
/// <summary>消息。</summary>
string Message { get; }
/// <summary>语句执行失败时的 Exception 对象。</summary>
Exception Exception { get; }
/// <summary>所有结果表。</summary>
List<DataTable> Tables { get; }
/// <summary>获取默认结果表。如果设置默认结果表,会丢失设置前的所有结果表。</summary>
DataTable Table { get; }
/// <summary>结果集为空。</summary>
bool Empty { get; }
/// <summary>默认表中的数据总行数。</summary>
int Rows { get; }
@ -66,31 +54,6 @@ namespace Apewer.Source
#endregion
#region 以文本获取结果集中的内容。
/// <summary>获取默认表中第 0 行、第 0 列的单元格内容。</summary>
string Text();
/// <summary>获取默认表中指定行中第 0 列的内容。</summary>
/// <param name="rowIndex">行索引,从 0 开始。</param>
string Text(int rowIndex);
/// <summary>获取默认表中第 0 行指定列的内容。</summary>
/// <param name="columnName">列名称。</param>
string Text(string columnName);
/// <summary>获取默认表中指定单元格的内容。</summary>
/// <param name="rowIndex">行索引,从 0 开始。</param>
/// <param name="columnIndex">列索引,从 0 开始。</param>
string Text(int rowIndex, int columnIndex);
/// <summary>获取默认表中指定单元的内容。</summary>
/// <param name="rowIndex">行索引,从 0 开始。</param>
/// <param name="columnName">列名称。</param>
string Text(int rowIndex, string columnName);
#endregion
}
}

6
Apewer/Source/IRecord.cs

@ -6,15 +6,21 @@ namespace Apewer.Source
{
/// <summary>数据库记录通用字段模型。</summary>
/// <remarks>带有 Independent 特性的模型不包含此接口声明的属性。</remarks>
public interface IRecord
{
/// <summary>记录唯一键,一般使用 GUID 的字符串形式,字段长度不应超过 255 个字符。</summary>
/// <remarks>带有 Independent 特性的模型不包含此属性。</remarks>
string Key { get; set; }
/// <summary>记录的标记,区分记录的状态。</summary>
/// <remarks>带有 Independent 特性的模型不包含此属性。</remarks>
long Flag { get; set; }
/// <summary>重置 Key 属性的值。</summary>
void ResetKey();
}
}

198
Apewer/Source/OrmHelper.cs

@ -13,15 +13,17 @@ namespace Apewer.Source
#region As
private static List<T> As<T>(List<IRecord> input) where T : IRecord
private static T[] As<T>(IRecord[] input) where T : IRecord
{
if (input == null) return null;
var output = new List<T>(input.Count);
foreach (var record in input)
var count = input.Length;
var output = new T[count];
for (var i = 0; i < count; i++)
{
var record = input[i];
if (record == null) continue;
var t = (T)record;
output.Add(t);
output[i] = t;
}
return output;
}
@ -34,17 +36,14 @@ namespace Apewer.Source
return new Result<T>(value);
}
private static Result<List<T>> As<T>(Result<List<IRecord>> input) where T : class, IRecord, new()
private static Result<T[]> As<T>(Result<IRecord[]> input) where T : class, IRecord, new()
{
if (input == null) return null;
if (!input.HasValue) return new Result<List<T>>(input.Code, input.Message);
var list = new List<T>(input.Value.Count);
foreach (var inputItem in input.Value)
{
var value = inputItem as T;
list.Add(value);
}
return new Result<List<T>>(list);
if (!input.HasValue) return new Result<T[]>(input.Code, input.Message);
var count = input.Value.Length;
var output = new T[count];
for (var i = 0; i < count; i++) output[i] = input.Value[i] as T;
return new Result<T[]>(output);
}
#endregion
@ -52,81 +51,84 @@ namespace Apewer.Source
#region IQuery -> IRecord
/// <summary>读取所有行,生成列表。</summary>
public static List<T> Fill<T>(IQuery query) where T : class, IRecord, new() => As<T>(Fill(query, typeof(T)));
public static T[] Fill<T>(IQuery query) where T : class, IRecord, new() => As<T>(Fill(query, typeof(T)));
/// <summary>读取所有行填充到 T,组成 List&lt;T&gt;。</summary>
/// <exception cref="Exception"></exception>
public static List<IRecord> Fill(IQuery query, Type model)
/// <summary>读取所有行填充到 T,组成 T[]。</summary>
public static IRecord[] Fill(IQuery query, Type model)
{
if (query == null) return new List<IRecord>();
if (model == null) return new List<IRecord>();
if (query == null) return new IRecord[0];
if (model == null) return new IRecord[0];
var list = new List<IRecord>(query.Rows);
var ts = TableStructure.ParseModel(model);
for (int r = 0; r < query.Rows; r++)
{
var record = Row(query, r, model, ts);
if (record != null) list.Add(record);
}
list.Capacity = list.Count;
return list;
var ts = TableStructure.Parse(model);
var output = new IRecord[query.Rows];
for (int r = 0; r < query.Rows; r++) output[r] = Row(query, r, model, ts);
return output;
}
/// <summary>获取指定列的所有值,无效值不加入结果。</summary>
/// <exception cref="ArgumentNullException"></exception>
public static List<T> Column<T>(IQuery query, Func<int, T> filler)
public static T[] Column<T>(IQuery query, Func<int, T> filler)
{
if (query == null) throw new ArgumentNullException(nameof(query));
if (filler == null) throw new ArgumentNullException(nameof(filler));
if (query == null || filler == null) return new T[0];
var rows = query.Rows;
var list = new List<T>(rows);
if (rows > 0)
var output = new T[rows];
var added = 0;
for (int r = 0; r < rows; r++)
{
list.Capacity = rows;
var added = 0;
for (int r = 0; r < rows; r++)
var value = filler(r);
if (value == null) continue;
if (value is string str)
{
var value = filler(r);
if (value == null) continue;
if (value is string)
{
var valueString = value as string;
if (string.IsNullOrEmpty(valueString)) continue;
}
list.Add(value);
added++;
if (str == "") continue;
}
list.Capacity = added;
output[added] = value;
added++;
}
return list;
if (added < 1) return new T[0];
if (added == rows) return output;
var output2 = new T[added];
Array.Copy(output, output2, added);
return output2;
}
/// <summary>填充指定行为记录实体。</summary>
/// <summary>将 Query 的行,填充到模型实体。</summary>
/// <remarks>填充失败时返回 NULL 值。</remarks>
/// <exception cref="Exception"></exception>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IRecord Row(IQuery query, int rowIndex, Type model, TableStructure structure)
{
if (query == null) throw new ArgumentNullException(nameof(query));
if (model == null) throw new ArgumentNullException(nameof(model));
if (structure == null) throw new ArgumentNullException(nameof(structure));
if (rowIndex < 0 || rowIndex >= query.Rows) throw new ArgumentOutOfRangeException(nameof(rowIndex));
var record = Activator.CreateInstance(model);
var properties = model.GetProperties();
// 检查参数。
if (query == null || model == null || structure == null) return null;
if (rowIndex < 0 || rowIndex >= query.Rows) return null;
if (!RuntimeUtility.CanNew(model)) return null;
// 变量别名。
var ts = structure;
var r = rowIndex;
var columns = ts.Columns;
// 检查模型的属性,按属性从表中取相应的列。
var record = Activator.CreateInstance(model);
var properties = model.GetProperties();
foreach (var property in properties)
{
if (ts.Columns.ContainsKey(property.Name) == false) continue;
// 必须有 setter 访问器。
var setter = property.GetSetMethod();
if (setter == null) continue;
var attribute = ts.Columns[property.Name];
var pt = property.PropertyType;
// 在表结构中检查,是否包含此属性,并获取 ColumnAttribute。
var attribute = null as ColumnAttribute;
for (var j = 0; j < columns.Length; j++)
{
if (columns[j].PropertyName == property.Name)
{
attribute = columns[j];
break;
}
}
if (attribute == null) continue;
// 根据属性类型设置值。
var pt = property.PropertyType;
if (pt.Equals(typeof(object)) || pt.Equals(typeof(Nullable<DateTime>)))
{
setter.Invoke(record, new object[] { query.Value(r, attribute.Field) });
@ -197,8 +199,7 @@ namespace Apewer.Source
catch { }
}
}
var iRecord = record as IRecord;
return iRecord;
return record as IRecord;
}
#endregion
@ -209,23 +210,27 @@ namespace Apewer.Source
/// <param name="database">数据库对象。</param>
/// <param name="model">记录模型。</param>
/// <param name="sql">SQL 语句。</param>
public static Result<List<IRecord>> Query(IDatabaseQuery database, Type model, string sql)
public static Result<IRecord[]> Query(IDbClientAdo database, Type model, string sql)
{
if (database == null) return new Result<List<IRecord>>("数据库无效。");
if (model == null) return new Result<List<IRecord>>("模型类型无效。");
if (string.IsNullOrEmpty(sql)) return new Result<List<IRecord>>("SQL 语句无效。");
if (database == null) return new Result<IRecord[]>("数据库无效。");
if (model == null) return new Result<IRecord[]>("模型类型无效。");
if (string.IsNullOrEmpty(sql)) return new Result<IRecord[]>("SQL 语句无效。");
using (var query = database.Query(sql) as Query)
{
if (query == null) return new Result<List<IRecord>>("查询实例无效。");
if (query.Exception != null) return new Result<List<IRecord>>(query.Exception);
if (query == null) return new Result<IRecord[]>("查询实例无效。");
if (query.Table == null)
{
if (!string.IsNullOrEmpty(query.Message)) return new Result<IRecord[]>(query.Message);
return new Result<IRecord[]>("查询实例不包含数据表。");
}
try
{
var list = Fill(query, model);
return new Result<List<IRecord>>(list);
var array = Fill(query, model);
return new Result<IRecord[]>(array);
}
catch (Exception ex)
{
return new Result<List<IRecord>>(ex);
return new Result<IRecord[]>(ex);
}
}
}
@ -234,24 +239,24 @@ namespace Apewer.Source
/// <typeparam name="T">记录模型。</typeparam>
/// <param name="database">数据库对象。</param>
/// <param name="sql">SQL 语句。</param>
public static Result<List<T>> Query<T>(IDatabaseQuery database, string sql) where T : class, IRecord, new() => As<T>(Query(database, typeof(T), sql));
public static Result<T[]> Query<T>(IDbClientAdo database, string sql) where T : class, IRecord, new() => As<T>(Query(database, typeof(T), sql));
/// <summary>查询记录。</summary>
/// <param name="database">数据库对象。</param>
/// <param name="model">记录模型。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
public static Result<List<IRecord>> Query(IDatabaseQuery database, Type model, Func<string, string> sqlGetter)
public static Result<IRecord[]> Query(IDbClientAdo database, Type model, Func<string, string> sqlGetter)
{
if (sqlGetter == null) return new Result<List<IRecord>>("SQL 语句获取函数无效。");
if (sqlGetter == null) return new Result<IRecord[]>("SQL 语句获取函数无效。");
try
{
var tableName = TableStructure.ParseModel(model).Table;
if (string.IsNullOrEmpty(tableName)) return new Result<List<IRecord>>("表名无效。");
var tableName = TableStructure.Parse(model).Name;
if (string.IsNullOrEmpty(tableName)) return new Result<IRecord[]>("表名无效。");
return Query(database, model, sqlGetter(tableName));
}
catch (Exception ex)
{
return new Result<List<IRecord>>(ex);
return new Result<IRecord[]>(ex);
}
}
@ -259,14 +264,14 @@ namespace Apewer.Source
/// <typeparam name="T">记录模型。</typeparam>
/// <param name="database">数据库对象。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
public static Result<List<T>> Query<T>(IDatabaseQuery database, Func<string, string> sqlGetter) where T : class, IRecord, new() => As<T>(Query(database, typeof(T), sqlGetter));
public static Result<T[]> Query<T>(IDbClientAdo database, Func<string, string> sqlGetter) where T : class, IRecord, new() => As<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<IRecord> Get(IDatabaseQuery database, Type model, string key, Func<string, string, string> sqlGetter)
public static Result<IRecord> Get(IDbClientAdo database, Type model, string key, Func<string, string, string> sqlGetter)
{
if (sqlGetter == null) return new Result<IRecord>("SQL 语句获取函数无效。");
@ -277,8 +282,8 @@ namespace Apewer.Source
var record = null as IRecord;
try
{
var ts = TableStructure.ParseModel(model);
var tableName = ts.Table;
var ts = TableStructure.Parse(model);
var tableName = ts.Name;
if (string.IsNullOrEmpty(tableName)) return new Result<IRecord>("表名无效。");
var sql = sqlGetter(tableName, safetyKey);
@ -301,30 +306,30 @@ namespace Apewer.Source
/// <param name="database">数据库对象。</param>
/// <param name="key">主键。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名和主键值。</param>
public static Result<T> Get<T>(IDatabaseQuery database, string key, Func<string, string, string> sqlGetter) where T : class, IRecord, new() => As<T>(Get(database, typeof(T), key, sqlGetter));
public static Result<T> Get<T>(IDbClientAdo database, string key, Func<string, string, string> sqlGetter) where T : class, IRecord, new() => As<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<List<string>> Keys(IDatabaseQuery database, Type model, Func<string, string> sqlGetter)
public static Result<string[]> Keys(IDbClientAdo database, Type model, Func<string, string> sqlGetter)
{
if (database == null) return new Result<List<string>>("数据库无效。");
if (model == null) return new Result<List<string>>("模型类型无效。");
if (sqlGetter == null) return new Result<List<string>>("SQL 语句获取函数无效。");
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.ParseModel(model);
tableStructure = TableStructure.Parse(model);
}
catch (Exception ex)
{
return new Result<List<string>>(ex);
return new Result<string[]>(ex);
}
var tableName = tableStructure.Table;
if (string.IsNullOrEmpty(tableName)) return new Result<List<string>>("表名无效。");
var tableName = tableStructure.Name;
if (string.IsNullOrEmpty(tableName)) return new Result<string[]>("表名无效。");
// var keyName = null as string;
// foreach (var column in tableStructure.Columns)
@ -343,7 +348,7 @@ namespace Apewer.Source
try
{
query = database.Query(sql);
if (query == null) return new Result<List<string>>("查询实例无效。");
if (query == null) return new Result<string[]>("查询实例无效。");
var list = new List<string>(query.Rows);
for (var r = 0; r < query.Rows; r++)
@ -354,12 +359,13 @@ namespace Apewer.Source
}
query.Dispose();
list.Capacity = list.Count;
return new Result<List<string>>(list);
var array = list.ToArray();
return new Result<string[]>(array);
}
catch (Exception ex)
{
RuntimeUtility.Dispose(query);
return new Result<List<string>>(ex);
return new Result<string[]>(ex);
}
}
@ -367,7 +373,7 @@ namespace Apewer.Source
/// <typeparam name="T">记录模型。</typeparam>
/// <param name="database">数据库对象。</param>
/// <param name="sqlGetter">生成 SQL 语句的函数,传入参数为表名。</param>
public static Result<List<string>> Keys<T>(IDatabaseQuery database, Func<string, string> sqlGetter) where T : IRecord
public static Result<string[]> Keys<T>(IDbClientAdo database, Func<string, string> sqlGetter) where T : IRecord
{
return Keys(database, typeof(T), sqlGetter);
}

20
Apewer/Source/Parameter.cs

@ -10,24 +10,8 @@ namespace Apewer.Source
public class Parameter
{
private string _name;
/// <summary>名称,不可设置位为空。</summary>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentNullException"></exception>
public string Name
{
get
{
return _name;
}
set
{
if (value == null) throw new ArgumentNullException();
if (value == "") throw new ArgumentException();
_name = value;
}
}
public string Name { get; set; }
/// <summary>值。</summary>
public object Value { get; set; }
@ -35,7 +19,7 @@ namespace Apewer.Source
/// <summary>类型。</summary>
public ColumnType Type { get; set; }
/// <summary>类型为 VarChar 时,指定长度。</summary>
/// <summary>类型为 VarChar 时,指定长度。</summary>
public int Size { get; set; }
/// <summary>创建用于执行 SQL 语句的参数,名称不可设置位为空。</summary>

418
Apewer/Source/Query.cs

@ -1,4 +1,5 @@
using Apewer.Internals;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Data;
@ -8,212 +9,74 @@ using static Apewer.TextUtility;
namespace Apewer.Source
{
/// <summary>查询数据表。</summary>
public class Query : IQuery, IDisposable
/// <summary>System.Data.DataTable 装箱查询。</summary>
public class Query : IQuery, IDisposable, IToJson
{
private bool _disposed = false;
private bool _success = false;
private string _error = "";
private string _message = "";
private Exception _exception = null;
private List<DataTable> _tables = new List<DataTable>();
private string _message = null;
private DataTable _table = null;
private DataTable[] _tables = null;
#region Property
/// <summary>语句执行成功。</summary>
public bool Success
/// <summary>创建实例,默认状态为失败。</summary>
public Query(bool success = false, string message = null)
{
get
{
if (_disposed) return false;
return _success;
}
set
{
if (_disposed) return;
_success = value;
}
}
/// <summary>错误信息。</summary>
public string Error
{
get
{
if (_disposed) return "";
if (!string.IsNullOrEmpty(_error)) return _error;
if (_exception == null) return "";
var error = "";
try { error = _exception.Message; } finally { }
return error;
}
set
{
if (_disposed) return;
_error = value ?? "";
}
}
/// <summary>消息。</summary>
public string Message
{
get
{
if (_disposed) return "";
return _message ?? "";
}
set
{
if (_disposed) return;
_message = value ?? "";
}
_success = false;
_message = message;
}
/// <summary>语句执行失败时的 Exception 对象。</summary>
public Exception Exception
/// <summary>创建实例,Exception 为 NULL 时成功,非 NULL 时失败。</summary>
public Query(Exception exception)
{
get { if (_disposed) return null; return _exception; }
set { if (_disposed) return; _exception = value; }
_success = exception == null;
_message = RuntimeUtility.Message(exception);
}
/// <summary>所有结果表。</summary>
public List<DataTable> Tables
/// <summary>创建实例,包装一个 DataTable 对象,数据表为 NULL 时失败,非 NULL 时成功。</summary>
public Query(DataTable table)
{
get { if (_disposed) return new List<DataTable>(); return _tables; }
_table = table;
_success = table != null;
_message = table == null ? "未获取有效的数据表。" : null;
}
/// <summary>获取默认结果表。如果设置默认结果表,会丢失设置前的所有结果表。</summary>
public DataTable Table
/// <summary>创建实例,包装一个 DataTable 对象。</summary>
public Query(DataTable table, bool success, string message = null)
{
get
{
if (_disposed) return null;
if (_tables.Count < 1) return null;
return _tables[0];
}
set
{
if (_disposed) return;
Clear();
if (_disposed) return;
_tables.Add(value);
}
_table = table;
_success = success;
_message = message;
}
/// <summary>所有表中不含内容行。</summary>
public bool Empty
/// <summary>创建实例,包装多个 DataTable 对象。</summary>
public Query(DataTable[] tables, bool success = true, string message = null)
{
get
{
if (_disposed) return true;
if (_tables.Count < 1) return true;
foreach (var table in _tables)
{
if (table == null) continue;
try
{
if (table.Rows.Count > 0) return false;
}
finally { }
}
return true;
}
_tables = tables;
_success = success;
_message = message;
if (tables != null && tables.Length > 0) _table = tables[0];
}
/// <summary>默认表中的数据总行数。</summary>
public int Rows
{
get
{
if (_disposed) return 0;
if (Table != null) return Table.Rows.Count;
else return 0;
}
}
/// <summary>默认表中的数据总列数。</summary>
public int Columns
{
get
{
if (_disposed) return 0;
if (Table != null) return Table.Columns.Count;
else return 0;
}
}
#endregion
#region Text
/// <summary>获取默认表中第 0 行、第 0 列的单元格内容。</summary>
public string Text()
{
if (_disposed) return Constant.EmptyString;
var value = Value();
return Text(value);
}
#region Property
/// <summary>获取默认表中指定行中第 0 列的内容。</summary>
/// <param name="rowIndex">行索引,从 0 开始。</param>
public string Text(int rowIndex)
{
if (_disposed) return Constant.EmptyString;
var value = Value(rowIndex);
return Text(value);
}
/// <summary>语句执行成功。</summary>
public bool Success { get => _success; }
/// <summary>获取默认表中第 0 行指定列的内容。</summary>
/// <param name="columnName">列名称。</param>
public string Text(string columnName)
{
if (_disposed) return Constant.EmptyString;
var value = Value(columnName);
return Text(value);
}
/// <summary>消息。</summary>
public string Message { get => _message; }
/// <summary>获取默认表中指定单元格的内容。</summary>
/// <param name="rowIndex">行索引,从 0 开始。</param>
/// <param name="columnIndex">列索引,从 0 开始。</param>
public string Text(int rowIndex, int columnIndex)
{
if (_disposed) return Constant.EmptyString;
var value = Value(rowIndex, columnIndex);
return Text(value);
}
/// <summary>所有结果表。</summary>
public DataTable[] Tables { get => _tables; }
/// <summary>获取默认表中指定单元的内容。</summary>
/// <param name="rowIndex">行索引,从 0 开始。</param>
/// <param name="columnName">列名称。</param>
public string Text(int rowIndex, string columnName)
{
if (_disposed) return Constant.EmptyString;
var value = Value(rowIndex, columnName);
return Text(value);
}
/// <summary>获取默认结果表。如果设置默认结果表,会丢失设置前的所有结果表。</summary>
public DataTable Table { get => _table; }
/// <summary>搜索默认表。</summary>
/// <param name="conditionColumn">搜索条件:列名。</param>
/// <param name="conditionValue">搜索条件:列值。</param>
/// <param name="resultColumn">搜索结果。</param>
public string Text(string conditionColumn, string conditionValue, string resultColumn)
{
if (_disposed) return Constant.EmptyString;
var value = Value(conditionColumn, conditionValue, resultColumn);
return Text(value);
}
/// <summary>默认表中的数据总行数。</summary>
public int Rows { get => _table == null ? 0 : _table.Rows.Count; }
/// <summary>搜索默认表。</summary>
/// <param name="conditionColumn">搜索条件:列名。</param>
/// <param name="conditionValue">搜索条件:列值。</param>
/// <param name="resultColumn">搜索结果。</param>
public string Text(int conditionColumn, string conditionValue, int resultColumn)
{
if (_disposed) return Constant.EmptyString;
var value = Value(conditionColumn, conditionValue, resultColumn);
return Text(value);
}
/// <summary>默认表中的数据总列数。</summary>
public int Columns { get => _table == null ? 0 : _table.Columns.Count; }
#endregion
@ -248,13 +111,13 @@ namespace Apewer.Source
public object Value(int rowIndex, int columnIndex)
{
if (_disposed) return null;
if (Table != null)
if (_table != null)
{
if (rowIndex >= 0 && rowIndex < Table.Rows.Count)
if (rowIndex >= 0 && rowIndex < _table.Rows.Count)
{
if (columnIndex >= 0 && columnIndex < Table.Columns.Count)
if (columnIndex >= 0 && columnIndex < _table.Columns.Count)
{
return Table.Rows[rowIndex][columnIndex];
return _table.Rows[rowIndex][columnIndex];
}
}
}
@ -302,7 +165,7 @@ namespace Apewer.Source
/// <summary>搜索默认表。</summary>
/// <param name="conditionColumn">搜索条件:列名。</param>
/// <param name="conditionValue">搜索条件:列值。</param>
/// <param name="resultColumn">搜索结果。</param>
/// <param name="resultColumn">搜索结果的列名。</param>
public object Value(int conditionColumn, string conditionValue, int resultColumn)
{
if (_disposed) return null;
@ -327,68 +190,28 @@ namespace Apewer.Source
#region Method
/// <summary>拆分表组,单独查询。</summary>
public List<Query> Split()
{
var list = new List<Query>();
if (_disposed) return list;
foreach (var table in _tables)
{
if (table == null) continue;
var query = new Query();
query._success = true;
query._tables.Add(table);
list.Add(query);
}
return list;
}
/// <summary>添加数据表。</summary>
public bool Add(DataTable tables)
{
if (_disposed) return false;
if (tables == null) return false;
_tables.Add(tables);
return true;
}
/// <summary>添加数据表。</summary>
public int Add(IEnumerable<DataTable> tables)
/// <summary>搜索默认表。</summary>
/// <param name="conditionColumn">搜索条件:列名。</param>
/// <param name="conditionValue">搜索条件:列值。</param>
/// <param name="resultColumn">搜索结果。</param>
public string Text(int conditionColumn, string conditionValue, int resultColumn)
{
var count = 0;
if (_disposed) return count;
if (tables == null) return count;
foreach (var table in tables)
{
if (table == null) continue;
_tables.Add(table);
count = count + 1;
}
return count;
var value = Value(conditionColumn, conditionValue, resultColumn);
return Text(value);
}
/// <summary>清除所有表,并释放系统资源。</summary>
public virtual void Clear()
/// <summary>释放系统资源。</summary>
public virtual void Dispose()
{
if (_disposed) return;
if (_tables != null)
{
foreach (var table in _tables)
{
if (table != null)
{
try { table.Dispose(); } catch { }
}
}
_tables.Clear();
foreach (var table in _tables) RuntimeUtility.Dispose(table);
_tables = null;
}
if (_exception != null) _exception = null;
_success = false;
}
/// <summary>释放系统资源。</summary>
public virtual void Dispose()
{
Clear();
RuntimeUtility.Dispose(_table);
_table = null;
_tables = null;
_disposed = true;
// GC.SuppressFinalize(this);
}
@ -402,40 +225,79 @@ namespace Apewer.Source
}
/// <summary>获取指定列的所有值,无效值不加入结果。</summary>
public List<T> ReadColumn<T>(int column = 0, Func<object, T> formatter = null) => OrmHelper.Column(this, (r) => (formatter ?? GetValueFormatter<T>()).Invoke(Value(r, column)));
public T[] ReadColumn<T>(int column = 0, Func<object, T> formatter = null) => OrmHelper.Column(this, (r) => (formatter ?? GetValueFormatter<T>()).Invoke(Value(r, column)));
/// <summary>获取指定列的所有值,无效值不加入结果。</summary>
/// <exception cref="ArgumentNullException"></exception>
public List<T> ReadColumn<T>(string column, Func<object, T> formatter = null) => OrmHelper.Column(this, (r) => (formatter ?? GetValueFormatter<T>()).Invoke(Value(r, column)));
public T[] ReadColumn<T>(string column, Func<object, T> formatter = null) => OrmHelper.Column(this, (r) => (formatter ?? GetValueFormatter<T>()).Invoke(Value(r, column)));
/// <summary>获取指定列的所有值,无效值不加入结果。</summary>
public List<string> ReadColumn(int column = 0) => OrmHelper.Column(this, (r) => Text(r, column));
public string[] ReadColumn(int column = 0) => OrmHelper.Column(this, (r) => this.Text(r, column));
/// <summary>获取指定列的所有值,无效值不加入结果。</summary>
/// <exception cref="ArgumentNullException"></exception>
public List<string> ReadColumn(string column) => OrmHelper.Column(this, (r) => Text(r, column));
public string[] ReadColumn(string column) => OrmHelper.Column(this, (r) => this.Text(r, column));
#endregion
#region Static
#region IToJson
private static string Text(object value)
/// <summary>转换为 Json 对象。</summary>
public Json ToJson()
{
var result = Constant.EmptyString;
if (value != null)
var columns = Json.NewArray();
var rows = Json.NewArray();
var table = _table;
if (!_disposed && table != null)
{
if (!value.Equals(DBNull.Value))
var columnsCount = _table.Columns.Count;
for (var c = 0; c < columnsCount; c++)
{
try
var dc = table.Columns[c];
var column = Json.NewObject();
column.SetProperty("name", dc.ColumnName);
column.SetProperty("type", dc.DataType.FullName);
columns.AddItem(column);
}
var rowsCount = table.Rows.Count;
for (var r = 0; r < _table.Rows.Count; r++)
{
var row = Json.NewArray();
for (var c = 0; c < columnsCount; c++)
{
result = value.ToString();
var v = Value(r, c);
if (v == null) row.AddItem();
else if (v.Equals(DBNull.Value)) row.AddItem();
else if (v is byte vByte) row.AddItem(vByte);
else if (v is short vInt16) row.AddItem(vInt16);
else if (v is int vInt32) row.AddItem(vInt32);
else if (v is long vInt64) row.AddItem(vInt64);
else if (v is float vSingle) row.AddItem(vSingle);
else if (v is double vDouble) row.AddItem(vDouble);
else if (v is decimal vDecimal) row.AddItem(vDecimal);
else if (v is bool vBoolean) row.AddItem(vBoolean);
else if (v is byte[] vBytes) row.AddItem(vBytes.Base64());
else if (v is DateTime vDateTime) row.AddItem(vDateTime.Lucid());
else row.AddItem(v.ToString());
}
finally { }
rows.AddItem(row);
}
}
return result;
var jsonObject = Json.NewObject();
jsonObject.SetProperty("success", _success);
jsonObject.SetProperty("message", _message);
jsonObject.SetProperty("columns", columns);
jsonObject.SetProperty("rows", rows);
return jsonObject;
}
#endregion
#region Static
private static T ForceFormatter<T>(object input) => (T)input;
private static T TextFormatter<T>(object input) => (T)(Text(input) as object);
@ -443,56 +305,68 @@ namespace Apewer.Source
private static ObjectDisposedException DisposedException { get { return new ObjectDisposedException(typeof(Query).FullName); } }
/// <summary>简单查询:取结果中第 0 列所有单元格的文本形式,可指定查询后关闭服务器连接,返回结果中不包含无效文本。</summary>
public static List<string> SimpleColumn(IDatabaseQuery database, string statement, bool dispose = false)
public static string[] SimpleColumn(IDbClientAdo database, string statement, bool dispose = false)
{
var list = new List<string>();
if (database == null) return list;
if (database == null) return new string[0];
var ab = new ArrayBuilder<string>();
using (var query = database.Query(statement))
{
var rows = query.Rows;
if (rows > 0)
{
list.Capacity = query.Rows;
var added = 0;
for (int i = 0; i < rows; i++)
{
var cell = Trim(query.Text(i));
if (string.IsNullOrEmpty(cell)) continue;
list.Add(cell);
ab.Add(cell);
added++;
}
list.Capacity = added;
}
}
if (dispose) RuntimeUtility.Dispose(database);
return list;
return ab.Export();
}
/// <summary>简单查询:取结果中第 0 行、第 0 列单元格中的文本,可指定查询后关闭服务器连接。</summary>
public static string SimpleCell(IDatabaseQuery database, string statement, bool dispose = false)
public static string SimpleCell(IDbClientAdo database, string statement, bool dispose = false)
{
if (database == null) return "";
var vquery = database.Query(statement);
var vcell = Trim(vquery.Text());
vquery.Dispose();
var query = database.Query(statement);
var cell = Trim(Query.Text(query.Value()));
query.Dispose();
if (dispose) RuntimeUtility.Dispose(database);
return vcell;
return cell;
}
#endregion
#region Extension
/// <summary></summary>
internal static DateTime DateTime(IQuery query, int row, string column)
internal static string Text(object value)
{
var result = Constant.EmptyString;
if (value != null)
{
if (!value.Equals(DBNull.Value))
{
try
{
result = value.ToString();
}
finally { }
}
}
return result;
}
internal static Class<DateTime> DateTime(object value)
{
if (query == null) return ClockUtility.Origin;
var value = query.Value(row, column);
if (value == null) return ClockUtility.Origin;
if (value is DateTime) return (DateTime)value;
if (value == null) return null;
if (value is DateTime) return null;
DateTime result;
var parsed = System.DateTime.TryParse(value.ToString(), out result);
return parsed ? result : ClockUtility.Origin;
return parsed ? new Class<DateTime>(result) : null;
}
#endregion

20
Apewer/Source/Record.cs

@ -9,34 +9,34 @@ namespace Apewer.Source
{
/// <summary>数据库记录通用字段模型。</summary>
/// <remarks>带有 Independent 特性的模型不包含此类型声明的属性。</remarks>
[Serializable]
public class Record : IRecord
public abstract class Record : IRecord
{
const int KeyLength = 64;
const int KeyLength = 191;
private string _key = null;
private long _flag = 0;
/// <summary>记录唯一键,一般使用 GUID 的字符串形式。</summary>
/// <summary>记录主键,一般使用 GUID 的字符串形式。</summary>
/// <remarks>带有 Independent 特性的模型不包含此属性。</remarks>
[Column("_key", ColumnType.NVarChar, KeyLength)]
public virtual string Key { get { return _key; } set { _key = Compact(value, KeyLength); } }
public string Key { get { return _key; } set { _key = Compact(value, KeyLength); } }
/// <summary>记录的标记,Int64 类型,区分记录的状态。</summary>
/// <remarks>带有 Independent 特性的模型不包含此属性。</remarks>
[Column("_flag", ColumnType.Integer)]
public virtual long Flag { get { return _flag; } set { _flag = value; } }
public long Flag { get { return _flag; } set { _flag = value; } }
/// <summary>重置 Key 属性的值。</summary>
public virtual void ResetKey() => _key = GenerateKey();
public virtual void ResetKey() => _key = TextUtility.Key();
/// <summary></summary>
public Record() => ResetKey();
#region static
/// <summary>生成新主键。</summary>
public static string GenerateKey() => Guid.NewGuid().ToString().ToLower().Replace("-", "");
internal static void FixProperties(IRecord record)
{
if (record == null) return;
@ -120,7 +120,7 @@ namespace Apewer.Source
#region 运算符。
/// <summary>从 Record 到 Boolean 的隐式转换,判断 Record 对象不为 NULL。</summary>
public static implicit operator bool(Record instance) => instance!=null;
public static implicit operator bool(Record instance) => instance != null;
#endregion
}

81
Apewer/Source/TableAttribute.cs

@ -7,45 +7,80 @@ namespace Apewer.Source
{
/// <summary>数据库中的表。</summary>
/// <remarks>
/// <para>Name: 数据库的表名。</para>
/// <para>Store: 数据存储区名称。</para>
/// </remarks>
[Serializable]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)]
public sealed class TableAttribute : Attribute
{
private string _name;
private bool _allprops = false;
private bool _independent = false;
private string _store;
/// <summary></summary>
public TableAttribute(string name = null, bool allProperties = false)
/// <summary>标记表属性。</summary>
public TableAttribute(string name = null, string store = null)
{
_name = TableStructure.RestrictName(name, string.IsNullOrEmpty(name));
_allprops = allProperties;
_store = string.IsNullOrEmpty(store) ? null : TableStructure.RestrictName(store, false);
}
/// <summary>表名。</summary>
public string Name
{
get => _name;
set => _name = TableStructure.RestrictName(value, false);
}
public string Name { get => _name; }
/// <summary></summary>
public bool Independent
{
get => _independent;
internal set => _independent = value;
}
/// <summary>存储名。</summary>
public string Store { get => _store; }
/// <summary>表的说明信息。(需要数据库客户端支持)</summary>
public string Description { get; set; }
/// <summary>独立结构,不依赖 Record 公共属性。</summary>
internal bool Independent { get; set; }
/// <summary>使用模型的所有属性,对缺少 Column 特性的属性使用默认参数的 Column 特性。</summary>
public bool AllProperties { get; set; }
/// <summary>使用所有属性,即使属性不带有 Column 特性。</summary>
public bool AllProperties
private static Dictionary<string, TableAttribute> _tac = new Dictionary<string, TableAttribute>();
/// <summary>解析表特性,默认使用缓存以提升性能。</summary>
public static TableAttribute Parse<T>(bool useCache = true) where T : IRecord => Parse(typeof(T), useCache);
/// <summary>解析表特性,默认使用缓存以提升性能。</summary>
public static TableAttribute Parse(Type type, bool useCache = true)
{
get => _allprops;
set => _allprops = value;
}
var cacheKey = type.FullName;
if (useCache)
{
var hint = null as TableAttribute;
lock (_tac)
{
if (_tac.ContainsKey(cacheKey))
{
hint = _tac[cacheKey];
}
}
if (hint != null) return hint;
}
// throw new Exception($"类型 {type.FullName} 不包含 {typeof(TableAttribute).FullName}。");
var tas = type.GetCustomAttributes(typeof(TableAttribute), false);
if (tas.LongLength < 1L) return null;
/// <summary></summary>
public override int GetHashCode() => _name.GetHashCode();
var ta = (TableAttribute)tas[0];
if (string.IsNullOrEmpty(ta.Name)) ta._name = "_" + type.Name;
ta.Independent = RuntimeUtility.Contains<IndependentAttribute>(type, true);
if (useCache)
{
lock (_tac)
{
if (!_tac.ContainsKey(cacheKey)) _tac.Add(cacheKey, ta);
}
}
return ta;
}
}

408
Apewer/Source/TableStructure.cs

@ -9,290 +9,136 @@ using System.Text;
namespace Apewer.Source
{
/// <summary></summary>
/// <summary>表结构。</summary>
[Serializable]
public sealed class TableStructure
{
private string _tablename = Constant.EmptyString;
private bool _independent = false;
#region Instance
private Dictionary<string, ColumnAttribute> _columns = new Dictionary<string, ColumnAttribute>();
TableAttribute _attribute = null;
bool _independent = false;
string _name = null;
string _description = null;
bool _allprops = false;
ColumnAttribute _key = null;
ColumnAttribute _flag = null;
ColumnAttribute[] _columns = null;
Type _model = null;
internal TableStructure() { }
private TableStructure() { }
/// <summary>不依赖 Record 公共属性。</summary>
public bool Independent
{
get => _independent;
private set => _independent = value;
}
public bool Independent { get => _independent; }
/// <summary>表名称。</summary>
public string Table
{
get => _tablename;
private set => _tablename = value ?? "";
}
public string Name { get => _name; }
/// <summary>列信息。</summary>
public Dictionary<string, ColumnAttribute> Columns
{
get => _columns;
private set => _columns = value;
}
/// <summary>表的说明信息。</summary>
public string Description { get => _description; }
#region cache
/// <summary>使用模型的所有属性,自动对属性添加缺少的 Column 特性。</summary>
public bool AllProperties { get => _allprops; }
private static Dictionary<string, TableStructure> _tsc = new Dictionary<string, TableStructure>();
/// <summary>此结构的特性。</summary>
public TableAttribute Attribute { get => _attribute; }
/// <summary>使用此结构的记录模型。</summary>
public Type Model { get => _model; }
private static Dictionary<string, TableAttribute> _tac = new Dictionary<string, TableAttribute>();
/// <summary>主键。</summary>
public ColumnAttribute Key { get => _key; }
/// <summary>列信息。</summary>
public ColumnAttribute[] Columns { get => _columns; }
#endregion
#region static
#region TableStructure
/// <summary></summary>
/// <exception cref="System.Exception"></exception>
/// <exception cref="System.ArgumentNullException"></exception>
public static TableStructure ParseModel(object entity, bool useCache = true)
{
if (entity == null) throw new ArgumentNullException("参数无效");
return ParseModel(entity.GetType(), useCache);
}
private static Dictionary<string, TableStructure> _tsc = new Dictionary<string, TableStructure>();
/// <summary></summary>
/// <exception cref="System.Exception"></exception>
/// <exception cref="System.ArgumentNullException"></exception>
public static TableStructure ParseModel<T>(bool useCache = true) where T : IRecord => ParseModel(typeof(T), useCache);
/// <summary>解析表结构。</summary>
public static TableStructure Parse<T>(bool useCache = true) where T : IRecord => Parse(typeof(T), useCache);
/// <summary></summary>
/// <exception cref="System.Exception"></exception>
/// <exception cref="System.ArgumentNullException"></exception>
public static TableStructure ParseModel(Type model, bool useCache = true)
/// <summary>解析表结构。</summary>
public static TableStructure Parse(Type model, bool useCache = true)
{
var type = model;
if (type == null) throw new ArgumentNullException("参数无效");
if (type == null || !type.IsClass || type.IsAbstract) return null;
// 使用缓存。
var cacheKey = type.FullName;
if (useCache)
{
var hint = null as TableStructure;
lock (_tsc)
{
if (_tsc.ContainsKey(cacheKey))
{
hint = _tsc[cacheKey];
}
TableStructure cached;
if (_tsc.TryGetValue(cacheKey, out cached)) return cached;
}
if (hint != null) return hint;
}
// 检查基类
// if (type.BaseType.FullName.Equals(typeof(DatabaseRecord).FullName) == false) return "基类不受支持。";
// 获取 Table Attribute
var ta = TableAttribute.Parse(type);
// 检查 Attribute。
var ta = ParseTable(type);
// 获取所有属性。
// 遍历所有属性。
var properties = type.GetProperties();
if (properties.LongLength < 1L) throw new Exception(TextUtility.Merge("类 ", type.FullName, " 不包含属性。"));
// Record 根类属性名。
var roots = GetRootProperties();
// 检查字段定义。键:属性名称。
var columns = new Dictionary<string, ColumnAttribute>();
foreach (var property in properties)
{
var ca = ParseColumn(type, property, ta);
if (ca == null) continue;
// 检查冗余。
foreach (var column in columns)
var key = null as ColumnAttribute;
var flag = null as ColumnAttribute;
var columns = new ColumnAttribute[properties.Length];
var columnsCount = 0;
if (properties.Length > 0)
{
var addedFields = new List<string>(properties.Length);
foreach (var property in properties)
{
if (column.Value.Field == ca.Field)
{
throw new Exception(TextUtility.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 的列名称存在冗余。"));
}
// 解析 ColumnAttribute,抛弃无效。
var ca = ColumnAttribute.Parse(type, property, ta);
if (ca == null) continue;
// 检查 field 重复,只保留第一个。
var field = ca.Field;
if (addedFields.Contains(field)) continue;
addedFields.Add(field);
if (property.Name == "Key") key = ca;
if (property.Name == "Flag") flag = ca;
columns[columnsCount] = ca;
columnsCount += 1;
}
// 检查基类。
if (roots.Contains(ca.Property.Name)) ca.Independent = true;
columns.Add(property.Name, ca);
}
// if (columns.Count < 1) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 不包含可用的列。"));
if (columnsCount > 0 && columnsCount != columns.Length) columns = columns.Slice(0, columnsCount);
// 排序。
columns = SortColumns(columns);
// 排序,将 Key 和 Flag 排在最前。
columns = ColumnAttribute.Sort(columns);
// 返回结果。
var ts = new TableStructure();
ts.Table = ta.Name;
ts.Independent = ta.Independent;
ts.Columns = columns;
ts._attribute = ta;
ts._key = key;
ts._flag = flag;
ts._name = ta.Name;
ts._description = ta.Description;
ts._allprops = ta.AllProperties;
ts._independent = ta.Independent;
ts._columns = columns;
ts._model = model;
// 加入缓存。
if (useCache)
{
lock (_tsc)
{
if (!_tsc.ContainsKey(cacheKey))
{
_tsc.Add(cacheKey, ts);
}
if (!_tsc.ContainsKey(cacheKey)) _tsc.Add(cacheKey, ts);
}
}
return ts;
}
/// <summary></summary>
/// <exception cref="Exception"></exception>"
public static TableAttribute ParseTable<T>(bool useCache = true) where T : IRecord => ParseTable(typeof(T), useCache);
/// <summary></summary>
/// <exception cref="Exception"></exception>"
public static TableAttribute ParseTable(Type type, bool useCache = true)
{
// 使用缓存。
var cacheKey = type.FullName;
if (useCache)
{
var hint = null as TableAttribute;
lock (_tac)
{
if (_tac.ContainsKey(cacheKey))
{
hint = _tac[cacheKey];
}
}
if (hint != null) return hint;
}
var tas = type.GetCustomAttributes(typeof(TableAttribute), false);
if (tas.LongLength < 1L) throw new Exception(TextUtility.Merge("类 ", type.FullName, " 不包含 ", typeof(TableAttribute).FullName, "。"));
if (tas.LongLength > 1L) throw new Exception(TextUtility.Merge("类 ", type.FullName, " 包含多个 ", typeof(TableAttribute).FullName, "。"));
var ta = (TableAttribute)tas[0];
if (TextUtility.IsBlank(ta.Name))
{
ta = new TableAttribute("_" + type.Name);
if (TextUtility.IsBlank(ta.Name)) throw new Exception(TextUtility.Merge("类 ", type.FullName, " 的表名称无效。"));
}
ta.Independent = RuntimeUtility.Contains<IndependentAttribute>(type, true);
// 加入缓存。
if (useCache)
{
lock (_tac)
{
if (!_tac.ContainsKey(cacheKey))
{
_tac.Add(cacheKey, ta);
}
}
}
return ta;
}
/// <summary></summary>
/// <exception cref="Exception">Exception</exception>"
static ColumnAttribute ParseColumn(Type type, PropertyInfo property, TableAttribute ta)
{
// 检查 ColumnAttribute。
ColumnAttribute ca;
{
var cas = property.GetCustomAttributes(typeof(ColumnAttribute), false);
if (cas.LongLength < 1L)
{
if (!ta.AllProperties) return null;
ca = new ColumnAttribute();
}
else ca = (ColumnAttribute)cas[0];
}
// 检查属性方法。
var getter = property.GetGetMethod(false);
var setter = property.GetSetMethod(false);
if (getter == null || getter.IsStatic) return null;
if (setter == null || setter.IsStatic) return null;
// getter 或 setter 存在异常时忽略此属性,而不是抛出异常。
// if (getter == null) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 不支持获取。"));
// if (setter == null) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 不支持设置。"));
// 检查列名称。
if (TextUtility.IsBlank(ca.Field))
{
ca = new ColumnAttribute("_" + property.Name, ca.Type, ca.Length, true);
if (TextUtility.IsBlank(ca.Field)) throw new Exception(TextUtility.Merge("类 ", type.FullName, "中,属性 ", property.Name, " 的列名称无效。"));
}
// 类型兼容。
var pt = property.PropertyType;
if (pt.Equals(typeof(byte[]).FullName)) ca.Type = ColumnType.Bytes;
else if (pt.Equals(typeof(Byte))) ca.Type = ColumnType.Integer;
else if (pt.Equals(typeof(SByte))) ca.Type = ColumnType.Integer;
else if (pt.Equals(typeof(Int16))) ca.Type = ColumnType.Integer;
else if (pt.Equals(typeof(UInt16))) ca.Type = ColumnType.Integer;
else if (pt.Equals(typeof(Int32))) ca.Type = ColumnType.Integer;
else if (pt.Equals(typeof(UInt32))) ca.Type = ColumnType.Integer;
else if (pt.Equals(typeof(Int64))) ca.Type = ColumnType.Integer;
else if (pt.Equals(typeof(Single))) ca.Type = ColumnType.Float;
else if (pt.Equals(typeof(Double))) ca.Type = ColumnType.Float;
else if (pt.Equals(typeof(Decimal))) ca.Type = ColumnType.Float;
else if (pt.Equals(typeof(DateTime))) ca.Type = ColumnType.DateTime;
else if (pt.Equals(typeof(String)))
{
switch (ca.Type)
{
case ColumnType.Bytes:
case ColumnType.Integer:
case ColumnType.Float:
case ColumnType.DateTime:
//throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 的类型不受支持。"));
ca.Type = ColumnType.NText;
break;
}
}
else
{
ca.Type = ColumnType.NText;
}
ca.Property = property;
return ca;
}
/// <summary>排序。</summary>
static Dictionary<string, ColumnAttribute> SortColumns(Dictionary<string, ColumnAttribute> columns)
{
// if (type.BaseType.FullName.Equals(typeof(Record).FullName)) // 仅当使用基类时排序。
var sorted = new Dictionary<string, ColumnAttribute>();
if (columns.ContainsKey("Key")) sorted.Add("Key", columns["Key"]);
if (columns.ContainsKey("Flag")) sorted.Add("Flag", columns["Flag"]);
if (columns.ContainsKey("Created")) sorted.Add("Created", columns["Created"]);
if (columns.ContainsKey("Updated")) sorted.Add("Updated", columns["Updated"]);
foreach (var property in columns.Keys)
{
if (property == "Key") continue;
if (property == "Flag") continue;
if (property == "Created") continue;
if (property == "Updated") continue;
sorted.Add(property, columns[property]);
}
#endregion
return sorted;
}
#region TableAttribute
/// <summary>限定表名称/列名称。</summary>
/// <param name="name">名称。</param>
@ -315,110 +161,84 @@ namespace Apewer.Source
return lower;
}
static IDataParameter GenerateDataParameter(IRecord entity, ColumnAttribute attribute, CreateDataParameterCallback callback)
static IDataParameter CreateParameter(IRecord record, ColumnAttribute ca, Func<Parameter, IDataParameter> callback)
{
var property = attribute.Property;
var property = ca.Property;
if (property == null) return null;
var getter = property.GetGetMethod();
if (getter == null) return null;
var parameter = null as IDataParameter;
var value = getter.Invoke(entity, null);
var value = getter.Invoke(record, null);
//
if (attribute.Type == ColumnType.Bytes || attribute.Type == ColumnType.Integer || attribute.Type == ColumnType.Float)
if (ca.Type == ColumnType.Bytes || ca.Type == ColumnType.Integer || ca.Type == ColumnType.Float)
{
var temp = value;
if (property.PropertyType.FullName == typeof(Decimal).FullName)
{
temp = NumberUtility.Double(temp.ToString());
}
parameter = callback(new Parameter(attribute.Field, temp, attribute.Type, attribute.Length));
return callback(new Parameter(ca.Field, value, ca.Type, ca.Length));
}
else if (attribute.Type == ColumnType.DateTime)
if (ca.Type == ColumnType.DateTime)
{
parameter = callback(new Parameter(attribute.Field, value, attribute.Type, 0));
return callback(new Parameter(ca.Field, value, ca.Type, 0));
}
else if (property.PropertyType.Equals(typeof(String)))
if (property.PropertyType.Equals(typeof(String)))
{
var text = value as string;
if (text == null) text = "";
if (attribute.Length > 0)
if (ca.Length > 0)
{
switch (attribute.Type)
switch (ca.Type)
{
case ColumnType.VarChar:
case ColumnType.NVarChar:
text = TextUtility.Left(text, attribute.Length);
text = TextUtility.Left(text, ca.Length);
break;
case ColumnType.VarChar255:
case ColumnType.NVarChar255:
text = TextUtility.Left(text, 255);
case ColumnType.VarChar191:
case ColumnType.NVarChar191:
text = TextUtility.Left(text, 191);
break;
}
}
parameter = callback(new Parameter(attribute.Field, text, attribute.Type, attribute.Length));
}
else
{
var text = (value == null) ? TextUtility.Empty : value.ToString();
parameter = callback(new Parameter(attribute.Field, text, attribute.Type, attribute.Length));
return callback(new Parameter(ca.Field, text, ca.Type, ca.Length));
}
return parameter;
var defaultText = (value == null) ? TextUtility.Empty : value.ToString();
return callback(new Parameter(ca.Field, defaultText, ca.Type, ca.Length));
}
/// <summary>生成 IDataParameter 列表,用于 Insert 或 Update。</summary>
/// <exception cref="ArgumentNullException"></exception>
public List<IDataParameter> CreateDataParameters(IRecord entity, CreateDataParameterCallback callback, params string[] excluded)
/// <summary>生成 IDataParameter 列表,用于 Insert 和 Update 方法。</summary>
public IDataParameter[] CreateParameters(IRecord record, Func<Parameter, IDataParameter> callback, params string[] excludeds)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
if (callback == null) throw new ArgumentNullException(nameof(excluded));
entity.FixProperties();
if (record == null || callback == null) return null;
record.FixProperties();
var list = new List<IDataParameter>();
foreach (var column in Columns)
var list = new List<IDataParameter>(_columns.Length);
foreach (var ca in Columns)
{
var attribute = column.Value;
if (ParseTable(entity.GetType()).Independent && attribute.Independent) continue;
if (ca == null) continue;
var parameter = GenerateDataParameter(entity, attribute, callback);
var parameter = CreateParameter(record, ca, callback);
if (parameter == null) continue;
var add = true;
foreach (var exclude in excluded)
if (excludeds != null)
{
var lower = parameter.ParameterName.ToLower();
if (lower == exclude.ToLower())
foreach (var excluded in excludeds)
{
add = false;
if (string.IsNullOrEmpty(excluded)) continue;
if (lower == excluded.ToLower())
{
add = false;
break;
}
}
}
if (add) list.Add(parameter);
}
return list;
}
#endregion
#region
/// <summary>获取 Record 根类中的属性名称。</summary>
static List<string> GetRootProperties()
{
var list = new List<string>();
var type = typeof(Record);
var properties = type.GetProperties();
foreach (var property in properties)
{
if (RuntimeUtility.Contains<ColumnAttribute>(property, false))
{
list.Add(property.Name);
}
}
return list;
return list.ToArray();
}
#endregion

4
Apewer/Source/Timeout.cs

@ -5,7 +5,7 @@ namespace Apewer.Source
/// <summary>超时。</summary>
[Serializable]
public struct Timeout
public class Timeout
{
private int _connect, _query, _execute;
@ -40,7 +40,7 @@ namespace Apewer.Source
}
/// <summary>默认超时设置:连接 10000、查询 60000,执行 60000。</summary>
public static Timeout Default { get { return new Timeout(10000, 60000, 60000); } }
public static Timeout Default { get => new Timeout(10000, 60000, 60000); }
}

6
Apewer/StringPairs.cs

@ -18,6 +18,12 @@ namespace Apewer
/// <summary></summary>
public StringPairs(int capacity) : base(capacity) { }
/// <summary></summary>
public string this[string key]
{
get { return GetValue(key); }
}
/// <summary>添加项。返回错误信息。</summary>
public string Add(string key, string value)
{

9
Apewer/TextUtility.cs

@ -513,18 +513,21 @@ namespace Apewer
public static double Similarity(string arg1, string arg2) => Levenshtein.Compute(arg1, arg2).Rate;
/// <summary>生成新的 GUID,默认为小写,且不包含连字符,长度为 32 位。</summary>
public static string NewGuid(bool hyphenation = false, bool lower = true)
public static string Guid(bool hyphenation = false, bool lower = true)
{
var guid = Guid.NewGuid();
var guid = System.Guid.NewGuid();
if (!hyphenation && lower) return guid.ToString("n");
var text = Guid.NewGuid().ToString();
var text = System.Guid.NewGuid().ToString();
if (lower) text = text.ToLower();
else text = text.ToUpper();
if (!hyphenation) text = text.Replace("-", "");
return text;
}
/// <summary>生成新主键。</summary>
public static string Key() => System.Guid.NewGuid().ToString().ToLower().Replace("-", "");
/// <summary>生成随机字符串,出现的字符由字符池指定,默认池包含数字和字母。</summary>
/// <param name="length">随机字符串的长度。</param>
/// <param name="pool">字符池,字符池中每个字符在随机字符串中出现的概率约等。</param>

4
Apewer/Web/ApiOptions.cs

@ -34,6 +34,10 @@ namespace Apewer.Web
/// <remarks>默认值:不缩进。</remarks>
public bool JsonIndent { get; set; } = false;
/// <summary>在响应头中设置 Content-Security-Policy,要求浏览器升级资源链接,使用 HTTPS。</summary>
/// <remarks>默认值:不要求。在 HTTPS 页面中,不自动升级 HTTP 资源。</remarks>
public bool UpgradeHttps { get; set; } = false;
/// <summary>允许响应中包含 Exception 对象的属性。</summary>
/// <remarks>默认值:不允许。</remarks>
public bool WithException { get; set; } = false;

6
Apewer/Web/ApiUtility.cs

@ -208,7 +208,7 @@ namespace Apewer.Web
if (url.IsEmpty()) return "ApiController 无效。";
var s = Json.NewObject();
s.SetProperty("random", TextUtility.NewGuid());
s.SetProperty("random", TextUtility.Guid());
s.SetProperty("application", application.IsEmpty() ? controller.Request.Application : application);
s.SetProperty("function", function.IsEmpty() ? controller.Request.Function : function);
s.SetProperty("data", controller.Request.Data);
@ -379,13 +379,11 @@ namespace Apewer.Web
public static string Respond(ApiResponse response, Json data, bool lower = true)
{
if (response == null) return "Response 对象无效。";
if (data != null)
{
if (lower) data = Json.Lower(data);
response.Data.Reset(data);
response.Data = data;
}
return null;
}

20
Apewer/Web/DefaultController.cs

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer.Web
{
/// <summary>默认控制器。</summary>
public abstract class DefaultController : ApiController
{
/// <summary>默认控制器实例。</summary>
public DefaultController() : base((c) => ((DefaultController)c).Process()) { }
/// <summary>处理请求。</summary>
public abstract void Process();
}
}

5
Apewer/_Common.props

@ -5,15 +5,16 @@
<!-- 生成 -->
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> -->
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
<LangVersion>latest</LangVersion>
<!--<NoWarn>CS0108,CS0162,CS0414,CS0612,CS0618,CS0649,CS1589,CS1570,CS1572,CS1573,CS3019,CS3021</NoWarn>-->
<!-- <NoWarn>CS0108,CS0162,CS0414,CS0612,CS0618,CS0649,CS1589,CS1570,CS1572,CS1573,CS3019,CS3021</NoWarn> -->
</PropertyGroup>
<!-- 程序集信息 -->
<PropertyGroup>
<Product>Apewer Libraries</Product>
<Version>6.4.0</Version>
<Version>6.4.1</Version>
</PropertyGroup>
<!-- NuGet -->

51
Apewer/_Extensions.cs

@ -45,6 +45,9 @@ public static class Extensions
/// <summary>判断静态属性。</summary>
public static bool IsStatic(this PropertyInfo @this) => RuntimeUtility.IsStatic(@this);
/// <summary>以安全的方式获取消息内容,对无效的 Exception 返回 NULL 值。</summary>
public static string Message(this Exception ex) => RuntimeUtility.Message(ex);
#endregion
#region Number
@ -152,6 +155,12 @@ public static class Extensions
/// <returns>剪取后的内容,不包含 head 和 foot。</returns>
public static string Cut(this string text, string head = null, string foot = null) => TextUtility.Cut(text, head, foot);
/// <summary>约束字符串中的字符,只包含指定的字符。</summary>
public static string Restrict(this string text, char[] chars) => TextUtility.Restrict(text, chars);
/// <summary>约束字符串中的字符,只包含指定的字符。</summary>
public static string Restrict(this string text, string chars) => TextUtility.Restrict(text, chars);
#endregion
#region Byte[]
@ -200,6 +209,7 @@ public static class Extensions
public static long Stamp(this DateTime @this, bool byMilliseconds = true) => ClockUtility.Stamp(@this, byMilliseconds);
/// <summary>转换为易于阅读的文本。</summary>
/// <remarks>格式:1970-</remarks>
public static string Lucid(this DateTime @this, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true) => ClockUtility.Lucid(@this, date, time, seconds, milliseconds);
/// <summary>转换为紧凑的文本。</summary>
@ -208,6 +218,10 @@ public static class Extensions
/// <summary>当前 DateTime 为闰年。</summary>
public static bool LeapYear(this DateTime @this) => ClockUtility.IsLeapYear(@this);
/// <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);
#endregion
#region Json
@ -252,7 +266,7 @@ public static class Extensions
/// <param name="ignoreCase">忽略属性名称大小写。</param>
/// <param name="ignoreChars">忽略的属性名称字符。</param>
/// <param name="force">强制填充,忽略 <typeparamref name="T"/> 的 Serializable 特性。</param>
public static List<T> Array<T>(this Json @this, bool ignoreCase = true, string ignoreChars = null, bool force = false) where T : class, new() => Apewer.Json.Array<T>(@this, ignoreCase, ignoreChars, force);
public static T[] Array<T>(this Json @this, bool ignoreCase = true, string ignoreChars = null, bool force = false) where T : class, new() => Apewer.Json.Array<T>(@this, ignoreCase, ignoreChars, force);
/// <summary>设置属性名称为小写。</summary>
public static Json Lower(this Json @this) => Apewer.Json.Lower(@this);
@ -411,21 +425,42 @@ public static class Extensions
/// <summary>修补基本属性。</summary>
public static void FixProperties(this IRecord @this) => Record.FixProperties(@this);
/// <summary></summary>
public static DateTime DateTime(this IQuery @this, int row, string column) => Query.DateTime(@this, row, column);
/// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
public static Class<DateTime> DateTime(this IQuery @this, int row = 0, int column = 0) => @this == null ? null : Query.DateTime(@this.Value(row, column));
/// <summary></summary>
/// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
public static Class<DateTime> DateTime(this IQuery @this, int row, string column) => @this == null ? null : Query.DateTime(@this.Value(row, column));
/// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
public static Int32 Int32(this IQuery @this, int row = 0, int column = 0) => @this == null ? 0 : Int32(@this.Text(row, column));
/// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
public static Int32 Int32(this IQuery @this, int row, string column) => @this == null ? 0 : Int32(@this.Text(row, column));
/// <summary></summary>
/// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
public static Int64 Int64(this IQuery @this, int row = 0, int column = 0) => @this == null ? 0L : Int64(@this.Text(row, column));
/// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
public static Int64 Int64(this IQuery @this, int row, string column) => @this == null ? 0L : Int64(@this.Text(row, column));
/// <summary></summary>
/// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
public static Decimal Decimal(this IQuery @this, int row = 0, int column = 0) => @this == null ? 0M : Decimal(@this.Text(row, column));
/// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
public static Decimal Decimal(this IQuery @this, int row, string column) => @this == null ? 0M : Decimal(@this.Text(row, column));
/// <summary></summary>
/// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>>
public static Double Double(this IQuery @this, int row = 0, int column = 0) => @this == null ? 0D : Double(@this.Text(row, column));
/// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>>
public static Double Double(this IQuery @this, int row, string column) => @this == null ? 0D : Double(@this.Text(row, column));
/// <summary>获取默认表中指定单元格的内容。从第 0 行第 0 列开始。</summary>
public static string Text(this IQuery @this, int row = 0, int column = 0) => @this == null ? null : Query.Text(@this.Value(row, column));
/// <summary>获取默认表中指定单元格的内容。从第 0 行开始。</summary>
public static string Text(this IQuery @this, int row, string column) => @this == null ? null : Query.Text(@this.Value(row, column));
#endregion
#region Web
@ -456,7 +491,7 @@ public static class Extensions
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, Record record, bool lower = true) => ApiUtility.Respond(@this, record, lower);
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);

10
ChangeLog.md

@ -1,6 +1,16 @@

### 最新提交
### 6.4.1
- Clock:增加 long.DateTime() 方法;
- Json:引用的 List 现改为数组;
- Source:SqlServer 重命名为 SqlClient,并支持 .NET Standard;
- Source:Query 和 Execute 支持 ToJson 方法;
- Source:TableAttribute 增加 Store 属性,用于 Accessor 匹配;
- Source:ORM 引用的 List 现改为数组;
- Source:增加 Begin、Commit 和 Rollback,用于控制事务;
- Web:不再要求控制器拥有 public 修饰符。
### 6.4.0
- 重构项目,减少了主类库的依赖项和文件体积;
- BytesUtility:新类,由 BinaryUtility 重命名而来;

Loading…
Cancel
Save