22 changed files with 1272 additions and 435 deletions
			
			
		@ -1,64 +0,0 @@ | 
				
			|||||
using System; | 
					 | 
				
			||||
using System.Collections.Generic; | 
					 | 
				
			||||
using System.Text; | 
					 | 
				
			||||
 | 
					 | 
				
			||||
namespace Apewer.Source | 
					 | 
				
			||||
{ | 
					 | 
				
			||||
 | 
					 | 
				
			||||
    /// <summary></summary>
 | 
					 | 
				
			||||
    public abstract class Accessor<T> where T : class, IDatabase, new() | 
					 | 
				
			||||
    { | 
					 | 
				
			||||
         | 
					 | 
				
			||||
        private T _source = null; | 
					 | 
				
			||||
 | 
					 | 
				
			||||
        private bool _disposed = false; | 
					 | 
				
			||||
 | 
					 | 
				
			||||
        /// <summary></summary>
 | 
					 | 
				
			||||
        public Accessor() { } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
        /// <summary></summary>
 | 
					 | 
				
			||||
        protected virtual T Source | 
					 | 
				
			||||
        { | 
					 | 
				
			||||
            get | 
					 | 
				
			||||
            { | 
					 | 
				
			||||
                if (_source == null) _source = new T(); | 
					 | 
				
			||||
                return _source; | 
					 | 
				
			||||
            } | 
					 | 
				
			||||
        } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
        /// <summary></summary>
 | 
					 | 
				
			||||
        public virtual bool Connected | 
					 | 
				
			||||
        { | 
					 | 
				
			||||
            get | 
					 | 
				
			||||
            { | 
					 | 
				
			||||
                return (_source == null) ? false : _source.Online; | 
					 | 
				
			||||
            } | 
					 | 
				
			||||
        } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
        /// <summary></summary>
 | 
					 | 
				
			||||
        public virtual bool Disposed | 
					 | 
				
			||||
        { | 
					 | 
				
			||||
            get { return _disposed; } | 
					 | 
				
			||||
        } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
        /// <summary></summary>
 | 
					 | 
				
			||||
        public virtual void Close() | 
					 | 
				
			||||
        { | 
					 | 
				
			||||
            if (_source == null) return; | 
					 | 
				
			||||
            _source.Close(); | 
					 | 
				
			||||
        } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
        /// <summary></summary>
 | 
					 | 
				
			||||
        public virtual void Dispose() | 
					 | 
				
			||||
        { | 
					 | 
				
			||||
            if (_source != null) | 
					 | 
				
			||||
            { | 
					 | 
				
			||||
                _source.Dispose(); | 
					 | 
				
			||||
                _source = null; | 
					 | 
				
			||||
            } | 
					 | 
				
			||||
            _disposed = true; | 
					 | 
				
			||||
        } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
} | 
					 | 
				
			||||
@ -0,0 +1,46 @@ | 
				
			|||||
 | 
					using System; | 
				
			||||
 | 
					using System.Collections.Generic; | 
				
			||||
 | 
					using System.Text; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					namespace Apewer.Source | 
				
			||||
 | 
					{ | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    /// <summary>数据库引擎支持 ORM 访问。</summary>
 | 
				
			||||
 | 
					    public interface IOrm : IDatabase | 
				
			||||
 | 
					    { | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>初始化指定类型,以创建表或增加字段。</summary>
 | 
				
			||||
 | 
					        /// <param name="model">要初始化的类型。</param>
 | 
				
			||||
 | 
					        /// <returns>错误信息。当成功时候返回空字符串。</returns>
 | 
				
			||||
 | 
					        public string Initialize(Type model); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>插入记录。</summary>
 | 
				
			||||
 | 
					        /// <param name="record">要插入的记录实体。</param>
 | 
				
			||||
 | 
					        /// <returns>错误信息。当成功时候返回空字符串。</returns>
 | 
				
			||||
 | 
					        public string Insert(Record record); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>更新记录。</summary>
 | 
				
			||||
 | 
					        /// <param name="record">要插入的记录实体。</param>
 | 
				
			||||
 | 
					        /// <returns>错误信息。当成功时候返回空字符串。</returns>
 | 
				
			||||
 | 
					        public string Update(Record record); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>获取指定类型的主键,按 Flag 属性筛选。</summary>
 | 
				
			||||
 | 
					        /// <param name="model">要查询的类型。</param>
 | 
				
			||||
 | 
					        /// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
 | 
				
			||||
 | 
					        public Result<List<string>> Keys(Type model, long flag = 0); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>获取具有指定 Key 的记录,并要求记录具有指定的 Flag 属性。</summary>
 | 
				
			||||
 | 
					        /// <param name="key">目标记录的主键。</param>
 | 
				
			||||
 | 
					        /// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
 | 
				
			||||
 | 
					        public Result<T> Get<T>(string key, long flag = 0) where T : Record; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>使用指定语句查询,获取查询结果。</summary>
 | 
				
			||||
 | 
					        public Result<List<T>> Query<T>(string sql) where T : Record; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>查询所有记录。</summary>
 | 
				
			||||
 | 
					        /// <param name="flag">要求目标记录具有的 Flag 属性,当指定 0 时忽略此要求。</param>
 | 
				
			||||
 | 
					        public Result<List<T>> Query<T>(long flag = 0) where T : Record; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					} | 
				
			||||
@ -0,0 +1,965 @@ | 
				
			|||||
 | 
					#if EXTRA
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					/* 2021.08.03 */ | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					using Apewer; | 
				
			||||
 | 
					using Apewer.Source; | 
				
			||||
 | 
					using System; | 
				
			||||
 | 
					using System.Collections.Generic; | 
				
			||||
 | 
					using System.Data; | 
				
			||||
 | 
					using System.Data.SQLite; | 
				
			||||
 | 
					using System.Text; | 
				
			||||
 | 
					using System.Data.Common; | 
				
			||||
 | 
					//using Mono.Data.Sqlite;
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					namespace Apewer.Source | 
				
			||||
 | 
					{ | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    /// <summary>用于快速连接 SQLite 数据库的辅助。</summary>
 | 
				
			||||
 | 
					    public class Sqlite : IDatabase, IOrm, IDisposable | 
				
			||||
 | 
					    { | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #region 变量定义。
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        private SQLiteConnection _db = null; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        private Timeout _timeout = new Timeout(); | 
				
			||||
 | 
					        private string _connstring = ""; | 
				
			||||
 | 
					        private string _path = ""; | 
				
			||||
 | 
					        private string _pass = ""; | 
				
			||||
 | 
					        private byte[] _passdata = BinaryUtility.EmptyBytes; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #endregion
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #region this
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        private void VarInit(string path, Timeout timeout, string pass, byte[] passData) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            _path = (path == null) ? "" : path; | 
				
			||||
 | 
					            _passdata = (passData == null) ? BinaryUtility.EmptyBytes : passData; | 
				
			||||
 | 
					            _pass = (pass == null) ? "" : 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); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>连接指定文件。</summary>
 | 
				
			||||
 | 
					        private Sqlite(string path, Timeout timeout, byte[] pass) => VarInit(path, timeout, null, pass); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>连接指定文件。</summary>
 | 
				
			||||
 | 
					        public Sqlite(string path, Timeout timeout, string pass) => VarInit(path, timeout, pass, null); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #endregion
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #region 日志。
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <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); | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        private void LogError(string action, string message) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var logger = Logger; | 
				
			||||
 | 
					            if (logger != null) logger.Error(this, "SQLite", action, message); | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #endregion
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #region 实现接口。
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>数据库是否已经连接。</summary>
 | 
				
			||||
 | 
					        public bool Online { get => _db != null && _db.State == ConnectionState.Open; } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>连接数据库,若未连接则尝试连接。</summary>
 | 
				
			||||
 | 
					        /// <returns>是否已连接。</returns>
 | 
				
			||||
 | 
					        public bool Connect() | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            if (_db == null) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                _db = new SQLiteConnection(); | 
				
			||||
 | 
					                _db.ConnectionString = ConnectionString; | 
				
			||||
 | 
					                //if (string.IsNullOrEmpty(_connstring) && string.IsNullOrEmpty(_pass) && (_passdata.Length > 0))
 | 
				
			||||
 | 
					                //{
 | 
				
			||||
 | 
					                //    _db.SetPassword(_pass);
 | 
				
			||||
 | 
					                //}
 | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            else | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                if (_db.State == ConnectionState.Open) return true; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            try | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                _db.Open(); | 
				
			||||
 | 
					                switch (_db.State) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    case ConnectionState.Open: return true; | 
				
			||||
 | 
					                    default: return false; | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            catch (Exception ex) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                LogError("Connection", ex, _db.ConnectionString); | 
				
			||||
 | 
					                Close(); | 
				
			||||
 | 
					                return false; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>关闭连接,并释放对象所占用的系统资源。</summary>
 | 
				
			||||
 | 
					        public void Close() | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            if (_db != null) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                //_db.Close();
 | 
				
			||||
 | 
					                _db.Dispose(); | 
				
			||||
 | 
					                _db = null; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>关闭连接,释放对象所占用的系统资源,并清除连接信息。</summary>
 | 
				
			||||
 | 
					        public void Dispose() { Close(); } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>查询。</summary>
 | 
				
			||||
 | 
					        public IQuery Query(string sql) => Query(sql, null); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>查询。</summary>
 | 
				
			||||
 | 
					        public IQuery Query(string sql, IEnumerable<IDataParameter> parameters) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            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) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    foreach (var p in parameters) | 
				
			||||
 | 
					                    { | 
				
			||||
 | 
					                        if (p != null) command.Parameters.Add(p); | 
				
			||||
 | 
					                    } | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					                using (var dataset = new DataSet()) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    using (var da = new SQLiteDataAdapter(sql, _db)) | 
				
			||||
 | 
					                    { | 
				
			||||
 | 
					                        da.Fill(dataset, table); | 
				
			||||
 | 
					                        query.Table = dataset.Tables[table]; | 
				
			||||
 | 
					                    } | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					                command.Dispose(); | 
				
			||||
 | 
					                query.Success = true; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            catch (Exception ex) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                LogError("Query", ex, sql); | 
				
			||||
 | 
					                query.Success = false; | 
				
			||||
 | 
					                query.Exception = ex; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            return query; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>执行单条 Transact-SQL 语句。</summary>
 | 
				
			||||
 | 
					        public IExecute Execute(string sql) => Execute(sql, null); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>执行单条 Transact-SQL 语句,并加入参数。</summary>
 | 
				
			||||
 | 
					        public IExecute Execute(string sql, IEnumerable<IDataParameter> parameters) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            if (string.IsNullOrEmpty(sql)) return Example.InvalidExecuteStatement; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            var connected = Connect(); | 
				
			||||
 | 
					            if (!connected) return Example.InvalidExecuteConnection; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            var transaction = _db.BeginTransaction(); | 
				
			||||
 | 
					            var execute = new Execute(); | 
				
			||||
 | 
					            try | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                var command = new SQLiteCommand(); | 
				
			||||
 | 
					                command.Connection = _db; | 
				
			||||
 | 
					                command.Transaction = transaction; | 
				
			||||
 | 
					                command.CommandTimeout = Timeout.Execute; | 
				
			||||
 | 
					                command.CommandText = sql; | 
				
			||||
 | 
					                if (parameters != null) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    foreach (var p in parameters) | 
				
			||||
 | 
					                    { | 
				
			||||
 | 
					                        if (p != null) command.Parameters.Add(p); | 
				
			||||
 | 
					                    } | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					                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; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            try { transaction.Dispose(); } catch { } | 
				
			||||
 | 
					            return execute; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #endregion
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #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) ? BinaryUtility.EmptyBytes : value; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>保存当前数据库到文件,若文件已存在则将重写文件。</summary>
 | 
				
			||||
 | 
					        public bool Save(string path) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            if (!StorageUtility.CreateFile(path, 0, true)) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                LogError("Save", TextUtility.Merge("创建文件 ", path, " 失败。")); | 
				
			||||
 | 
					                return false; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            var temp = new Sqlite(path); | 
				
			||||
 | 
					            var result = Save(temp); | 
				
			||||
 | 
					            temp.Close(); | 
				
			||||
 | 
					            return result; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>保存当前数据库到文件,若文件已存在则将重写文件。</summary>
 | 
				
			||||
 | 
					        public bool Save(string path, string password) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            if (!StorageUtility.CreateFile(path, 0, true)) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                LogError("Save", TextUtility.Merge("创建文件 ", path, " 失败。")); | 
				
			||||
 | 
					                return false; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            var temp = new Sqlite(path, password); | 
				
			||||
 | 
					            var result = Save(temp); | 
				
			||||
 | 
					            temp.Close(); | 
				
			||||
 | 
					            return result; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>保存当前数据库到目标数据库。</summary>
 | 
				
			||||
 | 
					        public bool Save(Sqlite destination) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var error = Backup(this, destination); | 
				
			||||
 | 
					            return string.IsNullOrEmpty(error); | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>加载文件到当前数据库。</summary>
 | 
				
			||||
 | 
					        public bool Load(string path) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var temp = new Sqlite(path); | 
				
			||||
 | 
					            var result = Load(temp); | 
				
			||||
 | 
					            temp.Close(); | 
				
			||||
 | 
					            return result; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>加载文件到当前数据库。</summary>
 | 
				
			||||
 | 
					        public bool Load(string path, params byte[] pass) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var temp = new Sqlite(path, pass); | 
				
			||||
 | 
					            var result = Load(temp); | 
				
			||||
 | 
					            temp.Close(); | 
				
			||||
 | 
					            return result; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>加载文件到当前数据库。</summary>
 | 
				
			||||
 | 
					        public bool Load(string path, string pass) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var temp = new Sqlite(path, pass); | 
				
			||||
 | 
					            var result = Load(temp); | 
				
			||||
 | 
					            temp.Close(); | 
				
			||||
 | 
					            return result; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>加载源数据库到当前数据库。</summary>
 | 
				
			||||
 | 
					        public bool Load(Sqlite source) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var error = Backup(source, this); | 
				
			||||
 | 
					            return string.IsNullOrEmpty(error); | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #endregion
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #region ORM。
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>查询数据库中的所有表名。</summary>
 | 
				
			||||
 | 
					        public List<string> TableNames() | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var list = new List<string>(); | 
				
			||||
 | 
					            if (Connect()) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                var sql = "select name from sqlite_master where type='table' order by name; "; | 
				
			||||
 | 
					                var query = (Query)Query(sql); | 
				
			||||
 | 
					                for (int r = 0; r < query.Rows; r++) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    var cell = query.Text(r, 0); | 
				
			||||
 | 
					                    if (TextUtility.IsBlank(cell)) continue; | 
				
			||||
 | 
					                    list.Add(cell); | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					                query.Dispose(); | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            return list; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>查询数据库中的所有视图名。</summary>
 | 
				
			||||
 | 
					        public List<string> ViewNames() | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var list = new List<string>(); | 
				
			||||
 | 
					            if (Connect()) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                var sql = "select name from sqlite_master where type='view' order by name; "; | 
				
			||||
 | 
					                var query = (Query)Query(sql); | 
				
			||||
 | 
					                for (int r = 0; r < query.Rows; r++) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    var cell = query.Text(r, 0); | 
				
			||||
 | 
					                    if (TextUtility.IsBlank(cell)) continue; | 
				
			||||
 | 
					                    list.Add(cell); | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					                query.Dispose(); | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            return list; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>查询表中的所有列名。</summary>
 | 
				
			||||
 | 
					        public List<string> ColumnNames(string table) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var list = new List<string>(); | 
				
			||||
 | 
					            if (Connect()) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                var t = TextUtility.AntiInject(table); | 
				
			||||
 | 
					                var sql = TextUtility.Merge("pragma table_info('", TextUtility.AntiInject(t), "'); "); | 
				
			||||
 | 
					                using (var query = Query(sql) as Query) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    for (int r = 0; r < query.Rows; r++) | 
				
			||||
 | 
					                    { | 
				
			||||
 | 
					                        var cell = query.Text(r, "name"); | 
				
			||||
 | 
					                        if (TextUtility.IsBlank(cell)) continue; | 
				
			||||
 | 
					                        list.Add(cell); | 
				
			||||
 | 
					                    } | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            return list; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>创建表,不修改已存在表。成功时返回空字符串,发生异常时返回异常信息。</summary>
 | 
				
			||||
 | 
					        public string Initialize(Record model) => model == null ? "参数无效。" : Initialize(model.GetType()); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>创建表,不修改已存在表。成功时返回空字符串,发生异常时返回异常信息。</summary>
 | 
				
			||||
 | 
					        public string Initialize<T>() where T : Record => Initialize(typeof(T)); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>创建表,不修改已存在表。当现存表中缺少模型中属性对应的列时增加列。成功时返回空字符串,发生异常时返回异常信息。</summary>
 | 
				
			||||
 | 
					        public string Initialize(Type model) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var structure = null as TableStructure; | 
				
			||||
 | 
					            try { structure = TableStructure.ParseModel(model); } | 
				
			||||
 | 
					            catch (Exception exception) { return exception.Message; } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            // 连接数据库。
 | 
				
			||||
 | 
					            if (!Connect()) return "连接数据库失败。"; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            // 检查现存表。
 | 
				
			||||
 | 
					            var exists = false; | 
				
			||||
 | 
					            var tables = TableNames(); | 
				
			||||
 | 
					            if (tables.Count > 0) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                var lower = structure.Table.ToLower(); | 
				
			||||
 | 
					                foreach (var table in tables) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    if (TextUtility.IsBlank(table)) continue; | 
				
			||||
 | 
					                    if (table.ToLower() == lower) | 
				
			||||
 | 
					                    { | 
				
			||||
 | 
					                        exists = true; | 
				
			||||
 | 
					                        break; | 
				
			||||
 | 
					                    } | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            if (exists) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                return TextUtility.Merge("指定的表已经存在。"); | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            else | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                var sqlcolumns = new List<string>(); | 
				
			||||
 | 
					                foreach (var column in structure.Columns.Values) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    var type = GetColumnDeclaration(column); | 
				
			||||
 | 
					                    if (type == TextUtility.EmptyString) return TextUtility.Merge("类型 ", column.Type.ToString(), " 不受支持。"); | 
				
			||||
 | 
					                    sqlcolumns.Add(type); | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					                var sql = TextUtility.Merge("create table [", structure.Table, "](", TextUtility.Join(", ", sqlcolumns), "); "); | 
				
			||||
 | 
					                var execute = Execute(sql); | 
				
			||||
 | 
					                if (execute.Success) return TextUtility.EmptyString; | 
				
			||||
 | 
					                return execute.Error; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>插入记录。成功时候返回空字符串,发生异常时返回异常信息。</summary>
 | 
				
			||||
 | 
					        public string Insert(Record 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 execute = Execute(sql, parameters); | 
				
			||||
 | 
					            if (execute.Success && execute.Rows > 0) return TextUtility.EmptyString; | 
				
			||||
 | 
					            return execute.Error; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>更新记录,实体中的 Created 和 Key 属性不被更新。成功时返回空字符串,发生异常时返回异常信息。</summary>
 | 
				
			||||
 | 
					        public string Update(Record record) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            if (record == null) return "参数无效。"; | 
				
			||||
 | 
					            record.FixProperties(); | 
				
			||||
 | 
					            record.Updated = ClockUtility.LucidNow; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            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 execute = Execute(sql, parameters); | 
				
			||||
 | 
					            if (execute.Success && execute.Rows > 0) return TextUtility.EmptyString; | 
				
			||||
 | 
					            return execute.Error; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>获取具有指定 Key 的记录。</summary>
 | 
				
			||||
 | 
					        public Result<T> Get<T>(string key) where T : Record => Get<T>(key, 0); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>获取具有指定 Key 的记录。</summary>
 | 
				
			||||
 | 
					        public Result<T> Get<T>(string key, long flag) where T : Record | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var k = TextUtility.SafeKey(key); | 
				
			||||
 | 
					            if (TextUtility.IsBlank(k) == false) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                try | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    var ts = TableStructure.ParseModel(typeof(T)); | 
				
			||||
 | 
					                    var f = (flag == 0) ? TextUtility.EmptyString : TextUtility.Merge(" _flag=", flag.ToString(), " and"); | 
				
			||||
 | 
					                    var query = (Query)Query(TextUtility.Merge("select * from [", ts.Table, "] where", f, " _key='", k, "' limit 1; ")); | 
				
			||||
 | 
					                    var list = query.Fill<T>(); | 
				
			||||
 | 
					                    query.Dispose(); | 
				
			||||
 | 
					                    if (list.Count > 0) return new Result<T>(list[0]); | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					                catch (Exception ex) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    return new Result<T>(ex); | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            return new Result<T>(new Exception("参数无效。")); | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>查询多条记录。</summary>
 | 
				
			||||
 | 
					        public Result<List<T>> Query<T>() where T : Record => Query<T>(0); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>查询多条记录。</summary>
 | 
				
			||||
 | 
					        public Result<List<T>> Query<T>(long flag) where T : Record | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            try | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                var ts = TableStructure.ParseModel(typeof(T)); | 
				
			||||
 | 
					                var f = (flag == 0) ? TextUtility.EmptyString : TextUtility.Merge(" where _flag=", flag.ToString()); | 
				
			||||
 | 
					                var query = Query(TextUtility.Merge("select * from [", ts.Table, "]", f, "; ")) as Query; | 
				
			||||
 | 
					                var list = query.Fill<T>(); | 
				
			||||
 | 
					                query.Dispose(); | 
				
			||||
 | 
					                return new Result<List<T>>(list); | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            catch (Exception ex) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                return new Result<List<T>>(ex); | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>获取按指定语句查询到的所有记录。</summary>
 | 
				
			||||
 | 
					        public Result<List<T>> Query<T>(string sql) where T : Record | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            using (var query = Query(sql) as Query) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                if (query.Exception == null) return new Result<List<T>>(query.Fill<T>()); | 
				
			||||
 | 
					                else return new Result<List<T>>(query.Exception); | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>查询所有有效的 Key 值。</summary>
 | 
				
			||||
 | 
					        public Result<List<string>> Keys<T>() where T : Record => Keys<T>(0); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>查询所有有效的 Key 值。</summary>
 | 
				
			||||
 | 
					        public Result<List<string>> Keys<T>(long flag) where T : Record => Keys(typeof(T), flag); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>查询所有有效的 Key 值。</summary>
 | 
				
			||||
 | 
					        public Result<List<string>> Keys(Type model) => Keys(model, 0); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>查询所有有效的 Key 值。</summary>
 | 
				
			||||
 | 
					        public Result<List<string>> Keys(Type model, long flag) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            if (model != null) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                try | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    var list = new List<string>(); | 
				
			||||
 | 
					                    var ts = TableStructure.ParseModel((Type)model); | 
				
			||||
 | 
					                    var f = (flag == 0) ? TextUtility.EmptyString : TextUtility.Merge(" where _flag=", flag.ToString()); | 
				
			||||
 | 
					                    var query = (Query)Query(TextUtility.Merge("select _key from [", ts.Table, "]", f, "; ")); | 
				
			||||
 | 
					                    for (var r = 0; r < query.Rows; r++) | 
				
			||||
 | 
					                    { | 
				
			||||
 | 
					                        var cell = TextUtility.Trim(query.Text(r)); | 
				
			||||
 | 
					                        if (cell.Length > 0) list.Add(cell); | 
				
			||||
 | 
					                    } | 
				
			||||
 | 
					                    query.Dispose(); | 
				
			||||
 | 
					                    return new Result<List<string>>(list); | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					                catch (Exception ex) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    return new Result<List<string>>(ex); | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            return new Result<List<string>>(new Exception("参数无效。")); | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #endregion
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #region static
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>对文本转义,符合 SQL 安全性。可根据字段类型限制 UTF-8 字节数,默认为 0 时不限制字节数。</summary>
 | 
				
			||||
 | 
					        public static string Escape(string text, int bytes = 0) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            if (text.IsEmpty()) return ""; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            var t = text ?? ""; | 
				
			||||
 | 
					            t = t.Replace("\\", "\\\\"); | 
				
			||||
 | 
					            t = t.Replace("'", "\\'"); | 
				
			||||
 | 
					            t = t.Replace("\n", "\\n"); | 
				
			||||
 | 
					            t = t.Replace("\r", "\\r"); | 
				
			||||
 | 
					            t = t.Replace("\b", "\\b"); | 
				
			||||
 | 
					            t = t.Replace("\t", "\\t"); | 
				
			||||
 | 
					            t = t.Replace("\f", "\\f"); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            if (bytes > 5) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                if (t.GetBytes(Encoding.UTF8).Length > bytes) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    while (true) | 
				
			||||
 | 
					                    { | 
				
			||||
 | 
					                        t = t.Substring(0, t.Length - 1); | 
				
			||||
 | 
					                        if (t.GetBytes(Encoding.UTF8).Length <= (bytes - 4)) break; | 
				
			||||
 | 
					                    } | 
				
			||||
 | 
					                    t = t + " ..."; | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            return t; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        private static string GetColumnTypeName(ColumnType type) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            switch (type) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                case ColumnType.Binary: | 
				
			||||
 | 
					                    return "blob"; | 
				
			||||
 | 
					                case ColumnType.Integer: | 
				
			||||
 | 
					                    return "integer"; | 
				
			||||
 | 
					                case ColumnType.Float: | 
				
			||||
 | 
					                    return "float"; | 
				
			||||
 | 
					                case ColumnType.VarChar: | 
				
			||||
 | 
					                case ColumnType.VarChar255: | 
				
			||||
 | 
					                case ColumnType.VarCharMax: | 
				
			||||
 | 
					                    return "varchar"; | 
				
			||||
 | 
					                case ColumnType.Text: | 
				
			||||
 | 
					                    return "text"; | 
				
			||||
 | 
					                case ColumnType.NVarChar: | 
				
			||||
 | 
					                case ColumnType.NVarChar255: | 
				
			||||
 | 
					                case ColumnType.NVarCharMax: | 
				
			||||
 | 
					                    return "nvarchar"; | 
				
			||||
 | 
					                case ColumnType.NText: | 
				
			||||
 | 
					                    return "ntext"; | 
				
			||||
 | 
					                default: | 
				
			||||
 | 
					                    return null; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        private static string GetColumnDeclaration(ColumnAttribute column) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var type = TextUtility.EmptyString; | 
				
			||||
 | 
					            var length = NumberUtility.RestrictValue(column.Length, 0, 255).ToString(); | 
				
			||||
 | 
					            switch (column.Type) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                case ColumnType.Binary: | 
				
			||||
 | 
					                    type = "blob"; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case ColumnType.Integer: | 
				
			||||
 | 
					                    type = "integer"; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case ColumnType.Float: | 
				
			||||
 | 
					                    type = "real"; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case ColumnType.VarChar: | 
				
			||||
 | 
					                case ColumnType.VarChar255: | 
				
			||||
 | 
					                    type = TextUtility.Merge("varchar(", length, ")"); | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case ColumnType.VarCharMax: | 
				
			||||
 | 
					                    type = TextUtility.Merge("varchar(255)"); | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case ColumnType.Text: | 
				
			||||
 | 
					                    type = TextUtility.Merge("text"); | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case ColumnType.NVarChar: | 
				
			||||
 | 
					                case ColumnType.NVarChar255: | 
				
			||||
 | 
					                    type = TextUtility.Merge("nvarchar(", length, ")"); | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case ColumnType.NVarCharMax: | 
				
			||||
 | 
					                    type = TextUtility.Merge("nvarchar(255)"); | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case ColumnType.NText: | 
				
			||||
 | 
					                    type = TextUtility.Merge("ntext"); | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                default: | 
				
			||||
 | 
					                    return TextUtility.EmptyString; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            return TextUtility.Merge("[", (object)column.Field, "] ", type); | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>创建参数。</summary>
 | 
				
			||||
 | 
					        /// <exception cref="ArgumentNullException"></exception>
 | 
				
			||||
 | 
					        /// <exception cref="InvalidOperationException"></exception>
 | 
				
			||||
 | 
					        public static SQLiteParameter CreateDataParameter(Parameter parameter) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            if (parameter == null) throw new InvalidOperationException("参数无效。"); | 
				
			||||
 | 
					            return CreateDataParameter(parameter.Name, parameter.Type, parameter.Size, parameter.Value); | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>创建参数。</summary>
 | 
				
			||||
 | 
					        public static SQLiteParameter CreateDataParameter(string name, ColumnType type, int size, object value) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var n = TextUtility.Trim(name); | 
				
			||||
 | 
					            if (TextUtility.IsBlank(n)) return null; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            var t = GetColumnTypeName(type); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            var s = size; | 
				
			||||
 | 
					            switch (type) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                case ColumnType.VarChar: | 
				
			||||
 | 
					                    s = NumberUtility.RestrictValue(s, 0, 8000); | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case ColumnType.NVarChar: | 
				
			||||
 | 
					                    s = NumberUtility.RestrictValue(s, 0, 4000); | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case ColumnType.VarChar255: | 
				
			||||
 | 
					                case ColumnType.VarCharMax: | 
				
			||||
 | 
					                case ColumnType.NVarChar255: | 
				
			||||
 | 
					                case ColumnType.NVarCharMax: | 
				
			||||
 | 
					                    s = NumberUtility.RestrictValue(s, 0, 255); | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                default: | 
				
			||||
 | 
					                    s = 0; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            var v = value; | 
				
			||||
 | 
					            if (v is string && v != null && s > 0) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                v = TextUtility.RestrictLength((string)v, s); | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            var p = new SQLiteParameter(); | 
				
			||||
 | 
					            p.ParameterName = n; | 
				
			||||
 | 
					            p.TypeName = t; | 
				
			||||
 | 
					            p.Value = v; | 
				
			||||
 | 
					            if (s > 0) p.Size = s; | 
				
			||||
 | 
					            return p; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>创建参数。</summary>
 | 
				
			||||
 | 
					        public static SQLiteParameter CreateDataParameter(string name, string type, int size, object value) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var v = value; | 
				
			||||
 | 
					            if (value is string && value != null && size > 0) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                v = TextUtility.RestrictLength((string)value, size); | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            var p = new SQLiteParameter(); | 
				
			||||
 | 
					            p.ParameterName = name; | 
				
			||||
 | 
					            p.TypeName = type; | 
				
			||||
 | 
					            p.Value = v; | 
				
			||||
 | 
					            p.Size = size; | 
				
			||||
 | 
					            return p; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>创建参数。</summary>
 | 
				
			||||
 | 
					        public static SQLiteParameter CreateDataParameter(string name, string type, object value) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var p = new SQLiteParameter(); | 
				
			||||
 | 
					            p.ParameterName = name; | 
				
			||||
 | 
					            p.TypeName = type; | 
				
			||||
 | 
					            p.Value = value; | 
				
			||||
 | 
					            return p; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>备份数据库,返回错误信息。</summary>
 | 
				
			||||
 | 
					        public static string Backup(Sqlite source, Sqlite destination) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            if (source == null) return "SQLite Backup Failed: Invalid Source"; | 
				
			||||
 | 
					            if (destination == null) return "SQLite Backup Failed: Invalid Destination"; | 
				
			||||
 | 
					            if (!source.Connect()) return "SQLite Backup Failed: Invalid Source Connection"; | 
				
			||||
 | 
					            if (!destination.Connect()) return "SQLite Backup Failed: Invalid Destination Connection"; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            try | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                source._db.BackupDatabase(destination._db, "main", "main", -1, null, 0); | 
				
			||||
 | 
					                return ""; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            catch (Exception ex) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                return "SQLite Load Failed: " + ex.Message; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>创建参数。</summary>
 | 
				
			||||
 | 
					        public static IDbDataParameter CreateParameter(string field, DbType type, int size, object value) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var p = new SQLiteParameter(); | 
				
			||||
 | 
					            p.ParameterName = field; | 
				
			||||
 | 
					            p.DbType = type; | 
				
			||||
 | 
					            p.Size = size; | 
				
			||||
 | 
					            p.Value = value; | 
				
			||||
 | 
					            return p; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>创建参数。</summary>
 | 
				
			||||
 | 
					        public static IDbDataParameter CreateParameter(string field, DbType type, object value) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var p = new SQLiteParameter(); | 
				
			||||
 | 
					            p.ParameterName = field; | 
				
			||||
 | 
					            p.DbType = type; | 
				
			||||
 | 
					            p.Value = value; | 
				
			||||
 | 
					            return p; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>整理数据库,压缩未使用的空间。</summary>
 | 
				
			||||
 | 
					        public const string Vacuum = "vacuum"; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>内存数据库的地址。</summary>
 | 
				
			||||
 | 
					        public const string Memory = ":memory:"; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #endregion
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #region ORM
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        private static string GetParameterName(string parameter) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var name = TextUtility.AntiInject(parameter, 255); | 
				
			||||
 | 
					            if (name.StartsWith("@") && name.Length > 1) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                name = name.Substring(1, name.Length - 1); | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            return name; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        private static string GetParameterName(IDataParameter parameter) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var name = TextUtility.EmptyString; | 
				
			||||
 | 
					            if (parameter != null) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                name = GetParameterName(parameter.ParameterName); | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            return name; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        private static List<string> GetParametersNames(IEnumerable<IDataParameter> parameters) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var columns = new List<string>(); | 
				
			||||
 | 
					            if (parameters != null) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                foreach (var parameter in parameters) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    var name = GetParameterName(parameter); | 
				
			||||
 | 
					                    var isblank = TextUtility.IsBlank(name); | 
				
			||||
 | 
					                    if (isblank) continue; | 
				
			||||
 | 
					                    columns.Add(name); | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            return columns; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        private static string GenerateInsertStatement(string table, List<string> columns) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var r = TextUtility.EmptyString; | 
				
			||||
 | 
					            var t = TextUtility.AntiInject(table, 255); | 
				
			||||
 | 
					            if (columns != null && !TextUtility.IsBlank(t)) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                var count = 0; | 
				
			||||
 | 
					                var names = new List<string>(); | 
				
			||||
 | 
					                var values = new List<string>(); | 
				
			||||
 | 
					                foreach (var column in columns) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    //names.Add(TextGenerator.Merge("[", column, "]"));
 | 
				
			||||
 | 
					                    names.Add(TextUtility.Merge(column)); | 
				
			||||
 | 
					                    values.Add("@" + column); | 
				
			||||
 | 
					                    count += 1; | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					                var sb = new StringBuilder(); | 
				
			||||
 | 
					                if (count > 0) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    sb.Append("insert into [", t, "](", TextUtility.Join(", ", names), ") "); | 
				
			||||
 | 
					                    sb.Append("values(", TextUtility.Join(", ", values), "); "); | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					                r = sb.ToString(); | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            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.EmptyString; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            return GenerateInsertStatement(t, cs); | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        private static string GenerateUpdateStatement(string table, string key, List<string> columns) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            var result = TextUtility.EmptyString; | 
				
			||||
 | 
					            var t = TextUtility.AntiInject(table, 255); | 
				
			||||
 | 
					            var k = TextUtility.AntiInject(key, 255); | 
				
			||||
 | 
					            if (columns != null && !TextUtility.IsBlank(t) && !TextUtility.IsBlank(k)) | 
				
			||||
 | 
					            { | 
				
			||||
 | 
					                var items = new List<string>(); | 
				
			||||
 | 
					                foreach (var column in columns) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    items.Add(TextUtility.Merge("[", column, "]=@", column)); | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					                if (items.Count > 0) | 
				
			||||
 | 
					                { | 
				
			||||
 | 
					                    result = TextUtility.Merge("update [", t, "] set ", TextUtility.Join(", ", items), " where [_key]='", k, "'; "); | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					            return result; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        /// <summary>生成 UPDATE 语句,键字段名为“_key”。表名必须有效,键值必须有效,无有效参数时将获取空结果。</summary>
 | 
				
			||||
 | 
					        /// <exception cref="System.ArgumentException"></exception>
 | 
				
			||||
 | 
					        /// <exception cref="System.ArgumentNullException"></exception>
 | 
				
			||||
 | 
					        public static string GenerateUpdateStatement(string table, string key, IEnumerable<IDataParameter> parameters) | 
				
			||||
 | 
					        { | 
				
			||||
 | 
					            if (table == null) throw new ArgumentNullException("argTable"); | 
				
			||||
 | 
					            var t = TextUtility.AntiInject(table, 255); | 
				
			||||
 | 
					            if (TextUtility.IsBlank(t)) throw new ArgumentException("表名无效。", nameof(table)); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            if (key == null) throw new ArgumentNullException("argKey"); | 
				
			||||
 | 
					            var k = TextUtility.AntiInject(key, 255); | 
				
			||||
 | 
					            if (TextUtility.IsBlank(k)) throw new ArgumentException("键值无效。", nameof(key)); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            var columns = GetParametersNames(parameters); | 
				
			||||
 | 
					            if (columns.Count < 1) return TextUtility.EmptyString; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					            return GenerateUpdateStatement(t, k, columns); | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        #endregion
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#endif
 | 
				
			||||
@ -1,34 +0,0 @@ | 
				
			|||||
using System; | 
					 | 
				
			||||
using System.Collections.Generic; | 
					 | 
				
			||||
using System.Text; | 
					 | 
				
			||||
 | 
					 | 
				
			||||
namespace Apewer.Web | 
					 | 
				
			||||
{ | 
					 | 
				
			||||
 | 
					 | 
				
			||||
    /// <summary></summary>
 | 
					 | 
				
			||||
    [Serializable] | 
					 | 
				
			||||
    public sealed class ApiMime | 
					 | 
				
			||||
    { | 
					 | 
				
			||||
 | 
					 | 
				
			||||
        /// <summary></summary>
 | 
					 | 
				
			||||
        public string Extension { get; set; } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
        /// <summary></summary>
 | 
					 | 
				
			||||
        public string ContentType { get; set; } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
        /// <summary>缓存过期时间,单位为秒,默认值 0 为不缓存(立即过期)。</summary>
 | 
					 | 
				
			||||
        public int Expires { get; set; } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
        /// <summary></summary>
 | 
					 | 
				
			||||
        public ApiMime() { } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
        internal ApiMime(string ext, string type, int expires) | 
					 | 
				
			||||
        { | 
					 | 
				
			||||
            Extension = ext; | 
					 | 
				
			||||
            ContentType = type; | 
					 | 
				
			||||
            Expires = expires; | 
					 | 
				
			||||
        } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
    } | 
					 | 
				
			||||
 | 
					 | 
				
			||||
} | 
					 | 
				
			||||
@ -0,0 +1,28 @@ | 
				
			|||||
 | 
					<Project> | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						<!-- 生成 --> | 
				
			||||
 | 
						<PropertyGroup> | 
				
			||||
 | 
							<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | 
				
			||||
 | 
							<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile> | 
				
			||||
 | 
							<LangVersion>latest</LangVersion> | 
				
			||||
 | 
							<NoWarn>CS0108,CS0162,CS0414,CS0612,CS0618,CS0649,CS1589,CS1570,CS1572,CS1573,CS3019,CS3021</NoWarn> | 
				
			||||
 | 
						</PropertyGroup> | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						<!-- 程序集信息 --> | 
				
			||||
 | 
						<PropertyGroup> | 
				
			||||
 | 
							<Product>Apewer Libraries</Product> | 
				
			||||
 | 
							<Version>6.3.4</Version> | 
				
			||||
 | 
						</PropertyGroup> | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						<!-- NuGet --> | 
				
			||||
 | 
						<PropertyGroup Condition="'$(Configuration)'=='Release'"> | 
				
			||||
 | 
							<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | 
				
			||||
 | 
							<IsPackable>true</IsPackable> | 
				
			||||
 | 
						</PropertyGroup> | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						<!-- Debug --> | 
				
			||||
 | 
						<PropertyGroup Condition="'$(Configuration)'=='Debug'"> | 
				
			||||
 | 
							<DefineConstants>DEBUG;TRACE;$(DefineConstants);$(AdditionalConstants)</DefineConstants> | 
				
			||||
 | 
						</PropertyGroup> | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					</Project> | 
				
			||||
@ -1,6 +1,16 @@ | 
				
			|||||
 | 
					 | 
				
			||||
### 最新提交 | 
					### 最新提交 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					### 6.3.4 | 
				
			||||
 | 
					- Logger:支持指定文件路径; | 
				
			||||
 | 
					- Models:StringPairs 支持通过构造函数设置初始容量; | 
				
			||||
 | 
					- Source:去除 Attribute 的 Lock 方法,不再锁定属性; | 
				
			||||
 | 
					- Source:增加 ORM 接口,统一方法; | 
				
			||||
 | 
					- Web:增加 HTTP 头,应对 Edge 浏览器的检查; | 
				
			||||
 | 
					- Web:新增 Response.Binary 重载,支持指定文件路径; | 
				
			||||
 | 
					- Web:ApiOptions 支持设置 X-Content-Type-Options; | 
				
			||||
 | 
					- Web:Staitc 控制器对部分纯文本文件的 Content-Type 加入“utf-8”。 | 
				
			||||
 | 
					
 | 
				
			||||
### 6.3.3 | 
					### 6.3.3 | 
				
			||||
- 全局:恢复 .NET Standard 2.0 的输出,用于支持 .NET Framework 4.6.1; | 
					- 全局:恢复 .NET Standard 2.0 的输出,用于支持 .NET Framework 4.6.1; | 
				
			||||
- StorageUtility:优化了 ReadFile 的内存占用; | 
					- StorageUtility:优化了 ReadFile 的内存占用; | 
				
			||||
					Loading…
					
					
				
		Reference in new issue