DataAccess.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. using Dapper;
  2. using Mono.Data.Sqlite;
  3. using Rac.Entities;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Data;
  7. using System.Data.Common;
  8. using System.IO;
  9. using System.Linq;
  10. namespace Rac.Tools
  11. {
  12. internal class DataAccess
  13. {
  14. private readonly string _dbFilePath;
  15. public DataAccess(string dbFilePath)
  16. {
  17. _dbFilePath = dbFilePath;
  18. CreateDatabaseIfNotExist();
  19. }
  20. private void CreateDatabaseIfNotExist()
  21. {
  22. if (File.Exists(_dbFilePath)) return;
  23. using (var conn = GetConnection())
  24. {
  25. conn.Execute(
  26. //Create table:Config
  27. "CREATE TABLE Configs("
  28. + $"{nameof(ConfigEntry.Key)} VARCHAR(16) PRIMARY KEY,"
  29. + $"{nameof(ConfigEntry.Value)} VARCHAR(512)"
  30. + ");" +
  31. //Create table:Archive
  32. "CREATE TABLE Archives("
  33. + $"{nameof(ArchiveEntry.Url)} VARCHAR(512) PRIMARY KEY,"
  34. + $"{nameof(ArchiveEntry.LastWriteTime)} DATETIME,"
  35. + $"{nameof(ArchiveEntry.StatusCode)} INT,"
  36. + $"{nameof(ArchiveEntry.StatusDescription)} VARCHAR(128),"
  37. + $"{nameof(ArchiveEntry.Headers)} VARCHAR(512),"
  38. + $"{nameof(ArchiveEntry.Content)} BLOB"
  39. + ");" +
  40. //Create table:PageLinks
  41. "CREATE TABLE ResourceLinks("
  42. + $"{nameof(ResourceLinkEntry.Resource)} VARCHAR(512),"
  43. + $"{nameof(ResourceLinkEntry.Link)} VARCHAR(512),"
  44. + $"{nameof(ResourceLinkEntry.Included)} BOOLEAN,"
  45. + $"{nameof(ResourceLinkEntry.Remark)} VARCHAR(512),"
  46. + $"PRIMARY KEY ({nameof(ResourceLinkEntry.Resource)},{nameof(ResourceLinkEntry.Link)})"
  47. + ");"
  48. //Init Data
  49. + $"INSERT INTO Configs ({nameof(ConfigEntry.Key)}) VALUES ('{nameof(ConfigAdapter.HomeUrl)}');"
  50. + $"INSERT INTO Configs ({nameof(ConfigEntry.Key)}) VALUES ('{nameof(ConfigAdapter.HostsInclude)}');"
  51. + $"INSERT INTO Configs ({nameof(ConfigEntry.Key)}) VALUES ('{nameof(ConfigAdapter.UrlPrefixExclude)}');"
  52. + $"INSERT INTO Configs ({nameof(ConfigEntry.Key)}) VALUES ('{nameof(ConfigAdapter.OwsPort)}');"
  53. + $"INSERT INTO Configs ({nameof(ConfigEntry.Key)}) VALUES ('{nameof(ConfigAdapter.Parallel)}');"
  54. + $"INSERT INTO Configs ({nameof(ConfigEntry.Key)}) VALUES ('{nameof(ConfigAdapter.DefaultCharset)}');"
  55. );
  56. }
  57. }
  58. private int BulkOperation<T>(IEnumerable<T> items, Func<T, DbConnection, int> proc)
  59. {
  60. var count = 0;
  61. using (var conn = GetConnection())
  62. {
  63. using (var t = conn.BeginTransaction())
  64. {
  65. count += items.Sum(p => proc(p, conn));
  66. t.Commit();
  67. }
  68. }
  69. return count;
  70. }
  71. private DbConnection GetConnection()
  72. {
  73. var conn = new SqliteConnection("Data Source=" + _dbFilePath);
  74. conn.Open();
  75. return conn;
  76. }
  77. // --- common ---
  78. public Dictionary<string, string> GetConfigs()
  79. {
  80. using (var conn = GetConnection())
  81. return conn.Query<ConfigEntry>("select * from Configs")
  82. .ToDictionary(p => p.Key, p => p.Value);
  83. }
  84. // --- for crawler ---
  85. public string[] GetNonDumpedUrls(int num)
  86. {
  87. using (var conn = GetConnection())
  88. return conn.Query<ArchiveEntry>($"select {nameof(ArchiveEntry.Url)} from Archives WHERE {nameof(ArchiveEntry.StatusCode)} IS NULL LIMIT @num", new { num })
  89. .Select(p => p.Url).ToArray();
  90. }
  91. public int CreateArchiveEntryIfNotExist(string url, DbConnection conn = null)
  92. {
  93. int Run(IDbConnection con) => 1 != con.ExecuteScalar<int>($"SELECT 1 FROM Archives WHERE {nameof(ArchiveEntry.Url)}=@url", new { url })
  94. ? con.Execute($"INSERT INTO Archives ({nameof(ArchiveEntry.Url)},{nameof(ArchiveEntry.LastWriteTime)}) VALUES(@url,@now)", new { url, now = DateTime.Now })
  95. : 0;
  96. if (null != conn)
  97. return Run(conn);
  98. using (conn = GetConnection())
  99. return Run(conn);
  100. }
  101. public int UpdateArchiveEntity(ArchiveEntry entryToUpdate, DbConnection conn = null)
  102. {
  103. entryToUpdate.LastWriteTime = DateTime.Now;
  104. int Run(IDbConnection con) => con.Execute(
  105. "UPDATE Archives SET "
  106. + $"{nameof(ArchiveEntry.LastWriteTime)}=@{nameof(ArchiveEntry.LastWriteTime)},"
  107. + $"{nameof(ArchiveEntry.StatusCode)}=@{nameof(ArchiveEntry.StatusCode)},"
  108. + $"{nameof(ArchiveEntry.StatusDescription)}=@{nameof(ArchiveEntry.StatusDescription)},"
  109. + $"{nameof(ArchiveEntry.Headers)}=@{nameof(ArchiveEntry.Headers)},"
  110. + $"{nameof(ArchiveEntry.Content)}=@{nameof(ArchiveEntry.Content)} " +
  111. $"WHERE {nameof(ArchiveEntry.Url)}=@{nameof(ArchiveEntry.Url)}", entryToUpdate);
  112. if (null != conn)
  113. return Run(conn);
  114. using (conn = GetConnection())
  115. return Run(conn);
  116. }
  117. public int AddPageLink(ResourceLinkEntry entry, DbConnection conn = null)
  118. {
  119. int Run(IDbConnection con)
  120. {
  121. if (1 != con.ExecuteScalar<int>(
  122. "SELECT 1 FROM ResourceLinks WHERE "
  123. + $"{nameof(ResourceLinkEntry.Resource)}=@{nameof(ResourceLinkEntry.Resource)} "
  124. + $"AND {nameof(ResourceLinkEntry.Link)}=@{nameof(ResourceLinkEntry.Link)}"
  125. , entry))
  126. return con.Execute(
  127. "INSERT INTO ResourceLinks ("
  128. + $"{nameof(ResourceLinkEntry.Resource)},"
  129. + $"{nameof(ResourceLinkEntry.Link)},"
  130. + $"{nameof(ResourceLinkEntry.Included)},"
  131. + $"{nameof(ResourceLinkEntry.Remark)}" +
  132. ") VALUES ("
  133. + $"@{nameof(ResourceLinkEntry.Resource)},"
  134. + $"@{nameof(ResourceLinkEntry.Link)},"
  135. + $"@{nameof(ResourceLinkEntry.Included)},"
  136. + $"@{nameof(ResourceLinkEntry.Remark)}" +
  137. ")", entry);
  138. return 0;
  139. }
  140. if (null != conn)
  141. return Run(conn);
  142. using (conn = GetConnection())
  143. return Run(conn);
  144. }
  145. public int BulkAddNewArchiveEntry(IEnumerable<string> urls)
  146. {
  147. return BulkOperation(urls, CreateArchiveEntryIfNotExist);
  148. }
  149. public int BulkUpdateArchiveEntry(IEnumerable<ArchiveEntry> items)
  150. {
  151. return BulkOperation(items, UpdateArchiveEntity);
  152. }
  153. public int BulkAddResourceLink(string page, bool included, string remark, params string[] links)
  154. {
  155. return BulkOperation(links.Select(p => new ResourceLinkEntry { Resource = page, Link = p, Included = included, Remark = remark }), AddPageLink);
  156. }
  157. public int BulkAddResourceLink(IEnumerable<ResourceLinkEntry> items)
  158. {
  159. return BulkOperation(items, AddPageLink);
  160. }
  161. public string GetReferer(string url)
  162. {
  163. using (var conn = GetConnection())
  164. {
  165. return conn.Query<string>($"SELECT Resource FROM ResourceLinks WHERE {nameof(ResourceLinkEntry.Link)}=@url", new { url }).FirstOrDefault();
  166. }
  167. }
  168. // --- for offline server ---
  169. public ArchiveEntry GetEntry(string url)
  170. {
  171. using (var conn = GetConnection())
  172. {
  173. return conn.Query<ArchiveEntry>($"SELECT * FROM Archives WHERE {nameof(ArchiveEntry.Url)}=@url", new { url }).FirstOrDefault();
  174. }
  175. }
  176. // --- for file browser ---
  177. public string[] GetAllUrl()
  178. {
  179. using (var conn = GetConnection())
  180. {
  181. return conn.Query<string>($"SELECT {nameof(ArchiveEntry.Url)} FROM Archives").ToArray();
  182. }
  183. }
  184. }
  185. }