You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

339 lines
10 KiB

4 years ago
4 years ago
4 years ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data;
  4. using System.Data.Common;
  5. using System.Text;
  6. namespace Apewer.Source
  7. {
  8. /// <summary></summary>
  9. abstract class DbClient
  10. {
  11. /// <summary></summary>
  12. public virtual Logger Logger { get; set; }
  13. #region Connection
  14. DbConnection _conn = null;
  15. string _str = null;
  16. /// <summary></summary>
  17. public Timeout Timeout { get; set; }
  18. /// <summary></summary>
  19. public DbConnection Connection { get => _conn; }
  20. /// <summary></summary>
  21. public bool Online { get => _conn == null ? false : (_conn.State == ConnectionState.Open); }
  22. /// <summary>连接字符串。</summary>
  23. public string ConnectionString { get => _str; }
  24. /// <summary></summary>
  25. public virtual string Connect()
  26. {
  27. if (_conn == null)
  28. {
  29. _str = GetConnectionString();
  30. _conn = NewConnection();
  31. _conn.ConnectionString = _str;
  32. }
  33. else
  34. {
  35. if (_conn.State == ConnectionState.Open) return null;
  36. }
  37. try
  38. {
  39. _conn.Open();
  40. switch (_conn.State)
  41. {
  42. case ConnectionState.Open: return null;
  43. default: return $"连接失败,当前处于 {_conn.State} 状态。";
  44. }
  45. }
  46. catch (Exception ex)
  47. {
  48. Logger.Error(this, "Connect", ex, _conn.ConnectionString);
  49. Close();
  50. return ex.Message;
  51. }
  52. }
  53. /// <summary></summary>
  54. public void Close()
  55. {
  56. if (_conn != null)
  57. {
  58. if (_transaction != null)
  59. {
  60. if (_autocommit) Commit();
  61. else Rollback();
  62. }
  63. _conn.Close();
  64. _conn.Dispose();
  65. _conn = null;
  66. }
  67. }
  68. /// <summary></summary>
  69. public void Dispose() { Close(); }
  70. #endregion
  71. #region Transaction
  72. private DbTransaction _transaction = null;
  73. private bool _autocommit = false;
  74. /// <summary>启动事务。</summary>
  75. public string Begin(bool commit = true) => Begin(commit, null);
  76. /// <summary>启动事务。</summary>
  77. public string Begin(bool commit, Class<System.Data.IsolationLevel> isolation)
  78. {
  79. if (Connect() != null) return "未连接。";
  80. if (_transaction != null) return "存在已启动的事务,无法再次启动。";
  81. try
  82. {
  83. _transaction = isolation ? _conn.BeginTransaction(isolation.Value) : _conn.BeginTransaction();
  84. _autocommit = commit;
  85. return null;
  86. }
  87. catch (Exception ex)
  88. {
  89. Logger.Error(this, "Commit", ex.Message());
  90. return ex.Message();
  91. }
  92. }
  93. /// <summary>提交事务。</summary>
  94. public string Commit()
  95. {
  96. if (_transaction == null) return "事务不存在。";
  97. try
  98. {
  99. _transaction.Commit();
  100. RuntimeUtility.Dispose(_transaction);
  101. _transaction = null;
  102. return null;
  103. }
  104. catch (Exception ex)
  105. {
  106. RuntimeUtility.Dispose(_transaction);
  107. _transaction = null;
  108. Logger.Error(this, "Commit", ex.Message());
  109. return ex.Message();
  110. }
  111. }
  112. /// <summary>从挂起状态回滚事务。</summary>
  113. public string Rollback()
  114. {
  115. if (_transaction == null) return "事务不存在。";
  116. try
  117. {
  118. _transaction.Rollback();
  119. RuntimeUtility.Dispose(_transaction);
  120. _transaction = null;
  121. return null;
  122. }
  123. catch (Exception ex)
  124. {
  125. RuntimeUtility.Dispose(_transaction);
  126. _transaction = null;
  127. Logger.Error(this, "Rollback", ex.Message());
  128. return ex.Message();
  129. }
  130. }
  131. #endregion
  132. #region ADO
  133. /// <summary>查询。</summary>
  134. public IQuery Query(string sql) => Query(sql, null);
  135. /// <summary>查询。</summary>
  136. public IQuery Query(string sql, IEnumerable<IDataParameter> parameters)
  137. {
  138. if (TextUtility.IsBlank(sql)) return new Query(false, "语句无效。");
  139. var connected = Connect();
  140. if (connected.NotEmpty()) return new Query(false, connected);
  141. try
  142. {
  143. using (var command = NewCommand())
  144. {
  145. command.Connection = _conn;
  146. if (Timeout != null) command.CommandTimeout = Timeout.Query;
  147. command.CommandText = sql;
  148. if (parameters != null)
  149. {
  150. foreach (var parameter in parameters)
  151. {
  152. if (parameter != null) command.Parameters.Add(parameter);
  153. }
  154. }
  155. using (var ds = new DataSet())
  156. {
  157. using (var da = NewDataAdapter(sql))
  158. {
  159. var name = "table_" + Guid.NewGuid().ToString("n");
  160. da.Fill(ds, name);
  161. var table = ds.Tables[name];
  162. return new Query(table, true);
  163. }
  164. }
  165. }
  166. }
  167. catch (Exception exception)
  168. {
  169. Logger.Error(this, "Query", exception, sql);
  170. return new Query(exception);
  171. }
  172. }
  173. /// <summary>执行。</summary>
  174. public IExecute Execute(string sql) => Execute(sql, null);
  175. /// <summary>执行单条 Transact-SQL 语句,并加入参数。</summary>
  176. public IExecute Execute(string sql, IEnumerable<IDataParameter> parameters)
  177. {
  178. if (TextUtility.IsBlank(sql)) return Example.InvalidExecuteStatement;
  179. var connected = Connect();
  180. if (connected.NotEmpty()) return new Execute(false, connected);
  181. var inTransaction = _transaction != null;
  182. if (!inTransaction) Begin();
  183. try
  184. {
  185. using (var command = NewCommand())
  186. {
  187. command.Connection = _conn;
  188. command.Transaction = (DbTransaction)_transaction;
  189. if (Timeout != null) command.CommandTimeout = Timeout.Execute;
  190. command.CommandText = sql;
  191. if (parameters != null)
  192. {
  193. foreach (var parameter in parameters)
  194. {
  195. if (parameter != null) command.Parameters.Add(parameter);
  196. }
  197. }
  198. var rows = command.ExecuteNonQuery();
  199. if (!inTransaction) Commit(); // todo 此处应该检查事务提交产生的错误。
  200. return new Execute(true, rows);
  201. }
  202. }
  203. catch (Exception exception)
  204. {
  205. Logger.Error(this, "Execute", exception, sql);
  206. if (!inTransaction) Rollback();
  207. return new Execute(exception);
  208. }
  209. }
  210. #endregion
  211. #region ORM - Query
  212. /// <summary>查询记录。</summary>
  213. /// <param name="model">记录模型。</param>
  214. /// <param name="sql">SQL 语句。</param>
  215. public Result<object[]> Query(Type model, string sql)
  216. {
  217. if (_conn == null) return new Result<object[]>("连接无效。");
  218. if (model == null) return new Result<object[]>("数据模型类型无效。");
  219. if (string.IsNullOrEmpty(sql)) return new Result<object[]>("SQL 语句无效。");
  220. var query = Query(sql);
  221. var result = null as Result<object[]>;
  222. if (query.Success)
  223. {
  224. try
  225. {
  226. var array = OrmHelper.Fill(query, model);
  227. result = new Result<object[]>(array);
  228. }
  229. catch (Exception ex)
  230. {
  231. result = new Result<object[]>(ex);
  232. }
  233. }
  234. else
  235. {
  236. result = new Result<object[]>(query.Message);
  237. }
  238. query.Dispose();
  239. return result;
  240. }
  241. /// <summary></summary>
  242. public Result<T[]> Query<T>(string sql) where T : class, new()
  243. {
  244. var query = Query(sql);
  245. if (!query.Success) return new Result<T[]>(query.Message);
  246. var records = query.Fill<T>();
  247. query.Dispose();
  248. var result = new Result<T[]>(records);
  249. return result;
  250. }
  251. #endregion
  252. #region Static
  253. /// <summary>对文本转义,符合 SQL 安全性。可根据字段类型限制 UTF-8 字节数,默认为 0 时不限制字节数。</summary>
  254. protected static string Escape(string text, int bytes = 0)
  255. {
  256. if (text.IsEmpty()) return "";
  257. var t = text ?? "";
  258. t = t.Replace("\\", "\\\\");
  259. t = t.Replace("'", "\\'");
  260. t = t.Replace("\n", "\\n");
  261. t = t.Replace("\r", "\\r");
  262. t = t.Replace("\b", "\\b");
  263. t = t.Replace("\t", "\\t");
  264. t = t.Replace("\f", "\\f");
  265. if (bytes > 5)
  266. {
  267. if (t.Bytes(Encoding.UTF8).Length > bytes)
  268. {
  269. while (true)
  270. {
  271. t = t.Substring(0, t.Length - 1);
  272. if (t.Bytes(Encoding.UTF8).Length <= (bytes - 4)) break;
  273. }
  274. t = t + " ...";
  275. }
  276. }
  277. return t;
  278. }
  279. #endregion
  280. /// <summary></summary>
  281. protected abstract string GetConnectionString();
  282. /// <summary></summary>
  283. protected abstract DbConnection NewConnection();
  284. /// <summary></summary>
  285. protected abstract DbDataAdapter NewDataAdapter(string sql);
  286. /// <summary></summary>
  287. protected abstract DbCommand NewCommand();
  288. }
  289. }