SyncRepository.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. using MediaBrowser.Controller;
  2. using MediaBrowser.Controller.Sync;
  3. using MediaBrowser.Model.Logging;
  4. using MediaBrowser.Model.Querying;
  5. using MediaBrowser.Model.Sync;
  6. using MediaBrowser.Server.Implementations.Persistence;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Data;
  10. using System.Globalization;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Threading;
  14. using System.Threading.Tasks;
  15. namespace MediaBrowser.Server.Implementations.Sync
  16. {
  17. public class SyncRepository : ISyncRepository, IDisposable
  18. {
  19. private IDbConnection _connection;
  20. private readonly ILogger _logger;
  21. private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1);
  22. private readonly IServerApplicationPaths _appPaths;
  23. private readonly CultureInfo _usCulture = new CultureInfo("en-US");
  24. private IDbCommand _deleteJobCommand;
  25. private IDbCommand _deleteJobItemsCommand;
  26. private IDbCommand _saveJobCommand;
  27. private IDbCommand _saveJobItemCommand;
  28. public SyncRepository(ILogger logger, IServerApplicationPaths appPaths)
  29. {
  30. _logger = logger;
  31. _appPaths = appPaths;
  32. }
  33. public async Task Initialize()
  34. {
  35. var dbFile = Path.Combine(_appPaths.DataPath, "sync10.db");
  36. _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
  37. string[] queries = {
  38. "create table if not exists SyncJobs (Id GUID PRIMARY KEY, TargetId TEXT NOT NULL, Name TEXT NOT NULL, Quality TEXT NOT NULL, Status TEXT NOT NULL, Progress FLOAT, UserId TEXT NOT NULL, ItemIds TEXT NOT NULL, Category TEXT, ParentId TEXT, UnwatchedOnly BIT, ItemLimit INT, SyncNewContent BIT, DateCreated DateTime, DateLastModified DateTime, ItemCount int)",
  39. "create index if not exists idx_SyncJobs on SyncJobs(Id)",
  40. "create table if not exists SyncJobItems (Id GUID PRIMARY KEY, ItemId TEXT, ItemName TEXT, MediaSourceId TEXT, JobId TEXT, OutputPath TEXT, Status TEXT, TargetId TEXT, DateCreated DateTime, Progress FLOAT)",
  41. "create index if not exists idx_SyncJobItems on SyncJobs(Id)",
  42. //pragmas
  43. "pragma temp_store = memory",
  44. "pragma shrink_memory"
  45. };
  46. _connection.RunQueries(queries, _logger);
  47. PrepareStatements();
  48. }
  49. private void PrepareStatements()
  50. {
  51. _deleteJobCommand = _connection.CreateCommand();
  52. _deleteJobCommand.CommandText = "delete from SyncJobs where Id=@Id";
  53. _deleteJobCommand.Parameters.Add(_deleteJobCommand, "@Id");
  54. _deleteJobItemsCommand = _connection.CreateCommand();
  55. _deleteJobItemsCommand.CommandText = "delete from SyncJobItems where JobId=@JobId";
  56. _deleteJobItemsCommand.Parameters.Add(_deleteJobItemsCommand, "@JobId");
  57. _saveJobCommand = _connection.CreateCommand();
  58. _saveJobCommand.CommandText = "replace into SyncJobs (Id, TargetId, Name, Quality, Status, Progress, UserId, ItemIds, Category, ParentId, UnwatchedOnly, ItemLimit, SyncNewContent, DateCreated, DateLastModified, ItemCount) values (@Id, @TargetId, @Name, @Quality, @Status, @Progress, @UserId, @ItemIds, @Category, @ParentId, @UnwatchedOnly, @ItemLimit, @SyncNewContent, @DateCreated, @DateLastModified, @ItemCount)";
  59. _saveJobCommand.Parameters.Add(_saveJobCommand, "@Id");
  60. _saveJobCommand.Parameters.Add(_saveJobCommand, "@TargetId");
  61. _saveJobCommand.Parameters.Add(_saveJobCommand, "@Name");
  62. _saveJobCommand.Parameters.Add(_saveJobCommand, "@Quality");
  63. _saveJobCommand.Parameters.Add(_saveJobCommand, "@Status");
  64. _saveJobCommand.Parameters.Add(_saveJobCommand, "@Progress");
  65. _saveJobCommand.Parameters.Add(_saveJobCommand, "@UserId");
  66. _saveJobCommand.Parameters.Add(_saveJobCommand, "@ItemIds");
  67. _saveJobCommand.Parameters.Add(_saveJobCommand, "@Category");
  68. _saveJobCommand.Parameters.Add(_saveJobCommand, "@ParentId");
  69. _saveJobCommand.Parameters.Add(_saveJobCommand, "@UnwatchedOnly");
  70. _saveJobCommand.Parameters.Add(_saveJobCommand, "@ItemLimit");
  71. _saveJobCommand.Parameters.Add(_saveJobCommand, "@SyncNewContent");
  72. _saveJobCommand.Parameters.Add(_saveJobCommand, "@DateCreated");
  73. _saveJobCommand.Parameters.Add(_saveJobCommand, "@DateLastModified");
  74. _saveJobCommand.Parameters.Add(_saveJobCommand, "@ItemCount");
  75. _saveJobItemCommand = _connection.CreateCommand();
  76. _saveJobItemCommand.CommandText = "replace into SyncJobItems (Id, ItemId, ItemName, MediaSourceId, JobId, OutputPath, Status, TargetId, DateCreated, Progress) values (@Id, @ItemId, @ItemName, @MediaSourceId, @JobId, @OutputPath, @Status, @TargetId, @DateCreated, @Progress)";
  77. _saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@Id");
  78. _saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@ItemId");
  79. _saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@ItemName");
  80. _saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@MediaSourceId");
  81. _saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@JobId");
  82. _saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@OutputPath");
  83. _saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@Status");
  84. _saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@TargetId");
  85. _saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@DateCreated");
  86. _saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@Progress");
  87. }
  88. private const string BaseJobSelectText = "select Id, TargetId, Name, Quality, Status, Progress, UserId, ItemIds, Category, ParentId, UnwatchedOnly, ItemLimit, SyncNewContent, DateCreated, DateLastModified, ItemCount from SyncJobs";
  89. private const string BaseJobItemSelectText = "select Id, ItemId, ItemName, MediaSourceId, JobId, OutputPath, Status, TargetId, DateCreated, Progress from SyncJobItems";
  90. public SyncJob GetJob(string id)
  91. {
  92. if (string.IsNullOrEmpty(id))
  93. {
  94. throw new ArgumentNullException("id");
  95. }
  96. var guid = new Guid(id);
  97. if (guid == Guid.Empty)
  98. {
  99. throw new ArgumentNullException("id");
  100. }
  101. using (var cmd = _connection.CreateCommand())
  102. {
  103. cmd.CommandText = BaseJobSelectText + " where Id=@Id";
  104. cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid;
  105. using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
  106. {
  107. if (reader.Read())
  108. {
  109. return GetJob(reader);
  110. }
  111. }
  112. }
  113. return null;
  114. }
  115. private SyncJob GetJob(IDataReader reader)
  116. {
  117. var info = new SyncJob
  118. {
  119. Id = reader.GetGuid(0).ToString("N"),
  120. TargetId = reader.GetString(1),
  121. Name = reader.GetString(2)
  122. };
  123. if (!reader.IsDBNull(3))
  124. {
  125. info.Quality = (SyncQuality)Enum.Parse(typeof(SyncQuality), reader.GetString(3), true);
  126. }
  127. if (!reader.IsDBNull(4))
  128. {
  129. info.Status = (SyncJobStatus)Enum.Parse(typeof(SyncJobStatus), reader.GetString(4), true);
  130. }
  131. if (!reader.IsDBNull(5))
  132. {
  133. info.Progress = reader.GetDouble(5);
  134. }
  135. if (!reader.IsDBNull(6))
  136. {
  137. info.UserId = reader.GetString(6);
  138. }
  139. if (!reader.IsDBNull(7))
  140. {
  141. info.RequestedItemIds = reader.GetString(7).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
  142. }
  143. if (!reader.IsDBNull(8))
  144. {
  145. info.Category = (SyncCategory)Enum.Parse(typeof(SyncCategory), reader.GetString(8), true);
  146. }
  147. if (!reader.IsDBNull(9))
  148. {
  149. info.ParentId = reader.GetString(9);
  150. }
  151. if (!reader.IsDBNull(10))
  152. {
  153. info.UnwatchedOnly = reader.GetBoolean(10);
  154. }
  155. if (!reader.IsDBNull(11))
  156. {
  157. info.ItemLimit = reader.GetInt32(11);
  158. }
  159. info.SyncNewContent = reader.GetBoolean(12);
  160. info.DateCreated = reader.GetDateTime(13).ToUniversalTime();
  161. info.DateLastModified = reader.GetDateTime(14).ToUniversalTime();
  162. info.ItemCount = reader.GetInt32(15);
  163. return info;
  164. }
  165. public Task Create(SyncJob job)
  166. {
  167. return Update(job);
  168. }
  169. public async Task Update(SyncJob job)
  170. {
  171. if (job == null)
  172. {
  173. throw new ArgumentNullException("job");
  174. }
  175. await _writeLock.WaitAsync().ConfigureAwait(false);
  176. IDbTransaction transaction = null;
  177. try
  178. {
  179. transaction = _connection.BeginTransaction();
  180. var index = 0;
  181. _saveJobCommand.GetParameter(index++).Value = new Guid(job.Id);
  182. _saveJobCommand.GetParameter(index++).Value = job.TargetId;
  183. _saveJobCommand.GetParameter(index++).Value = job.Name;
  184. _saveJobCommand.GetParameter(index++).Value = job.Quality;
  185. _saveJobCommand.GetParameter(index++).Value = job.Status.ToString();
  186. _saveJobCommand.GetParameter(index++).Value = job.Progress;
  187. _saveJobCommand.GetParameter(index++).Value = job.UserId;
  188. _saveJobCommand.GetParameter(index++).Value = string.Join(",", job.RequestedItemIds.ToArray());
  189. _saveJobCommand.GetParameter(index++).Value = job.Category;
  190. _saveJobCommand.GetParameter(index++).Value = job.ParentId;
  191. _saveJobCommand.GetParameter(index++).Value = job.UnwatchedOnly;
  192. _saveJobCommand.GetParameter(index++).Value = job.ItemLimit;
  193. _saveJobCommand.GetParameter(index++).Value = job.SyncNewContent;
  194. _saveJobCommand.GetParameter(index++).Value = job.DateCreated;
  195. _saveJobCommand.GetParameter(index++).Value = job.DateLastModified;
  196. _saveJobCommand.GetParameter(index++).Value = job.ItemCount;
  197. _saveJobCommand.Transaction = transaction;
  198. _saveJobCommand.ExecuteNonQuery();
  199. transaction.Commit();
  200. }
  201. catch (OperationCanceledException)
  202. {
  203. if (transaction != null)
  204. {
  205. transaction.Rollback();
  206. }
  207. throw;
  208. }
  209. catch (Exception e)
  210. {
  211. _logger.ErrorException("Failed to save record:", e);
  212. if (transaction != null)
  213. {
  214. transaction.Rollback();
  215. }
  216. throw;
  217. }
  218. finally
  219. {
  220. if (transaction != null)
  221. {
  222. transaction.Dispose();
  223. }
  224. _writeLock.Release();
  225. }
  226. }
  227. public async Task DeleteJob(string id)
  228. {
  229. if (string.IsNullOrWhiteSpace(id))
  230. {
  231. throw new ArgumentNullException("id");
  232. }
  233. await _writeLock.WaitAsync().ConfigureAwait(false);
  234. IDbTransaction transaction = null;
  235. try
  236. {
  237. transaction = _connection.BeginTransaction();
  238. var index = 0;
  239. _deleteJobCommand.GetParameter(index++).Value = new Guid(id);
  240. _deleteJobCommand.Transaction = transaction;
  241. _deleteJobCommand.ExecuteNonQuery();
  242. index = 0;
  243. _deleteJobItemsCommand.GetParameter(index++).Value = id;
  244. _deleteJobItemsCommand.Transaction = transaction;
  245. _deleteJobItemsCommand.ExecuteNonQuery();
  246. transaction.Commit();
  247. }
  248. catch (OperationCanceledException)
  249. {
  250. if (transaction != null)
  251. {
  252. transaction.Rollback();
  253. }
  254. throw;
  255. }
  256. catch (Exception e)
  257. {
  258. _logger.ErrorException("Failed to save record:", e);
  259. if (transaction != null)
  260. {
  261. transaction.Rollback();
  262. }
  263. throw;
  264. }
  265. finally
  266. {
  267. if (transaction != null)
  268. {
  269. transaction.Dispose();
  270. }
  271. _writeLock.Release();
  272. }
  273. }
  274. public QueryResult<SyncJob> GetJobs(SyncJobQuery query)
  275. {
  276. if (query == null)
  277. {
  278. throw new ArgumentNullException("query");
  279. }
  280. using (var cmd = _connection.CreateCommand())
  281. {
  282. cmd.CommandText = BaseJobSelectText;
  283. var whereClauses = new List<string>();
  284. if (query.IsCompleted.HasValue)
  285. {
  286. if (query.IsCompleted.Value)
  287. {
  288. whereClauses.Add("Status=@Status");
  289. }
  290. else
  291. {
  292. whereClauses.Add("Status<>@Status");
  293. }
  294. cmd.Parameters.Add(cmd, "@Status", DbType.String).Value = SyncJobStatus.Completed.ToString();
  295. }
  296. if (!string.IsNullOrWhiteSpace(query.TargetId))
  297. {
  298. whereClauses.Add("TargetId=@TargetId");
  299. cmd.Parameters.Add(cmd, "@TargetId", DbType.String).Value = query.TargetId;
  300. }
  301. if (!string.IsNullOrWhiteSpace(query.UserId))
  302. {
  303. whereClauses.Add("UserId=@UserId");
  304. cmd.Parameters.Add(cmd, "@UserId", DbType.String).Value = query.UserId;
  305. }
  306. var whereTextWithoutPaging = whereClauses.Count == 0 ?
  307. string.Empty :
  308. " where " + string.Join(" AND ", whereClauses.ToArray());
  309. var startIndex = query.StartIndex ?? 0;
  310. if (startIndex > 0)
  311. {
  312. whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM SyncJobs ORDER BY DateLastModified DESC LIMIT {0})",
  313. startIndex.ToString(_usCulture)));
  314. }
  315. if (whereClauses.Count > 0)
  316. {
  317. cmd.CommandText += " where " + string.Join(" AND ", whereClauses.ToArray());
  318. }
  319. cmd.CommandText += " ORDER BY DateLastModified DESC";
  320. if (query.Limit.HasValue)
  321. {
  322. cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
  323. }
  324. cmd.CommandText += "; select count (Id) from SyncJobs" + whereTextWithoutPaging;
  325. var list = new List<SyncJob>();
  326. var count = 0;
  327. using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
  328. {
  329. while (reader.Read())
  330. {
  331. list.Add(GetJob(reader));
  332. }
  333. if (reader.NextResult() && reader.Read())
  334. {
  335. count = reader.GetInt32(0);
  336. }
  337. }
  338. return new QueryResult<SyncJob>()
  339. {
  340. Items = list.ToArray(),
  341. TotalRecordCount = count
  342. };
  343. }
  344. }
  345. public SyncJobItem GetJobItem(string id)
  346. {
  347. if (string.IsNullOrEmpty(id))
  348. {
  349. throw new ArgumentNullException("id");
  350. }
  351. var guid = new Guid(id);
  352. using (var cmd = _connection.CreateCommand())
  353. {
  354. cmd.CommandText = BaseJobItemSelectText + " where Id=@Id";
  355. cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid;
  356. using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
  357. {
  358. if (reader.Read())
  359. {
  360. return GetJobItem(reader);
  361. }
  362. }
  363. }
  364. return null;
  365. }
  366. public QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query)
  367. {
  368. if (query == null)
  369. {
  370. throw new ArgumentNullException("query");
  371. }
  372. using (var cmd = _connection.CreateCommand())
  373. {
  374. cmd.CommandText = BaseJobItemSelectText;
  375. var whereClauses = new List<string>();
  376. if (!string.IsNullOrWhiteSpace(query.JobId))
  377. {
  378. whereClauses.Add("JobId=@JobId");
  379. cmd.Parameters.Add(cmd, "@JobId", DbType.String).Value = query.JobId;
  380. }
  381. if (!string.IsNullOrWhiteSpace(query.TargetId))
  382. {
  383. whereClauses.Add("TargetId=@TargetId");
  384. cmd.Parameters.Add(cmd, "@TargetId", DbType.String).Value = query.TargetId;
  385. }
  386. if (query.Statuses.Count > 0)
  387. {
  388. var statuses = string.Join(",", query.Statuses.Select(i => "'" + i.ToString() + "'").ToArray());
  389. whereClauses.Add(string.Format("Status in ({0})", statuses));
  390. }
  391. var whereTextWithoutPaging = whereClauses.Count == 0 ?
  392. string.Empty :
  393. " where " + string.Join(" AND ", whereClauses.ToArray());
  394. var startIndex = query.StartIndex ?? 0;
  395. if (startIndex > 0)
  396. {
  397. whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM SyncJobItems ORDER BY DateCreated LIMIT {0})",
  398. startIndex.ToString(_usCulture)));
  399. }
  400. if (whereClauses.Count > 0)
  401. {
  402. cmd.CommandText += " where " + string.Join(" AND ", whereClauses.ToArray());
  403. }
  404. cmd.CommandText += " ORDER BY DateCreated";
  405. if (query.Limit.HasValue)
  406. {
  407. cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
  408. }
  409. cmd.CommandText += "; select count (Id) from SyncJobItems" + whereTextWithoutPaging;
  410. var list = new List<SyncJobItem>();
  411. var count = 0;
  412. using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
  413. {
  414. while (reader.Read())
  415. {
  416. list.Add(GetJobItem(reader));
  417. }
  418. if (reader.NextResult() && reader.Read())
  419. {
  420. count = reader.GetInt32(0);
  421. }
  422. }
  423. return new QueryResult<SyncJobItem>()
  424. {
  425. Items = list.ToArray(),
  426. TotalRecordCount = count
  427. };
  428. }
  429. }
  430. public Task Create(SyncJobItem jobItem)
  431. {
  432. return Update(jobItem);
  433. }
  434. public async Task Update(SyncJobItem jobItem)
  435. {
  436. if (jobItem == null)
  437. {
  438. throw new ArgumentNullException("jobItem");
  439. }
  440. await _writeLock.WaitAsync().ConfigureAwait(false);
  441. IDbTransaction transaction = null;
  442. try
  443. {
  444. transaction = _connection.BeginTransaction();
  445. var index = 0;
  446. _saveJobItemCommand.GetParameter(index++).Value = new Guid(jobItem.Id);
  447. _saveJobItemCommand.GetParameter(index++).Value = jobItem.ItemId;
  448. _saveJobItemCommand.GetParameter(index++).Value = jobItem.ItemName;
  449. _saveJobItemCommand.GetParameter(index++).Value = jobItem.MediaSourceId;
  450. _saveJobItemCommand.GetParameter(index++).Value = jobItem.JobId;
  451. _saveJobItemCommand.GetParameter(index++).Value = jobItem.OutputPath;
  452. _saveJobItemCommand.GetParameter(index++).Value = jobItem.Status.ToString();
  453. _saveJobItemCommand.GetParameter(index++).Value = jobItem.TargetId;
  454. _saveJobItemCommand.GetParameter(index++).Value = jobItem.DateCreated;
  455. _saveJobItemCommand.GetParameter(index++).Value = jobItem.Progress;
  456. _saveJobItemCommand.Transaction = transaction;
  457. _saveJobItemCommand.ExecuteNonQuery();
  458. transaction.Commit();
  459. }
  460. catch (OperationCanceledException)
  461. {
  462. if (transaction != null)
  463. {
  464. transaction.Rollback();
  465. }
  466. throw;
  467. }
  468. catch (Exception e)
  469. {
  470. _logger.ErrorException("Failed to save record:", e);
  471. if (transaction != null)
  472. {
  473. transaction.Rollback();
  474. }
  475. throw;
  476. }
  477. finally
  478. {
  479. if (transaction != null)
  480. {
  481. transaction.Dispose();
  482. }
  483. _writeLock.Release();
  484. }
  485. }
  486. private SyncJobItem GetJobItem(IDataReader reader)
  487. {
  488. var info = new SyncJobItem
  489. {
  490. Id = reader.GetGuid(0).ToString("N"),
  491. ItemId = reader.GetString(1)
  492. };
  493. if (!reader.IsDBNull(2))
  494. {
  495. info.ItemName = reader.GetString(2);
  496. }
  497. if (!reader.IsDBNull(3))
  498. {
  499. info.MediaSourceId = reader.GetString(3);
  500. }
  501. info.JobId = reader.GetString(4);
  502. if (!reader.IsDBNull(5))
  503. {
  504. info.OutputPath = reader.GetString(5);
  505. }
  506. if (!reader.IsDBNull(6))
  507. {
  508. info.Status = (SyncJobItemStatus)Enum.Parse(typeof(SyncJobItemStatus), reader.GetString(6), true);
  509. }
  510. info.TargetId = reader.GetString(7);
  511. info.DateCreated = reader.GetDateTime(8);
  512. if (!reader.IsDBNull(9))
  513. {
  514. info.Progress = reader.GetDouble(9);
  515. }
  516. return info;
  517. }
  518. /// <summary>
  519. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  520. /// </summary>
  521. public void Dispose()
  522. {
  523. Dispose(true);
  524. GC.SuppressFinalize(this);
  525. }
  526. private readonly object _disposeLock = new object();
  527. /// <summary>
  528. /// Releases unmanaged and - optionally - managed resources.
  529. /// </summary>
  530. /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  531. protected virtual void Dispose(bool dispose)
  532. {
  533. if (dispose)
  534. {
  535. try
  536. {
  537. lock (_disposeLock)
  538. {
  539. if (_connection != null)
  540. {
  541. if (_connection.IsOpen())
  542. {
  543. _connection.Close();
  544. }
  545. _connection.Dispose();
  546. _connection = null;
  547. }
  548. }
  549. }
  550. catch (Exception ex)
  551. {
  552. _logger.ErrorException("Error disposing database", ex);
  553. }
  554. }
  555. }
  556. }
  557. }