BaseDbMng.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. using Dapper;
  2. using SharpCompress.Archives;
  3. using SharpCompress.Common;
  4. using SharpCompress.Writers;
  5. using System;
  6. using System.Data;
  7. using System.IO;
  8. using System.Text.RegularExpressions;
  9. using Vit.Core.Module.Log;
  10. using Vit.Extensions;
  11. using Vit.Orm.Dapper;
  12. namespace Vit.Db.DbMng
  13. {
  14. public abstract class BaseDbMng<DbConnection>
  15. where DbConnection : IDbConnection
  16. {
  17. protected DbConnection conn;
  18. public BaseDbMng(DbConnection conn)
  19. {
  20. this.conn = conn;
  21. }
  22. /// <summary>
  23. /// 获取数据库状态
  24. /// </summary>
  25. /// <returns></returns>
  26. public abstract EDataBaseState GetDataBaseState();
  27. /// <summary>
  28. /// 创建数据库
  29. /// </summary>
  30. public abstract void CreateDataBase();
  31. /// <summary>
  32. /// 删除数据库
  33. /// </summary>
  34. public abstract void DropDataBase();
  35. /// <summary>
  36. /// 数据库是否存在
  37. /// </summary>
  38. /// <returns></returns>
  39. public virtual bool DataBaseIsOnline()
  40. {
  41. return GetDataBaseState() == EDataBaseState.online;
  42. }
  43. /// <summary>
  44. /// 构建建库语句
  45. /// </summary>
  46. /// <returns></returns>
  47. public abstract string BuildCreateDataBaseSql();
  48. protected abstract string Quote(string name);
  49. #region BackupSqler
  50. /// <summary>
  51. /// 备份数据库
  52. /// </summary>
  53. /// <param name="filePath">备份的文件路径。demo:@"F:\\website\appdata\dbname_2020-02-02_121212.bak"</param>
  54. /// <returns>备份的文件路径</returns>
  55. public virtual string BackupSqler(string filePath)
  56. {
  57. var tempPath = filePath + "_Temp";
  58. try
  59. {
  60. //(x.1)创建临时文件夹
  61. Directory.CreateDirectory(tempPath);
  62. #region (x.2)构建备份文件
  63. #region (x.x.1)创建建库语句文件(CreateDataBase.sql)
  64. var sqlPath = Path.Combine(tempPath, "CreateDataBase.sql");
  65. var sqlText = BuildCreateDataBaseSql();
  66. File.WriteAllText(sqlPath, sqlText, System.Text.Encoding.GetEncoding("utf-8"));
  67. #endregion
  68. #region (x.x.2)获取所有表数据(Data.sqlite3)
  69. string sqlitePath = Path.Combine(tempPath, "Data.sqlite3");
  70. #region backup to sqlite
  71. //using (var conn = ConnectionFactory.MySql_GetOpenConnection(sqlConnectionString))
  72. using (var connSqlite = ConnectionFactory.Sqlite_GetOpenConnectionByFilePath(sqlitePath))
  73. {
  74. //Logger.Info(" mysql backup");
  75. //Logger.Info(" backup database " + conn.Database);
  76. var tableNames = conn.GetAllTableName();
  77. int tbCount = 0;
  78. int sumRowCount = 0;
  79. foreach (var tableName in tableNames)
  80. {
  81. tbCount++;
  82. //try
  83. //{
  84. //Logger.Info($" [{tbCount}/{tableNames.Count}]start backup table " + tableName);
  85. int rowCount;
  86. using (IDataReader dr = conn.ExecuteReader($"select * from {Quote(tableName)}"))
  87. {
  88. //(x.x.1)create table
  89. //Logger.Info(" [x.x.1]create table " + tableName);
  90. connSqlite.Sqlite_CreateTable(dr, tableName);
  91. //(x.x.2)import table
  92. //Logger.Info(" [x.x.2]import table " + tableName + " start...");
  93. rowCount = connSqlite.Import(dr, tableName);
  94. }
  95. sumRowCount += rowCount;
  96. //Logger.Info(" import table " + tableName + " success,row count:" + rowCount);
  97. //}
  98. //catch (Exception ex)
  99. //{
  100. // Logger.Error(ex);
  101. //}
  102. }
  103. //var span = (DateTime.Now - startTime);
  104. //Logger.Info(" mysql backup success,sum row count:" + sumRowCount + $",耗时:{span.Hours}小时{span.Minutes}分{span.Seconds}秒{span.Milliseconds}毫秒");
  105. }
  106. #endregion
  107. #endregion
  108. #endregion
  109. #region (x.3)压缩备份文件
  110. //待压缩文件夹
  111. string input = tempPath;
  112. //压缩后文件名
  113. string output = filePath;
  114. var archiveType = ArchiveType.Zip;
  115. var compressionType = SharpCompress.Common.CompressionType.Deflate;
  116. var writerOptions = new WriterOptions(compressionType);
  117. writerOptions.ArchiveEncoding.Default = System.Text.Encoding.GetEncoding("utf-8");
  118. using (var fileStream = File.OpenWrite(output))
  119. using (var writer = WriterFactory.Open(fileStream, archiveType, writerOptions))
  120. {
  121. writer.WriteAll(input, "*", SearchOption.AllDirectories);
  122. }
  123. #endregion
  124. }
  125. finally
  126. {
  127. Directory.Delete(tempPath, true);
  128. }
  129. return filePath;
  130. }
  131. #endregion
  132. #region RestoreSqler
  133. protected virtual Regex RestoreSqler_SqlSplit => null;
  134. /// <summary>
  135. /// 远程还原数据库
  136. /// </summary>
  137. /// <param name="filePath">数据库备份文件的路径</param>
  138. /// <returns>备份文件的路径</returns>
  139. public virtual string RestoreSqler(string filePath)
  140. {
  141. var tempPath = filePath + "_Temp";
  142. try
  143. {
  144. //(x.1)创建临时文件夹
  145. Directory.CreateDirectory(tempPath);
  146. #region (x.1)解压备份文件到临时文件夹
  147. //待解压文件
  148. var input = filePath;
  149. var output = tempPath;
  150. using (var archive = ArchiveFactory.Open(input))
  151. {
  152. foreach (var entry in archive.Entries)
  153. {
  154. entry.WriteToDirectory(output, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true });
  155. }
  156. }
  157. #endregion
  158. #region (x.2)还原数据库
  159. //(x.x.1)若数据库存在,则删除数据库
  160. if (DataBaseIsOnline()) DropDataBase();
  161. //创建数据库
  162. CreateDataBase();
  163. #region (x.x.2)创建建库语句文件(CreateDataBase.sql)
  164. var sqlPath = Path.Combine(tempPath, "CreateDataBase.sql");
  165. var sqlText = File.ReadAllText(sqlPath, System.Text.Encoding.GetEncoding("utf-8"));
  166. Action runSql = () => {
  167. using (var tran = conn.BeginTransaction())
  168. {
  169. try
  170. {
  171. int index = 1;
  172. Regex reg = RestoreSqler_SqlSplit;
  173. if (reg == null)
  174. {
  175. conn.Execute(sqlText, transaction: tran);
  176. }
  177. else
  178. {
  179. var sqls = reg.Split(sqlText);
  180. foreach (String sql in sqls)
  181. {
  182. if (String.IsNullOrEmpty(sql.Trim()))
  183. {
  184. //sendMsg(EMsgType.Title, $"[{(index++)}/{sqls.Length}]空语句,无需执行.");
  185. }
  186. else
  187. {
  188. conn.Execute(sql,transaction: tran);
  189. //sendMsg(EMsgType.Title, $"[{(index++)}/{sqls.Length}]执行sql语句:");
  190. //sendMsg(EMsgType.Nomal, sql);
  191. //var result = "执行结果:" + conn.Execute(sql, null, tran) + " Lines effected.";
  192. //sendMsg(EMsgType.Title, result);
  193. }
  194. }
  195. }
  196. tran.Commit();
  197. }
  198. catch (Exception ex)
  199. {
  200. Logger.Error(ex);
  201. tran.Rollback();
  202. throw;
  203. }
  204. }
  205. };
  206. //确保conn打开
  207. if (conn.State == ConnectionState.Open)
  208. {
  209. runSql();
  210. }
  211. else
  212. {
  213. try
  214. {
  215. conn.Open();
  216. runSql();
  217. }
  218. finally
  219. {
  220. conn.Close();
  221. }
  222. }
  223. #endregion
  224. #region (x.x.3)导入所有表数据(Data.sqlite3)
  225. string sqlitePath = Path.Combine(tempPath, "Data.sqlite3");
  226. //using (var conn = ConnectionFactory.MySql_GetOpenConnection(sqlConnectionString))
  227. using (var connSqlite = ConnectionFactory.Sqlite_GetOpenConnectionByFilePath(sqlitePath))
  228. {
  229. var tableNames = connSqlite.Sqlite_GetAllTableName();
  230. int tbCount = 0;
  231. int sumRowCount = 0;
  232. foreach (var tableName in tableNames)
  233. {
  234. tbCount++;
  235. //Logger.Info($" [{tbCount}/{tableNames.Count}]start import table " + tableName);
  236. //get data
  237. using (var dr = connSqlite.ExecuteReader($"select * from {connSqlite.Quote(tableName)}"))
  238. {
  239. //(x.4)
  240. //Logger.Info(" [x.x.4]import table " + dt.TableName + ",row count:" + dt.Rows.Count);
  241. var rowCount = conn.BulkImport(dr, tableName);
  242. sumRowCount += rowCount;
  243. //Logger.Info(" import table " + dt.TableName + " success");
  244. }
  245. }
  246. //var span = (DateTime.Now - startTime);
  247. //Logger.Info(" mysql import success,sum row count:" + sumRowCount + $",耗时:{span.Hours}小时{span.Minutes}分{span.Seconds}秒{span.Milliseconds}毫秒");
  248. }
  249. #endregion
  250. #endregion
  251. }
  252. finally
  253. {
  254. Directory.Delete(tempPath, true);
  255. }
  256. return filePath;
  257. }
  258. #endregion
  259. }
  260. }