Horizon – Diff between revs 11 and 12

Subversion Repositories:
Rev:
Only display areas with differencesIgnore whitespace
Rev 11 Rev 12
1 using System; 1 using System;
2 using System.Collections.Generic; 2 using System.Collections.Generic;
3 using System.Data.SQLite; 3 using System.Data.SQLite;
4 using System.Drawing; 4 using System.Drawing;
5 using System.Drawing.Imaging; 5 using System.Drawing.Imaging;
6 using System.Globalization; 6 using System.Globalization;
7 using System.IO; 7 using System.IO;
8 using System.IO.Compression; 8 using System.IO.Compression;
9 using System.Runtime.CompilerServices; 9 using System.Runtime.CompilerServices;
10 using System.Security.Cryptography; 10 using System.Security.Cryptography;
11 using System.Threading; 11 using System.Threading;
12 using System.Threading.Tasks; 12 using System.Threading.Tasks;
13 using Horizon.Snapshots; 13 using Horizon.Snapshots;
14 using Horizon.Utilities; 14 using Horizon.Utilities;
15 using Serilog; 15 using Serilog;
16   16  
17 namespace Horizon.Database 17 namespace Horizon.Database
18 { 18 {
19 public class SnapshotDatabase : IDisposable 19 public class SnapshotDatabase : IDisposable
20 { 20 {
21 #region Static Fields and Constants 21 #region Static Fields and Constants
22   22  
23 private const string CreateTableSql = 23 private const string CreateTableSql =
24 "CREATE TABLE IF NOT EXISTS \"Snapshots\" ( \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"Name\" TEXT NOT NULL, \"Path\" TEXT NOT NULL, \"Time\" TEXT NOT NULL, \"Hash\" TEXT NOT NULL, \"Data\" BLOB, \"Color\" INTEGER, \"Shot\" BLOB, \"Note\" TEXT, UNIQUE (\"Hash\") ON CONFLICT FAIL)"; 24 "CREATE TABLE IF NOT EXISTS \"Snapshots\" ( \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \"Name\" TEXT NOT NULL, \"Path\" TEXT NOT NULL, \"Time\" TEXT NOT NULL, \"Hash\" TEXT NOT NULL, \"Data\" BLOB, \"Color\" INTEGER, \"Shot\" BLOB, \"Note\" TEXT, UNIQUE (\"Hash\") ON CONFLICT FAIL)";
25   25  
26 private const string SetAutoVacuumSql = "PRAGMA auto_vacuum = FULL"; 26 private const string SetAutoVacuumSql = "PRAGMA auto_vacuum = FULL";
27   27  
28 private const string SnapshotFileSql = 28 private const string SnapshotFileSql =
29 "INSERT INTO \"Snapshots\" ( \"Name\", \"Path\", \"Time\", \"Data\", \"Shot\", \"Color\", \"Hash\" ) VALUES ( @name, @path, @time, zeroblob(@dataLength), zeroblob(@shotLength), @color, @hash )"; 29 "INSERT INTO \"Snapshots\" ( \"Name\", \"Path\", \"Time\", \"Data\", \"Shot\", \"Color\", \"Hash\" ) VALUES ( @name, @path, @time, zeroblob(@dataLength), zeroblob(@shotLength), @color, @hash )";
30   30  
31 private const string SnapshotFileNoScreenshotSql = 31 private const string SnapshotFileNoScreenshotSql =
32 "INSERT INTO \"Snapshots\" ( \"Name\", \"Path\", \"Time\", \"Data\", \"Shot\", \"Color\", \"Hash\" ) VALUES ( @name, @path, @time, zeroblob(@dataLength), null, @color, @hash )"; 32 "INSERT INTO \"Snapshots\" ( \"Name\", \"Path\", \"Time\", \"Data\", \"Shot\", \"Color\", \"Hash\" ) VALUES ( @name, @path, @time, zeroblob(@dataLength), null, @color, @hash )";
33   33  
34 private const string RetrieveSnapshotsSql = 34 private const string RetrieveSnapshotsSql =
35 "SELECT \"Name\", \"Path\", \"Time\", \"Color\", \"Hash\" FROM \"Snapshots\" ORDER BY datetime(\"Time\") DESC"; 35 "SELECT \"Name\", \"Path\", \"Time\", \"Color\", \"Hash\" FROM \"Snapshots\" ORDER BY datetime(\"Time\") DESC";
36   36  
37 private const string RetrieveDataPathFromHashSql = 37 private const string RetrieveDataPathFromHashSql =
38 "SELECT \"id\", \"Path\", \"Data\" FROM \"Snapshots\" WHERE Hash = @hash"; 38 "SELECT \"id\", \"Path\", \"Data\" FROM \"Snapshots\" WHERE Hash = @hash";
39   39  
40 private const string RetrieveDataFromHashSql = 40 private const string RetrieveDataFromHashSql =
41 "SELECT \"id\", \"Data\" FROM \"Snapshots\" WHERE Hash = @hash"; 41 "SELECT \"id\", \"Data\" FROM \"Snapshots\" WHERE Hash = @hash";
42   42  
43 private const string UpdateFileSql = 43 private const string UpdateFileSql =
44 "UPDATE \"Snapshots\" SET Data = zeroblob(@dataLength), Hash = @recomputedHash WHERE Hash = @hash"; 44 "UPDATE \"Snapshots\" SET Data = zeroblob(@dataLength), Hash = @recomputedHash WHERE Hash = @hash";
45   45  
46 private const string RemoveSnapshotFromHashSql = 46 private const string RemoveSnapshotFromHashSql =
47 "DELETE FROM \"Snapshots\" WHERE Hash = @hash"; 47 "DELETE FROM \"Snapshots\" WHERE Hash = @hash";
-   48  
-   49 private const string GetTransferSnapshotFromHashSql =
-   50 "SELECT \"Name\", \"Path\", \"Time\", \"Data\", \"Shot\", \"Color\", \"Hash\", \"Note\" FROM \"Snapshots\" WHERE Hash = @hash";
-   51  
-   52 private const string SetTransferSnapshotSql =
-   53 "INSERT INTO \"Snapshots\" ( \"Name\", \"Path\", \"Time\", \"Data\", \"Shot\", \"Color\", \"Hash\", \"Note\" ) VALUES ( @name, @path, @time, zeroblob(@dataLength), zeroblob(@shotLength), @color, @hash, @note )";
48   54  
49 private const string RemoveScreenshotFromHashSql = 55 private const string RemoveScreenshotFromHashSql =
50 "UPDATE \"Snapshots\" SET Shot = null WHERE Hash = @hash"; 56 "UPDATE \"Snapshots\" SET Shot = null WHERE Hash = @hash";
51   57  
52 private const string UpdateColorFromHashSql = 58 private const string UpdateColorFromHashSql =
53 "UPDATE \"Snapshots\" SET Color = @color WHERE Hash = @hash"; 59 "UPDATE \"Snapshots\" SET Color = @color WHERE Hash = @hash";
54   60  
55 private const string UpdateNoteFromHashSql = 61 private const string UpdateNoteFromHashSql =
56 "UPDATE \"Snapshots\" SET Note = @note WHERE Hash = @hash"; 62 "UPDATE \"Snapshots\" SET Note = @note WHERE Hash = @hash";
57   63  
58 private const string UpdateHashFromHashSql = "UPDATE \"Snapshots\" SET Hash = @to WHERE Hash = @from"; 64 private const string UpdateHashFromHashSql = "UPDATE \"Snapshots\" SET Hash = @to WHERE Hash = @from";
59   65  
60 private const string RelocateFileFromHashSql = 66 private const string RelocateFileFromHashSql =
61 "UPDATE \"Snapshots\" SET Path = @path WHERE Hash = @hash"; 67 "UPDATE \"Snapshots\" SET Path = @path WHERE Hash = @hash";
62   68  
63 private const string RemoveColorFromHashSql = 69 private const string RemoveColorFromHashSql =
64 "UPDATE \"Snapshots\" SET Color = null WHERE Hash = @hash"; 70 "UPDATE \"Snapshots\" SET Color = null WHERE Hash = @hash";
65   71  
66 private const string RetrievePreviewFromHashSql = 72 private const string RetrievePreviewFromHashSql =
67 "SELECT \"id\", \"Note\", \"Shot\" FROM \"Snapshots\" WHERE Hash = @hash"; 73 "SELECT \"id\", \"Note\", \"Shot\" FROM \"Snapshots\" WHERE Hash = @hash";
68   74  
69 private const string CountSnapshotsSql = "SELECT COUNT(*) FROM \"Snapshots\""; 75 private const string CountSnapshotsSql = "SELECT COUNT(*) FROM \"Snapshots\"";
70   76  
71 private const string GetLastRowInsertSql = "SELECT last_insert_rowid()"; 77 private const string GetLastRowInsertSql = "SELECT last_insert_rowid()";
72   78  
73 private const string GetRowFromHashSql = "SELECT \"id\" FROM \"Snapshots\" WHERE Hash = @hash"; 79 private const string GetRowFromHashSql = "SELECT \"id\" FROM \"Snapshots\" WHERE Hash = @hash";
74   80  
75 private const string RetrieveTimeFromHash = "SELECT \"Time\" FROM \"Snapshots\" WHERE Hash = @hash"; 81 private const string RetrieveTimeFromHash = "SELECT \"Time\" FROM \"Snapshots\" WHERE Hash = @hash";
76   82  
77 private const string UpdateTimeFromHash = "UPDATE \"Snapshots\" SET Time = @time WHERE Hash = @hash"; 83 private const string UpdateTimeFromHash = "UPDATE \"Snapshots\" SET Time = @time WHERE Hash = @hash";
78   84  
79 private static readonly string DatabaseConnectionString = $"Data Source={Constants.DatabaseFilePath};"; 85 private static readonly string DatabaseConnectionString = $"Data Source={Constants.DatabaseFilePath};";
80   86  
81 private static CancellationToken _cancellationToken; 87 private static CancellationToken _cancellationToken;
82   88  
83 #endregion 89 #endregion
84   90  
85 #region Public Events & Delegates 91 #region Public Events & Delegates
86   92  
87 public event EventHandler<SnapshotDataUpdateEventArgs> SnapshotDataUpdate; 93 public event EventHandler<SnapshotDataUpdateEventArgs> SnapshotDataUpdate;
88   94  
89 public event EventHandler<SnapshotNoteUpdateEventArgs> SnapshotNoteUpdate; 95 public event EventHandler<SnapshotNoteUpdateEventArgs> SnapshotNoteUpdate;
90   96  
91 public event EventHandler<SnapshotCreateEventArgs> SnapshotCreate; 97 public event EventHandler<SnapshotCreateEventArgs> SnapshotCreate;
92   98  
93 public event EventHandler<SnapshotRevertEventArgs> SnapshotRevert; 99 public event EventHandler<SnapshotRevertEventArgs> SnapshotRevert;
94   100  
95 #endregion 101 #endregion
96   102  
97 #region Private Delegates, Events, Enums, Properties, Indexers and Fields 103 #region Private Delegates, Events, Enums, Properties, Indexers and Fields
98   104  
99 private readonly CancellationTokenSource _cancellationTokenSource; 105 private readonly CancellationTokenSource _cancellationTokenSource;
100 private readonly SemaphoreSlim _databaseLock; 106 private readonly SemaphoreSlim _databaseLock;
101 private readonly SQLiteConnectionStringBuilder _sqliteConnectionStringBuilder; 107 private readonly SQLiteConnectionStringBuilder _sqliteConnectionStringBuilder;
102   108  
103 #endregion 109 #endregion
104   110  
105 #region Constructors, Destructors and Finalizers 111 #region Constructors, Destructors and Finalizers
106   112  
107 private SnapshotDatabase() 113 private SnapshotDatabase()
108 { 114 {
109 Directory.CreateDirectory(Constants.DatabaseDirectory); 115 Directory.CreateDirectory(Constants.DatabaseDirectory);
110   116  
111 _databaseLock = new SemaphoreSlim(1, 1); 117 _databaseLock = new SemaphoreSlim(1, 1);
112   118  
113 _sqliteConnectionStringBuilder = new SQLiteConnectionStringBuilder 119 _sqliteConnectionStringBuilder = new SQLiteConnectionStringBuilder
114 { 120 {
115 ConnectionString = DatabaseConnectionString 121 ConnectionString = DatabaseConnectionString
116 }; 122 };
117 } 123 }
118   124  
119 public SnapshotDatabase(CancellationToken cancellationToken) : this() 125 public SnapshotDatabase(CancellationToken cancellationToken) : this()
120 { 126 {
121 _cancellationTokenSource = new CancellationTokenSource(); 127 _cancellationTokenSource = new CancellationTokenSource();
122 var localCancellationToken = _cancellationTokenSource.Token; 128 var localCancellationToken = _cancellationTokenSource.Token;
123 var combinedCancellationTokenSource = 129 var combinedCancellationTokenSource =
124 CancellationTokenSource.CreateLinkedTokenSource(localCancellationToken, cancellationToken); 130 CancellationTokenSource.CreateLinkedTokenSource(localCancellationToken, cancellationToken);
125 _cancellationToken = combinedCancellationTokenSource.Token; 131 _cancellationToken = combinedCancellationTokenSource.Token;
126   132  
127 CreateDatabase(_cancellationToken).ContinueWith(async createDatabaseTask => 133 CreateDatabase(_cancellationToken).ContinueWith(async createDatabaseTask =>
128 { 134 {
129 try 135 try
130 { 136 {
131 await createDatabaseTask; 137 await createDatabaseTask;
132   138  
133 try 139 try
134 { 140 {
135 await SetAutoVacuum(_cancellationToken); 141 await SetAutoVacuum(_cancellationToken);
136 } 142 }
137 catch (Exception exception) 143 catch (Exception exception)
138 { 144 {
139 Log.Error(exception, "Unable to set auto vacuum for database."); 145 Log.Error(exception, "Unable to set auto vacuum for database.");
140 } 146 }
141 } 147 }
142 catch (Exception exception) 148 catch (Exception exception)
143 { 149 {
144 Log.Error(exception, "Unable to create database;"); 150 Log.Error(exception, "Unable to create database;");
145 } 151 }
146 }).Wait(_cancellationToken); 152 }).Wait(_cancellationToken);
147 } 153 }
148   154  
149 public void Dispose() 155 public void Dispose()
150 { 156 {
151 _cancellationTokenSource.Cancel(); 157 _cancellationTokenSource.Cancel();
152 } 158 }
153   159  
154 #endregion 160 #endregion
155   161  
156 #region Public Methods 162 #region Public Methods
157   163  
158 public async Task DeleteScreenshotAsync(string hash, CancellationToken cancellationToken) 164 public async Task DeleteScreenshotAsync(string hash, CancellationToken cancellationToken)
159 { 165 {
160 var connectionString = new SQLiteConnectionStringBuilder 166 var connectionString = new SQLiteConnectionStringBuilder
161 { 167 {
162 ConnectionString = DatabaseConnectionString 168 ConnectionString = DatabaseConnectionString
163 }; 169 };
164   170  
165 await _databaseLock.WaitAsync(cancellationToken); 171 await _databaseLock.WaitAsync(cancellationToken);
166 try 172 try
167 { 173 {
168 using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString)) 174 using (var sqliteConnection = new SQLiteConnection(connectionString.ConnectionString))
169 { 175 {
170 await sqliteConnection.OpenAsync(cancellationToken); 176 await sqliteConnection.OpenAsync(cancellationToken);
171   177  
172 using (var dbTransaction = sqliteConnection.BeginTransaction()) 178 using (var dbTransaction = sqliteConnection.BeginTransaction())
173 { 179 {
174 // Insert the file change. 180 // Insert the file change.
175 using (var sqliteCommand = 181 using (var sqliteCommand =
176 new SQLiteCommand(RemoveScreenshotFromHashSql, sqliteConnection, dbTransaction)) 182 new SQLiteCommand(RemoveScreenshotFromHashSql, sqliteConnection, dbTransaction))
177 { 183 {
178 sqliteCommand.Parameters.AddRange(new[] 184 sqliteCommand.Parameters.AddRange(new[]
179 { 185 {
180 new SQLiteParameter("@hash", hash) 186 new SQLiteParameter("@hash", hash)
181 }); 187 });
182   188  
183 sqliteCommand.Prepare(); 189 sqliteCommand.Prepare();
184   190  
185 try 191 try
186 { 192 {
187 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); 193 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
188   194  
189 dbTransaction.Commit(); 195 dbTransaction.Commit();
190 } 196 }
191 catch 197 catch
192 { 198 {
193 dbTransaction.Rollback(); 199 dbTransaction.Rollback();
194   200  
195 throw; 201 throw;
196 } 202 }
197 } 203 }
198 } 204 }
199 } 205 }
200 } 206 }
201 finally 207 finally
202 { 208 {
203 _databaseLock.Release(); 209 _databaseLock.Release();
204 } 210 }
205 } 211 }
206   212  
207 public async Task NormalizeTimeAsync(string hash, CancellationToken cancellationToken) 213 public async Task NormalizeTimeAsync(string hash, CancellationToken cancellationToken)
208 { 214 {
209 await _databaseLock.WaitAsync(cancellationToken); 215 await _databaseLock.WaitAsync(cancellationToken);
210 try 216 try
211 { 217 {
212 using (var sqliteConnection = 218 using (var sqliteConnection =
213 new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 219 new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
214 { 220 {
215 await sqliteConnection.OpenAsync(cancellationToken); 221 await sqliteConnection.OpenAsync(cancellationToken);
216   222  
217 using (var readSQLiteCommand = new SQLiteCommand(RetrieveTimeFromHash, sqliteConnection)) 223 using (var readSQLiteCommand = new SQLiteCommand(RetrieveTimeFromHash, sqliteConnection))
218 { 224 {
219 readSQLiteCommand.Parameters.AddRange(new[] 225 readSQLiteCommand.Parameters.AddRange(new[]
220 { 226 {
221 new SQLiteParameter("@hash", hash) 227 new SQLiteParameter("@hash", hash)
222 }); 228 });
223   229  
224 readSQLiteCommand.Prepare(); 230 readSQLiteCommand.Prepare();
225   231  
226 using (var sqlDataReader = await readSQLiteCommand.ExecuteReaderAsync(cancellationToken)) 232 using (var sqlDataReader = await readSQLiteCommand.ExecuteReaderAsync(cancellationToken))
227 { 233 {
228 using (var dbTransaction = sqliteConnection.BeginTransaction()) 234 using (var dbTransaction = sqliteConnection.BeginTransaction())
229 { 235 {
230 try 236 try
231 { 237 {
232 while (await sqlDataReader.ReadAsync(cancellationToken)) 238 while (await sqlDataReader.ReadAsync(cancellationToken))
233 { 239 {
234 var time = (string)sqlDataReader["Time"]; 240 var time = (string)sqlDataReader["Time"];
235   241  
236 // Skip if already ISO 8601 242 // Skip if already ISO 8601
237 if (DateTime.TryParseExact(time, 243 if (DateTime.TryParseExact(time,
238 "yyyy-MM-ddTHH:mm:ss.fff", 244 "yyyy-MM-ddTHH:mm:ss.fff",
239 CultureInfo.InvariantCulture, 245 CultureInfo.InvariantCulture,
240 DateTimeStyles.None, out _)) 246 DateTimeStyles.None, out _))
241 { 247 {
242 continue; 248 continue;
243 } 249 }
244   250  
245 if (!DateTime.TryParse(time, out var dateTime)) 251 if (!DateTime.TryParse(time, out var dateTime))
246 { 252 {
247 dateTime = DateTime.Now; 253 dateTime = DateTime.Now;
248 } 254 }
249   255  
250 using (var writeSQLiteCommand = 256 using (var writeSQLiteCommand =
251 new SQLiteCommand(UpdateTimeFromHash, sqliteConnection, dbTransaction)) 257 new SQLiteCommand(UpdateTimeFromHash, sqliteConnection, dbTransaction))
252 { 258 {
253 writeSQLiteCommand.Parameters.AddRange(new[] 259 writeSQLiteCommand.Parameters.AddRange(new[]
254 { 260 {
255 new SQLiteParameter("@time", 261 new SQLiteParameter("@time",
256 dateTime.ToString("yyyy-MM-ddTHH:mm:ss.fff")), 262 dateTime.ToString("yyyy-MM-ddTHH:mm:ss.fff")),
257 new SQLiteParameter("@hash", hash) 263 new SQLiteParameter("@hash", hash)
258 }); 264 });
259   265  
260 writeSQLiteCommand.Prepare(); 266 writeSQLiteCommand.Prepare();
261   267  
262 await writeSQLiteCommand.ExecuteNonQueryAsync(cancellationToken); 268 await writeSQLiteCommand.ExecuteNonQueryAsync(cancellationToken);
263 } 269 }
264 } 270 }
265   271  
266 dbTransaction.Commit(); 272 dbTransaction.Commit();
267 } 273 }
268 catch 274 catch
269 { 275 {
270 dbTransaction.Rollback(); 276 dbTransaction.Rollback();
271   277  
272 throw; 278 throw;
273 } 279 }
274 } 280 }
275 } 281 }
276 } 282 }
277 } 283 }
278 } 284 }
279 finally 285 finally
280 { 286 {
281 _databaseLock.Release(); 287 _databaseLock.Release();
282 } 288 }
283 } 289 }
284   290  
285 public async Task<long> CountSnapshotsAsync(CancellationToken cancellationToken) 291 public async Task<long> CountSnapshotsAsync(CancellationToken cancellationToken)
286 { 292 {
287 await _databaseLock.WaitAsync(cancellationToken); 293 await _databaseLock.WaitAsync(cancellationToken);
288 try 294 try
289 { 295 {
290 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 296 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
291 { 297 {
292 await sqliteConnection.OpenAsync(cancellationToken); 298 await sqliteConnection.OpenAsync(cancellationToken);
293   299  
294 // Insert the file change. 300 // Insert the file change.
295 using (var sqliteCommand = new SQLiteCommand(CountSnapshotsSql, sqliteConnection)) 301 using (var sqliteCommand = new SQLiteCommand(CountSnapshotsSql, sqliteConnection))
296 { 302 {
297 long count = 0; 303 long count = 0;
298   304  
299 sqliteCommand.Prepare(); 305 sqliteCommand.Prepare();
300   306  
301 using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken)) 307 using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken))
302 { 308 {
303 while (await sqlDataReader.ReadAsync(cancellationToken)) 309 while (await sqlDataReader.ReadAsync(cancellationToken))
304 { 310 {
305 if (!(sqlDataReader[0] is long dbCount)) 311 if (!(sqlDataReader[0] is long dbCount))
306 { 312 {
307 count = -1; 313 count = -1;
308 break; 314 break;
309 } 315 }
310   316  
311 count = dbCount; 317 count = dbCount;
312 } 318 }
313   319  
314 return count; 320 return count;
315 } 321 }
316 } 322 }
317 } 323 }
318 } 324 }
319 finally 325 finally
320 { 326 {
321 _databaseLock.Release(); 327 _databaseLock.Release();
322 } 328 }
323 } 329 }
324   330  
325 public async IAsyncEnumerable<Snapshot> LoadSnapshotsAsync([EnumeratorCancellation] CancellationToken cancellationToken) 331 public async IAsyncEnumerable<Snapshot> LoadSnapshotsAsync([EnumeratorCancellation] CancellationToken cancellationToken)
326 { 332 {
327 await _databaseLock.WaitAsync(cancellationToken); 333 await _databaseLock.WaitAsync(cancellationToken);
328 try 334 try
329 { 335 {
330 using (var sqliteConnection = 336 using (var sqliteConnection =
331 new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 337 new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
332 { 338 {
333 await sqliteConnection.OpenAsync(cancellationToken); 339 await sqliteConnection.OpenAsync(cancellationToken);
334   340  
335 // Insert the file change. 341 // Insert the file change.
336 using (var sqliteCommand = new SQLiteCommand(RetrieveSnapshotsSql, sqliteConnection)) 342 using (var sqliteCommand = new SQLiteCommand(RetrieveSnapshotsSql, sqliteConnection))
337 { 343 {
338 sqliteCommand.Prepare(); 344 sqliteCommand.Prepare();
339   345  
340 using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken)) 346 using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken))
341 { 347 {
342 //var snapshots = new List<Snapshot>(); 348 //var snapshots = new List<Snapshot>();
343 while (await sqlDataReader.ReadAsync(cancellationToken)) 349 while (await sqlDataReader.ReadAsync(cancellationToken))
344 { 350 {
345 var name = (string)sqlDataReader["Name"]; 351 var name = (string)sqlDataReader["Name"];
346 var path = (string)sqlDataReader["Path"]; 352 var path = (string)sqlDataReader["Path"];
347 var time = (string)sqlDataReader["Time"]; 353 var time = (string)sqlDataReader["Time"];
348 var hash = (string)sqlDataReader["Hash"]; 354 var hash = (string)sqlDataReader["Hash"];
349   355  
350 var color = Color.Empty; 356 var color = Color.Empty;
351   357  
352 if (!(sqlDataReader["Color"] is DBNull)) 358 if (!(sqlDataReader["Color"] is DBNull))
353 { 359 {
354 var dbColor = Convert.ToInt32(sqlDataReader["Color"]); 360 var dbColor = Convert.ToInt32(sqlDataReader["Color"]);
355   361  
356 switch (dbColor) 362 switch (dbColor)
357 { 363 {
358 case 0: 364 case 0:
359 color = Color.Empty; 365 color = Color.Empty;
360 break; 366 break;
361 default: 367 default:
362 color = Color.FromArgb(dbColor); 368 color = Color.FromArgb(dbColor);
363 break; 369 break;
364 } 370 }
365 } 371 }
366   372  
367 yield return new Snapshot(name, path, time, hash, color); 373 yield return new Snapshot(name, path, time, hash, color);
368 } 374 }
369 } 375 }
370 } 376 }
371 } 377 }
372 } 378 }
373 finally 379 finally
374 { 380 {
375 _databaseLock.Release(); 381 _databaseLock.Release();
376 } 382 }
377 } 383 }
-   384  
-   385 public async Task SetCompleteSnapshotAsync(TransferSnapshot transferSnapshot, CancellationToken cancellationToken)
-   386 {
-   387 await _databaseLock.WaitAsync(cancellationToken);
-   388 try
-   389 {
-   390 using (var sqliteConnection =
-   391 new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
-   392 {
-   393 await sqliteConnection.OpenAsync(cancellationToken);
-   394  
-   395 using (var dbTransaction = sqliteConnection.BeginTransaction())
-   396 {
-   397 try
-   398 {
-   399 using (var dataMemoryStream = new MemoryStream())
-   400 {
-   401 using (var dataZipStream =
-   402 new GZipStream(dataMemoryStream, CompressionMode.Compress, true))
-   403 {
-   404 dataMemoryStream.Position = 0L;
-   405 await dataZipStream.WriteAsync(transferSnapshot.Data, 0,
-   406 transferSnapshot.Data.Length, cancellationToken);
-   407 dataZipStream.Close();
-   408  
-   409 using (var bitmapMemoryStream = new MemoryStream())
-   410 {
-   411 bitmapMemoryStream.Position = 0L;
-   412 using (var bitmapZipStream =
-   413 new GZipStream(bitmapMemoryStream, CompressionMode.Compress,
-   414 true))
-   415 {
-   416 using (var transferImageStream = new MemoryStream(transferSnapshot.Shot))
-   417 {
-   418 transferImageStream.Position = 0L;
-   419 await transferImageStream.CopyToAsync(bitmapZipStream);
-   420 bitmapZipStream.Close();
-   421 bitmapMemoryStream.Position = 0L;
-   422  
-   423 var a = bitmapMemoryStream.ToArray();
-   424  
-   425 // Insert the file change.
-   426 using (var sqliteCommand =
-   427 new SQLiteCommand(SetTransferSnapshotSql, sqliteConnection,
-   428 dbTransaction))
-   429 {
-   430 sqliteCommand.Parameters.AddRange(new[]
-   431 {
-   432 new SQLiteParameter("@name", transferSnapshot.Name),
-   433 new SQLiteParameter("@path", transferSnapshot.Path),
-   434 new SQLiteParameter("@time", transferSnapshot.Time),
-   435 new SQLiteParameter("@dataLength",
-   436 dataMemoryStream.Length),
-   437 new SQLiteParameter("@shotLength",
-   438 bitmapMemoryStream.Length),
-   439 new SQLiteParameter("@hash", transferSnapshot.Hash),
-   440 new SQLiteParameter("@note", transferSnapshot.Note)
-   441 });
-   442  
-   443 var numeric = transferSnapshot.Color;
-   444 switch (numeric)
-   445 {
-   446 case 0:
-   447 sqliteCommand.Parameters.Add(
-   448 new SQLiteParameter("@color", null));
-   449 break;
-   450 default:
-   451 sqliteCommand.Parameters.Add(
-   452 new SQLiteParameter("@color", numeric));
-   453 break;
-   454 }
-   455  
-   456 sqliteCommand.Prepare();
-   457  
-   458 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
-   459 }
-   460  
-   461 // Insert the data blobs.
-   462 using (var sqliteCommand =
-   463 new SQLiteCommand(GetLastRowInsertSql, sqliteConnection,
-   464 dbTransaction))
-   465 {
-   466 sqliteCommand.Prepare();
-   467  
-   468 var rowId =
-   469 (long)await sqliteCommand.ExecuteScalarAsync(
-   470 cancellationToken);
-   471  
-   472 using (var sqliteBlob =
-   473 SQLiteBlob.Create(sqliteConnection, "main",
-   474 "Snapshots",
-   475 "Data",
-   476 rowId,
-   477 false))
-   478 {
-   479 var fileMemoryStreamData = dataMemoryStream.ToArray();
-   480  
-   481 sqliteBlob.Write(fileMemoryStreamData,
-   482 fileMemoryStreamData.Length,
-   483 0);
-   484 }
-   485  
-   486 using (var sqliteBlob =
-   487 SQLiteBlob.Create(sqliteConnection, "main",
-   488 "Snapshots",
-   489 "Shot",
-   490 rowId,
-   491 false))
-   492 {
-   493 var bitmapMemoryStreamData =
-   494 bitmapMemoryStream.ToArray();
-   495  
-   496 sqliteBlob.Write(bitmapMemoryStreamData,
-   497 bitmapMemoryStreamData.Length,
-   498 0);
-   499 }
-   500 }
-   501  
-   502 dbTransaction.Commit();
-   503  
-   504 SnapshotCreate?.Invoke(this,
-   505 new SnapshotCreateSuccessEventArgs(transferSnapshot.Name,
-   506 transferSnapshot.Time, transferSnapshot.Path,
-   507 Color.FromArgb(transferSnapshot.Color),
-   508 transferSnapshot.Hash));
-   509 }
-   510 }
-   511 }
-   512 }
-   513 }
-   514 }
-   515 catch (SQLiteException exception)
-   516 {
-   517 dbTransaction.Rollback();
-   518  
-   519 if (exception.ResultCode != SQLiteErrorCode.Constraint)
-   520 {
-   521 SnapshotCreate?.Invoke(this,
-   522 new SnapshotCreateFailureEventArgs(transferSnapshot.Name, transferSnapshot.Path,
-   523 Color.FromArgb(transferSnapshot.Color), exception));
-   524 }
-   525  
-   526 throw;
-   527 }
-   528 catch (Exception exception)
-   529 {
-   530 dbTransaction.Rollback();
-   531  
-   532 SnapshotCreate?.Invoke(this,
-   533 new SnapshotCreateFailureEventArgs(transferSnapshot.Name, transferSnapshot.Path,
-   534 Color.FromArgb(transferSnapshot.Color), exception));
-   535  
-   536 throw;
-   537 }
-   538 }
-   539 }
-   540 }
-   541 finally
-   542 {
-   543 _databaseLock.Release();
-   544 }
-   545 }
-   546  
-   547  
-   548 public async Task<TransferSnapshot> GetCompleteSnapshot(string hash, CancellationToken cancellationToken)
-   549 {
-   550 await _databaseLock.WaitAsync(cancellationToken);
-   551 try
-   552 {
-   553 using (var sqliteConnection =
-   554 new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
-   555 {
-   556 await sqliteConnection.OpenAsync(cancellationToken);
-   557  
-   558 // Insert the file change.
-   559 using (var sqliteCommand = new SQLiteCommand(GetTransferSnapshotFromHashSql, sqliteConnection))
-   560 {
-   561  
-   562 sqliteCommand.Parameters.AddRange(new[]
-   563 {
-   564 new SQLiteParameter("@hash", hash)
-   565 });
-   566  
-   567 sqliteCommand.Prepare();
-   568  
-   569 using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken))
-   570 {
-   571 //var snapshots = new List<Snapshot>();
-   572 while (await sqlDataReader.ReadAsync(cancellationToken))
-   573 {
-   574 var name = (string)sqlDataReader["Name"];
-   575 var path = (string)sqlDataReader["Path"];
-   576 var time = (string)sqlDataReader["Time"];
-   577  
-   578 var color = Color.Empty;
-   579  
-   580 if (!(sqlDataReader["Color"] is DBNull))
-   581 {
-   582 var dbColor = Convert.ToInt32(sqlDataReader["Color"]);
-   583  
-   584 switch (dbColor)
-   585 {
-   586 case 0:
-   587 color = Color.Empty;
-   588 break;
-   589 default:
-   590 color = Color.FromArgb(dbColor);
-   591 break;
-   592 }
-   593 }
-   594  
-   595 var note = string.Empty;
-   596  
-   597 if (!(sqlDataReader["Note"] is DBNull))
-   598 {
-   599 note = (string)sqlDataReader["Note"];
-   600 }
-   601  
-   602 Bitmap shot = null;
-   603  
-   604 if (!(sqlDataReader["Shot"] is DBNull))
-   605 {
-   606 var readStream = sqlDataReader.GetStream(4);
-   607  
-   608 readStream.Position = 0L;
-   609  
-   610 using (var zipStream = new GZipStream(readStream, CompressionMode.Decompress))
-   611 {
-   612 using (var image = Image.FromStream(zipStream))
-   613 {
-   614 shot = new Bitmap(image);
-   615 }
-   616 }
-   617 }
-   618  
-   619 byte[] data = null;
-   620 if (!(sqlDataReader["Data"] is DBNull))
-   621 {
-   622 using (var readStream = sqlDataReader.GetStream(3))
-   623 {
-   624 using (var memoryStream = new MemoryStream())
-   625 {
-   626 readStream.Position = 0L;
-   627  
-   628 await readStream.CopyToAsync(memoryStream);
-   629  
-   630 memoryStream.Position = 0L;
-   631  
-   632 using (var zipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
-   633 {
-   634 // Do not dispose the returned stream and leave it up to callers to dispose.
-   635 var outputStream = new MemoryStream();
-   636  
-   637 await zipStream.CopyToAsync(outputStream);
-   638  
-   639 outputStream.Position = 0L;
-   640  
-   641 data = outputStream.ToArray();
-   642 }
-   643 }
-   644 }
-   645 }
-   646  
-   647 return new TransferSnapshot(name, path, time, hash, color, shot, note, data);
-   648 }
-   649 }
-   650 }
-   651 }
-   652 }
-   653 finally
-   654 {
-   655 _databaseLock.Release();
-   656 }
-   657  
-   658 return null;
-   659 }
378   660  
379 public async Task CreateSnapshotAsync(string name, string path, Color color, CancellationToken cancellationToken) 661 public async Task CreateSnapshotAsync(string name, string path, Color color, CancellationToken cancellationToken)
380 { 662 {
381 await _databaseLock.WaitAsync(cancellationToken); 663 await _databaseLock.WaitAsync(cancellationToken);
382 try 664 try
383 { 665 {
384 using (var sqliteConnection = 666 using (var sqliteConnection =
385 new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 667 new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
386 { 668 {
387 await sqliteConnection.OpenAsync(cancellationToken); 669 await sqliteConnection.OpenAsync(cancellationToken);
388   670  
389 using (var dbTransaction = sqliteConnection.BeginTransaction()) 671 using (var dbTransaction = sqliteConnection.BeginTransaction())
390 { 672 {
391 try 673 try
392 { 674 {
393 using (var md5 = MD5.Create()) 675 using (var md5 = MD5.Create())
394 { 676 {
395 using (var hashMemoryStream = new MemoryStream()) 677 using (var hashMemoryStream = new MemoryStream())
396 { 678 {
397 using (var fileStream = 679 using (var fileStream =
398 await Miscellaneous.GetFileStream(path, FileMode.Open, FileAccess.Read, 680 await Miscellaneous.GetFileStream(path, FileMode.Open, FileAccess.Read,
399 FileShare.Read, 681 FileShare.Read,
400 cancellationToken)) 682 cancellationToken))
401 { 683 {
402 fileStream.Position = 0L; 684 fileStream.Position = 0L;
403 await fileStream.CopyToAsync(hashMemoryStream); 685 await fileStream.CopyToAsync(hashMemoryStream);
404   686  
405 hashMemoryStream.Position = 0L; 687 hashMemoryStream.Position = 0L;
406 var hash = md5.ComputeHash(hashMemoryStream); 688 var hash = md5.ComputeHash(hashMemoryStream);
407 var hashHex = BitConverter.ToString(hash).Replace("-", "") 689 var hashHex = BitConverter.ToString(hash).Replace("-", "")
408 .ToLowerInvariant(); 690 .ToLowerInvariant();
409   691  
410 using (var fileMemoryStream = new MemoryStream()) 692 using (var fileMemoryStream = new MemoryStream())
411 { 693 {
412 using (var fileZipStream = 694 using (var fileZipStream =
413 new GZipStream(fileMemoryStream, CompressionMode.Compress, true)) 695 new GZipStream(fileMemoryStream, CompressionMode.Compress, true))
414 { 696 {
415 fileStream.Position = 0L; 697 fileStream.Position = 0L;
416 await fileStream.CopyToAsync(fileZipStream); 698 await fileStream.CopyToAsync(fileZipStream);
417 fileZipStream.Close(); 699 fileZipStream.Close();
418   700  
419 var time = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fff"); 701 var time = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fff");
420   702  
421 fileMemoryStream.Position = 0L; 703 fileMemoryStream.Position = 0L;
422   704  
423 // Insert the file change. 705 // Insert the file change.
424 using (var sqliteCommand = 706 using (var sqliteCommand =
425 new SQLiteCommand(SnapshotFileNoScreenshotSql, sqliteConnection, 707 new SQLiteCommand(SnapshotFileNoScreenshotSql, sqliteConnection,
426 dbTransaction)) 708 dbTransaction))
427 { 709 {
428 sqliteCommand.Parameters.AddRange(new[] 710 sqliteCommand.Parameters.AddRange(new[]
429 { 711 {
430 new SQLiteParameter("@name", name), 712 new SQLiteParameter("@name", name),
431 new SQLiteParameter("@time", time), 713 new SQLiteParameter("@time", time),
432 new SQLiteParameter("@path", path), 714 new SQLiteParameter("@path", path),
433 new SQLiteParameter("@dataLength", 715 new SQLiteParameter("@dataLength",
434 fileMemoryStream.Length), 716 fileMemoryStream.Length),
435 new SQLiteParameter("@hash", hashHex) 717 new SQLiteParameter("@hash", hashHex)
436 }); 718 });
437   719  
438 var numeric = color.ToArgb(); 720 var numeric = color.ToArgb();
439 switch (numeric) 721 switch (numeric)
440 { 722 {
441 case 0: 723 case 0:
442 sqliteCommand.Parameters.Add( 724 sqliteCommand.Parameters.Add(
443 new SQLiteParameter("@color", null)); 725 new SQLiteParameter("@color", null));
444 break; 726 break;
445 default: 727 default:
446 sqliteCommand.Parameters.Add( 728 sqliteCommand.Parameters.Add(
447 new SQLiteParameter("@color", numeric)); 729 new SQLiteParameter("@color", numeric));
448 break; 730 break;
449 } 731 }
450   732  
451 sqliteCommand.Prepare(); 733 sqliteCommand.Prepare();
452   734  
453 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); 735 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
454 } 736 }
455   737  
456 // Insert the data blobs. 738 // Insert the data blobs.
457 using (var sqliteCommand = 739 using (var sqliteCommand =
458 new SQLiteCommand(GetLastRowInsertSql, sqliteConnection, 740 new SQLiteCommand(GetLastRowInsertSql, sqliteConnection,
459 dbTransaction)) 741 dbTransaction))
460 { 742 {
461 sqliteCommand.Prepare(); 743 sqliteCommand.Prepare();
462   744  
463 var rowId = 745 var rowId =
464 (long)await sqliteCommand.ExecuteScalarAsync(cancellationToken); 746 (long)await sqliteCommand.ExecuteScalarAsync(cancellationToken);
465   747  
466 using (var sqliteBlob = 748 using (var sqliteBlob =
467 SQLiteBlob.Create(sqliteConnection, "main", "Snapshots", 749 SQLiteBlob.Create(sqliteConnection, "main", "Snapshots",
468 "Data", 750 "Data",
469 rowId, 751 rowId,
470 false)) 752 false))
471 { 753 {
472 var fileMemoryStreamData = fileMemoryStream.ToArray(); 754 var fileMemoryStreamData = fileMemoryStream.ToArray();
473   755  
474 sqliteBlob.Write(fileMemoryStreamData, 756 sqliteBlob.Write(fileMemoryStreamData,
475 fileMemoryStreamData.Length, 757 fileMemoryStreamData.Length,
476 0); 758 0);
477 } 759 }
478 } 760 }
479   761  
480 dbTransaction.Commit(); 762 dbTransaction.Commit();
481   763  
482 SnapshotCreate?.Invoke(this, 764 SnapshotCreate?.Invoke(this,
483 new SnapshotCreateSuccessEventArgs(name, time, path, color, 765 new SnapshotCreateSuccessEventArgs(name, time, path, color,
484 hashHex)); 766 hashHex));
485 } 767 }
486 } 768 }
487 } 769 }
488 } 770 }
489 } 771 }
490 } 772 }
491 catch (SQLiteException exception) 773 catch (SQLiteException exception)
492 { 774 {
493 dbTransaction.Rollback(); 775 dbTransaction.Rollback();
494   776  
495 if (exception.ResultCode != SQLiteErrorCode.Constraint) 777 if (exception.ResultCode != SQLiteErrorCode.Constraint)
496 { 778 {
497 SnapshotCreate?.Invoke(this, 779 SnapshotCreate?.Invoke(this,
498 new SnapshotCreateFailureEventArgs(name, path, color, exception)); 780 new SnapshotCreateFailureEventArgs(name, path, color, exception));
499 } 781 }
500   782  
501 throw; 783 throw;
502 } 784 }
503 catch (Exception exception) 785 catch (Exception exception)
504 { 786 {
505 dbTransaction.Rollback(); 787 dbTransaction.Rollback();
506   788  
507 SnapshotCreate?.Invoke(this, 789 SnapshotCreate?.Invoke(this,
508 new SnapshotCreateFailureEventArgs(name, path, color, exception)); 790 new SnapshotCreateFailureEventArgs(name, path, color, exception));
509   791  
510 throw; 792 throw;
511 } 793 }
512 } 794 }
513 } 795 }
514 } 796 }
515 finally 797 finally
516 { 798 {
517 _databaseLock.Release(); 799 _databaseLock.Release();
518 } 800 }
519 } 801 }
520   802  
521 public async Task CreateSnapshotAsync(string name, string path, 803 public async Task CreateSnapshotAsync(string name, string path,
522 Bitmap shot, Color color, CancellationToken cancellationToken) 804 Bitmap shot, Color color, CancellationToken cancellationToken)
523 { 805 {
524 await _databaseLock.WaitAsync(cancellationToken); 806 await _databaseLock.WaitAsync(cancellationToken);
525 try 807 try
526 { 808 {
527 using (var sqliteConnection = 809 using (var sqliteConnection =
528 new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 810 new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
529 { 811 {
530 await sqliteConnection.OpenAsync(cancellationToken); 812 await sqliteConnection.OpenAsync(cancellationToken);
531   813  
532 using (var dbTransaction = sqliteConnection.BeginTransaction()) 814 using (var dbTransaction = sqliteConnection.BeginTransaction())
533 { 815 {
534 try 816 try
535 { 817 {
536 using (var md5 = MD5.Create()) 818 using (var md5 = MD5.Create())
537 { 819 {
538 using (var hashMemoryStream = new MemoryStream()) 820 using (var hashMemoryStream = new MemoryStream())
539 { 821 {
540 using (var fileStream = 822 using (var fileStream =
541 await Miscellaneous.GetFileStream(path, FileMode.Open, FileAccess.Read, 823 await Miscellaneous.GetFileStream(path, FileMode.Open, FileAccess.Read,
542 FileShare.Read, 824 FileShare.Read,
543 cancellationToken)) 825 cancellationToken))
544 { 826 {
545 fileStream.Position = 0L; 827 fileStream.Position = 0L;
546 await fileStream.CopyToAsync(hashMemoryStream); 828 await fileStream.CopyToAsync(hashMemoryStream);
547   829  
548 hashMemoryStream.Position = 0L; 830 hashMemoryStream.Position = 0L;
549 var hash = md5.ComputeHash(hashMemoryStream); 831 var hash = md5.ComputeHash(hashMemoryStream);
550 var hashHex = BitConverter.ToString(hash).Replace("-", "") 832 var hashHex = BitConverter.ToString(hash).Replace("-", "")
551 .ToLowerInvariant(); 833 .ToLowerInvariant();
552   834  
553 using (var fileMemoryStream = new MemoryStream()) 835 using (var fileMemoryStream = new MemoryStream())
554 { 836 {
555 using (var fileZipStream = 837 using (var fileZipStream =
556 new GZipStream(fileMemoryStream, CompressionMode.Compress, true)) 838 new GZipStream(fileMemoryStream, CompressionMode.Compress, true))
557 { 839 {
558 fileStream.Position = 0L; 840 fileStream.Position = 0L;
559 await fileStream.CopyToAsync(fileZipStream); 841 await fileStream.CopyToAsync(fileZipStream);
560 fileZipStream.Close(); 842 fileZipStream.Close();
561   843  
562 using (var bitmapMemoryStream = new MemoryStream()) 844 using (var bitmapMemoryStream = new MemoryStream())
563 { 845 {
564 using (var bitmapZipStream = 846 using (var bitmapZipStream =
565 new GZipStream(bitmapMemoryStream, CompressionMode.Compress, 847 new GZipStream(bitmapMemoryStream, CompressionMode.Compress,
566 true)) 848 true))
567 { 849 {
568 shot.Save(bitmapZipStream, ImageFormat.Bmp); 850 shot.Save(bitmapZipStream, ImageFormat.Bmp);
569 bitmapZipStream.Close(); 851 bitmapZipStream.Close();
570   852  
571 var time = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fff"); 853 var time = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fff");
572   854  
573 fileMemoryStream.Position = 0L; 855 fileMemoryStream.Position = 0L;
574 bitmapMemoryStream.Position = 0L; 856 bitmapMemoryStream.Position = 0L;
575   857  
576 // Insert the file change. 858 // Insert the file change.
577 using (var sqliteCommand = 859 using (var sqliteCommand =
578 new SQLiteCommand(SnapshotFileSql, sqliteConnection, 860 new SQLiteCommand(SnapshotFileSql, sqliteConnection,
579 dbTransaction)) 861 dbTransaction))
580 { 862 {
581 sqliteCommand.Parameters.AddRange(new[] 863 sqliteCommand.Parameters.AddRange(new[]
582 { 864 {
583 new SQLiteParameter("@name", name), 865 new SQLiteParameter("@name", name),
584 new SQLiteParameter("@time", time), 866 new SQLiteParameter("@time", time),
585 new SQLiteParameter("@path", path), 867 new SQLiteParameter("@path", path),
586 new SQLiteParameter("@shotLength", 868 new SQLiteParameter("@shotLength",
587 bitmapMemoryStream.Length), 869 bitmapMemoryStream.Length),
588 new SQLiteParameter("@dataLength", 870 new SQLiteParameter("@dataLength",
589 fileMemoryStream.Length), 871 fileMemoryStream.Length),
590 new SQLiteParameter("@hash", hashHex) 872 new SQLiteParameter("@hash", hashHex)
591 }); 873 });
592   874  
593 var numeric = color.ToArgb(); 875 var numeric = color.ToArgb();
594 switch (numeric) 876 switch (numeric)
595 { 877 {
596 case 0: 878 case 0:
597 sqliteCommand.Parameters.Add( 879 sqliteCommand.Parameters.Add(
598 new SQLiteParameter("@color", null)); 880 new SQLiteParameter("@color", null));
599 break; 881 break;
600 default: 882 default:
601 sqliteCommand.Parameters.Add( 883 sqliteCommand.Parameters.Add(
602 new SQLiteParameter("@color", numeric)); 884 new SQLiteParameter("@color", numeric));
603 break; 885 break;
604 } 886 }
605   887  
606 sqliteCommand.Prepare(); 888 sqliteCommand.Prepare();
607   889  
608 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); 890 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
609 } 891 }
610   892  
611 // Insert the data blobs. 893 // Insert the data blobs.
612 using (var sqliteCommand = 894 using (var sqliteCommand =
613 new SQLiteCommand(GetLastRowInsertSql, sqliteConnection, 895 new SQLiteCommand(GetLastRowInsertSql, sqliteConnection,
614 dbTransaction)) 896 dbTransaction))
615 { 897 {
616 sqliteCommand.Prepare(); 898 sqliteCommand.Prepare();
617   899  
618 var rowId = 900 var rowId =
619 (long)await sqliteCommand.ExecuteScalarAsync( 901 (long)await sqliteCommand.ExecuteScalarAsync(
620 cancellationToken); 902 cancellationToken);
621   903  
622 using (var sqliteBlob = 904 using (var sqliteBlob =
623 SQLiteBlob.Create(sqliteConnection, "main", 905 SQLiteBlob.Create(sqliteConnection, "main",
624 "Snapshots", 906 "Snapshots",
625 "Data", 907 "Data",
626 rowId, 908 rowId,
627 false)) 909 false))
628 { 910 {
629 var fileMemoryStreamData = fileMemoryStream.ToArray(); 911 var fileMemoryStreamData = fileMemoryStream.ToArray();
630   912  
631 sqliteBlob.Write(fileMemoryStreamData, 913 sqliteBlob.Write(fileMemoryStreamData,
632 fileMemoryStreamData.Length, 914 fileMemoryStreamData.Length,
633 0); 915 0);
634 } 916 }
635   917  
636 using (var sqliteBlob = 918 using (var sqliteBlob =
637 SQLiteBlob.Create(sqliteConnection, "main", 919 SQLiteBlob.Create(sqliteConnection, "main",
638 "Snapshots", 920 "Snapshots",
639 "Shot", 921 "Shot",
640 rowId, 922 rowId,
641 false)) 923 false))
642 { 924 {
643 var bitmapMemoryStreamData = 925 var bitmapMemoryStreamData =
644 bitmapMemoryStream.ToArray(); 926 bitmapMemoryStream.ToArray();
645   927  
646 sqliteBlob.Write(bitmapMemoryStreamData, 928 sqliteBlob.Write(bitmapMemoryStreamData,
647 bitmapMemoryStreamData.Length, 929 bitmapMemoryStreamData.Length,
648 0); 930 0);
649 } 931 }
650 } 932 }
651   933  
652 dbTransaction.Commit(); 934 dbTransaction.Commit();
653   935  
654 SnapshotCreate?.Invoke(this, 936 SnapshotCreate?.Invoke(this,
655 new SnapshotCreateSuccessEventArgs(name, time, path, color, 937 new SnapshotCreateSuccessEventArgs(name, time, path, color,
656 hashHex)); 938 hashHex));
657 } 939 }
658 } 940 }
659 } 941 }
660 } 942 }
661 } 943 }
662 } 944 }
663 } 945 }
664 } 946 }
665 catch (SQLiteException exception) 947 catch (SQLiteException exception)
666 { 948 {
667 dbTransaction.Rollback(); 949 dbTransaction.Rollback();
668   950  
669 if (exception.ResultCode != SQLiteErrorCode.Constraint) 951 if (exception.ResultCode != SQLiteErrorCode.Constraint)
670 { 952 {
671 SnapshotCreate?.Invoke(this, 953 SnapshotCreate?.Invoke(this,
672 new SnapshotCreateFailureEventArgs(name, path, color, exception)); 954 new SnapshotCreateFailureEventArgs(name, path, color, exception));
673 } 955 }
674   956  
675 throw; 957 throw;
676 } 958 }
677 catch (Exception exception) 959 catch (Exception exception)
678 { 960 {
679 dbTransaction.Rollback(); 961 dbTransaction.Rollback();
680   962  
681 SnapshotCreate?.Invoke(this, 963 SnapshotCreate?.Invoke(this,
682 new SnapshotCreateFailureEventArgs(name, path, color, exception)); 964 new SnapshotCreateFailureEventArgs(name, path, color, exception));
683   965  
684 throw; 966 throw;
685 } 967 }
686 } 968 }
687 } 969 }
688 } 970 }
689 finally 971 finally
690 { 972 {
691 _databaseLock.Release(); 973 _databaseLock.Release();
692 } 974 }
693 } 975 }
694   976  
695 public async Task SaveFileAsync(string path, string hash, CancellationToken cancellationToken) 977 public async Task SaveFileAsync(string path, string hash, CancellationToken cancellationToken)
696 { 978 {
697 await _databaseLock.WaitAsync(cancellationToken); 979 await _databaseLock.WaitAsync(cancellationToken);
698 try 980 try
699 { 981 {
700 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 982 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
701 { 983 {
702 await sqliteConnection.OpenAsync(cancellationToken); 984 await sqliteConnection.OpenAsync(cancellationToken);
703   985  
704 // Insert the file change. 986 // Insert the file change.
705 using (var sqliteCommand = 987 using (var sqliteCommand =
706 new SQLiteCommand(RetrieveDataPathFromHashSql, sqliteConnection)) 988 new SQLiteCommand(RetrieveDataPathFromHashSql, sqliteConnection))
707 { 989 {
708 sqliteCommand.Parameters.AddRange(new[] 990 sqliteCommand.Parameters.AddRange(new[]
709 { 991 {
710 new SQLiteParameter("@hash", hash) 992 new SQLiteParameter("@hash", hash)
711 }); 993 });
712   994  
713 sqliteCommand.Prepare(); 995 sqliteCommand.Prepare();
714   996  
715 using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken)) 997 using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken))
716 { 998 {
717 while (await sqlDataReader.ReadAsync(cancellationToken)) 999 while (await sqlDataReader.ReadAsync(cancellationToken))
718 { 1000 {
719 // Create directories if they do not exist. 1001 // Create directories if they do not exist.
720 var dir = Path.GetDirectoryName(path); 1002 var dir = Path.GetDirectoryName(path);
721   1003  
722 if (dir != null && !Directory.Exists(dir)) 1004 if (dir != null && !Directory.Exists(dir))
723 { 1005 {
724 Directory.CreateDirectory(dir); 1006 Directory.CreateDirectory(dir);
725 } 1007 }
726   1008  
727 using (var readStream = sqlDataReader.GetStream(2)) 1009 using (var readStream = sqlDataReader.GetStream(2))
728 { 1010 {
729 using (var fileStream = 1011 using (var fileStream =
730 await Miscellaneous.GetFileStream(path, FileMode.Create, FileAccess.Write, 1012 await Miscellaneous.GetFileStream(path, FileMode.Create, FileAccess.Write,
731 FileShare.Write, 1013 FileShare.Write,
732 cancellationToken)) 1014 cancellationToken))
733 { 1015 {
734 readStream.Position = 0L; 1016 readStream.Position = 0L;
735   1017  
736 using (var zipStream = new GZipStream(readStream, CompressionMode.Decompress)) 1018 using (var zipStream = new GZipStream(readStream, CompressionMode.Decompress))
737 { 1019 {
738 await zipStream.CopyToAsync(fileStream); 1020 await zipStream.CopyToAsync(fileStream);
739 } 1021 }
740 } 1022 }
741 } 1023 }
742 } 1024 }
743 } 1025 }
744 } 1026 }
745 } 1027 }
746 } 1028 }
747 finally 1029 finally
748 { 1030 {
749 _databaseLock.Release(); 1031 _databaseLock.Release();
750 } 1032 }
751 } 1033 }
752   1034  
753 public async Task RevertFileAsync(string name, string hash, CancellationToken cancellationToken, bool atomic = true) 1035 public async Task RevertFileAsync(string name, string hash, CancellationToken cancellationToken, bool atomic = true)
754 { 1036 {
755 await _databaseLock.WaitAsync(cancellationToken); 1037 await _databaseLock.WaitAsync(cancellationToken);
756 try 1038 try
757 { 1039 {
758 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 1040 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
759 { 1041 {
760 await sqliteConnection.OpenAsync(cancellationToken); 1042 await sqliteConnection.OpenAsync(cancellationToken);
761   1043  
762 // Insert the file change. 1044 // Insert the file change.
763 using (var sqliteCommand = 1045 using (var sqliteCommand =
764 new SQLiteCommand(RetrieveDataPathFromHashSql, sqliteConnection)) 1046 new SQLiteCommand(RetrieveDataPathFromHashSql, sqliteConnection))
765 { 1047 {
766 try 1048 try
767 { 1049 {
768 sqliteCommand.Parameters.AddRange(new[] 1050 sqliteCommand.Parameters.AddRange(new[]
769 { 1051 {
770 new SQLiteParameter("@hash", hash) 1052 new SQLiteParameter("@hash", hash)
771 }); 1053 });
772   1054  
773 sqliteCommand.Prepare(); 1055 sqliteCommand.Prepare();
774   1056  
775 using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken)) 1057 using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken))
776 { 1058 {
777 while (await sqlDataReader.ReadAsync(cancellationToken)) 1059 while (await sqlDataReader.ReadAsync(cancellationToken))
778 { 1060 {
779 var path = (string)sqlDataReader["Path"]; 1061 var path = (string)sqlDataReader["Path"];
780   1062  
781 // Create directories if they do not exist. 1063 // Create directories if they do not exist.
782 var dir = Path.GetDirectoryName(path); 1064 var dir = Path.GetDirectoryName(path);
783   1065  
784 if (dir != null && !Directory.Exists(dir)) 1066 if (dir != null && !Directory.Exists(dir))
785 { 1067 {
786 Directory.CreateDirectory(dir); 1068 Directory.CreateDirectory(dir);
787 } 1069 }
788   1070  
789 switch (atomic) 1071 switch (atomic)
790 { 1072 {
791 case true: 1073 case true:
792 // Atomic 1074 // Atomic
793 var temp = Path.Combine(Path.GetDirectoryName(path), 1075 var temp = Path.Combine(Path.GetDirectoryName(path),
794 $"{Path.GetFileName(path)}.temp"); 1076 $"{Path.GetFileName(path)}.temp");
795   1077  
796 using (var readStream = sqlDataReader.GetStream(2)) 1078 using (var readStream = sqlDataReader.GetStream(2))
797 { 1079 {
798 using (var fileStream = new FileStream(temp, FileMode.Create, 1080 using (var fileStream = new FileStream(temp, FileMode.Create,
799 FileAccess.Write, 1081 FileAccess.Write,
800 FileShare.None)) 1082 FileShare.None))
801 { 1083 {
802 using (var zipStream = 1084 using (var zipStream =
803 new GZipStream(readStream, CompressionMode.Decompress)) 1085 new GZipStream(readStream, CompressionMode.Decompress))
804 { 1086 {
805 zipStream.CopyTo(fileStream); 1087 zipStream.CopyTo(fileStream);
806 } 1088 }
807 } 1089 }
808 } 1090 }
809   1091  
810 try 1092 try
811 { 1093 {
812 File.Replace(temp, path, null, true); 1094 File.Replace(temp, path, null, true);
813 } 1095 }
814 catch 1096 catch
815 { 1097 {
816 try 1098 try
817 { 1099 {
818 File.Delete(temp); 1100 File.Delete(temp);
819 } 1101 }
820 catch (Exception exception) 1102 catch (Exception exception)
821 { 1103 {
822 // Suppress deletion errors of temporary file. 1104 // Suppress deletion errors of temporary file.
823 Log.Warning(exception, "Could not delete temporary file.", temp); 1105 Log.Warning(exception, "Could not delete temporary file.", temp);
824 } 1106 }
825   1107  
826 throw; 1108 throw;
827 } 1109 }
828   1110  
829 break; 1111 break;
830 default: 1112 default:
831 // Asynchronous 1113 // Asynchronous
832 using (var readStream = sqlDataReader.GetStream(2)) 1114 using (var readStream = sqlDataReader.GetStream(2))
833 { 1115 {
834 using (var fileStream = 1116 using (var fileStream =
835 await Miscellaneous.GetFileStream(path, FileMode.Create, 1117 await Miscellaneous.GetFileStream(path, FileMode.Create,
836 FileAccess.Write, 1118 FileAccess.Write,
837 FileShare.Write, 1119 FileShare.Write,
838 cancellationToken)) 1120 cancellationToken))
839 { 1121 {
840 readStream.Position = 0L; 1122 readStream.Position = 0L;
841   1123  
842 using (var zipStream = 1124 using (var zipStream =
843 new GZipStream(readStream, CompressionMode.Decompress)) 1125 new GZipStream(readStream, CompressionMode.Decompress))
844 { 1126 {
845 await zipStream.CopyToAsync(fileStream); 1127 await zipStream.CopyToAsync(fileStream);
846 } 1128 }
847 } 1129 }
848 } 1130 }
849   1131  
850   1132  
851 break; 1133 break;
852 } 1134 }
853   1135  
854 SnapshotRevert?.Invoke(this, new SnapshotRevertSuccessEventArgs(name)); 1136 SnapshotRevert?.Invoke(this, new SnapshotRevertSuccessEventArgs(name));
855 } 1137 }
856 } 1138 }
857 } 1139 }
858 catch 1140 catch
859 { 1141 {
860 SnapshotRevert?.Invoke(this, new SnapshotRevertFailureEventArgs(name)); 1142 SnapshotRevert?.Invoke(this, new SnapshotRevertFailureEventArgs(name));
861   1143  
862 throw; 1144 throw;
863 } 1145 }
864 } 1146 }
865 } 1147 }
866 } 1148 }
867 finally 1149 finally
868 { 1150 {
869 _databaseLock.Release(); 1151 _databaseLock.Release();
870 } 1152 }
871 } 1153 }
872   1154  
873 public async Task RemoveFileFastAsync(IEnumerable<string> hashes, CancellationToken cancellationToken) 1155 public async Task RemoveFileFastAsync(IEnumerable<string> hashes, CancellationToken cancellationToken)
874 { 1156 {
875 await _databaseLock.WaitAsync(cancellationToken); 1157 await _databaseLock.WaitAsync(cancellationToken);
876 try 1158 try
877 { 1159 {
878 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 1160 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
879 { 1161 {
880 await sqliteConnection.OpenAsync(cancellationToken); 1162 await sqliteConnection.OpenAsync(cancellationToken);
881   1163  
882 using (var dbTransaction = sqliteConnection.BeginTransaction()) 1164 using (var dbTransaction = sqliteConnection.BeginTransaction())
883 { 1165 {
884 try 1166 try
885 { 1167 {
886 var transactionCommands = new List<Task>(); 1168 var transactionCommands = new List<Task>();
887   1169  
888 foreach (var hash in hashes) 1170 foreach (var hash in hashes)
889 { 1171 {
890 // Insert the file change. 1172 // Insert the file change.
891 using (var sqliteCommand = 1173 using (var sqliteCommand =
892 new SQLiteCommand(RemoveSnapshotFromHashSql, sqliteConnection, dbTransaction)) 1174 new SQLiteCommand(RemoveSnapshotFromHashSql, sqliteConnection, dbTransaction))
893 { 1175 {
894 sqliteCommand.Parameters.AddRange(new[] 1176 sqliteCommand.Parameters.AddRange(new[]
895 { 1177 {
896 new SQLiteParameter("@hash", hash) 1178 new SQLiteParameter("@hash", hash)
897 }); 1179 });
898   1180  
899 sqliteCommand.Prepare(); 1181 sqliteCommand.Prepare();
900   1182  
901 var command = sqliteCommand.ExecuteNonQueryAsync(cancellationToken); 1183 var command = sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
902   1184  
903 transactionCommands.Add(command); 1185 transactionCommands.Add(command);
904 } 1186 }
905 } 1187 }
906   1188  
907 await Task.WhenAll(transactionCommands); 1189 await Task.WhenAll(transactionCommands);
908   1190  
909 dbTransaction.Commit(); 1191 dbTransaction.Commit();
910 } 1192 }
911 catch 1193 catch
912 { 1194 {
913 dbTransaction.Rollback(); 1195 dbTransaction.Rollback();
914   1196  
915 throw; 1197 throw;
916 } 1198 }
917 } 1199 }
918 } 1200 }
919 } 1201 }
920 finally 1202 finally
921 { 1203 {
922 _databaseLock.Release(); 1204 _databaseLock.Release();
923 } 1205 }
924 } 1206 }
925   1207  
926 public async Task RemoveFileAsync(string hash, CancellationToken cancellationToken) 1208 public async Task RemoveFileAsync(string hash, CancellationToken cancellationToken)
927 { 1209 {
928 await _databaseLock.WaitAsync(cancellationToken); 1210 await _databaseLock.WaitAsync(cancellationToken);
929 try 1211 try
930 { 1212 {
931 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 1213 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
932 { 1214 {
933 await sqliteConnection.OpenAsync(cancellationToken); 1215 await sqliteConnection.OpenAsync(cancellationToken);
934   1216  
935 using (var dbTransaction = sqliteConnection.BeginTransaction()) 1217 using (var dbTransaction = sqliteConnection.BeginTransaction())
936 { 1218 {
937 // Insert the file change. 1219 // Insert the file change.
938 using (var sqliteCommand = 1220 using (var sqliteCommand =
939 new SQLiteCommand(RemoveSnapshotFromHashSql, sqliteConnection, dbTransaction)) 1221 new SQLiteCommand(RemoveSnapshotFromHashSql, sqliteConnection, dbTransaction))
940 { 1222 {
941 sqliteCommand.Parameters.AddRange(new[] 1223 sqliteCommand.Parameters.AddRange(new[]
942 { 1224 {
943 new SQLiteParameter("@hash", hash) 1225 new SQLiteParameter("@hash", hash)
944 }); 1226 });
945   1227  
946 sqliteCommand.Prepare(); 1228 sqliteCommand.Prepare();
947   1229  
948 try 1230 try
949 { 1231 {
950 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); 1232 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
951   1233  
952 dbTransaction.Commit(); 1234 dbTransaction.Commit();
953 } 1235 }
954 catch 1236 catch
955 { 1237 {
956 dbTransaction.Rollback(); 1238 dbTransaction.Rollback();
957   1239  
958 throw; 1240 throw;
959 } 1241 }
960 } 1242 }
961 } 1243 }
962 } 1244 }
963 } 1245 }
964 finally 1246 finally
965 { 1247 {
966 _databaseLock.Release(); 1248 _databaseLock.Release();
967 } 1249 }
968 } 1250 }
969   1251  
970 public async Task UpdateColorAsync(string hash, Color color, CancellationToken cancellationToken) 1252 public async Task UpdateColorAsync(string hash, Color color, CancellationToken cancellationToken)
971 { 1253 {
972 await _databaseLock.WaitAsync(cancellationToken); 1254 await _databaseLock.WaitAsync(cancellationToken);
973   1255  
974 try 1256 try
975 { 1257 {
976 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 1258 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
977 { 1259 {
978 await sqliteConnection.OpenAsync(cancellationToken); 1260 await sqliteConnection.OpenAsync(cancellationToken);
979   1261  
980 using (var dbTransaction = sqliteConnection.BeginTransaction()) 1262 using (var dbTransaction = sqliteConnection.BeginTransaction())
981 { 1263 {
982 // Insert the file change. 1264 // Insert the file change.
983 using (var sqliteCommand = 1265 using (var sqliteCommand =
984 new SQLiteCommand(UpdateColorFromHashSql, sqliteConnection, dbTransaction)) 1266 new SQLiteCommand(UpdateColorFromHashSql, sqliteConnection, dbTransaction))
985 { 1267 {
986 sqliteCommand.Parameters.AddRange(new[] 1268 sqliteCommand.Parameters.AddRange(new[]
987 { 1269 {
988 new SQLiteParameter("@hash", hash), 1270 new SQLiteParameter("@hash", hash),
989 new SQLiteParameter("@color", color.ToArgb()) 1271 new SQLiteParameter("@color", color.ToArgb())
990 }); 1272 });
991   1273  
992 sqliteCommand.Prepare(); 1274 sqliteCommand.Prepare();
993   1275  
994 try 1276 try
995 { 1277 {
996 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); 1278 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
997   1279  
998 dbTransaction.Commit(); 1280 dbTransaction.Commit();
999 } 1281 }
1000 catch 1282 catch
1001 { 1283 {
1002 dbTransaction.Rollback(); 1284 dbTransaction.Rollback();
1003   1285  
1004 throw; 1286 throw;
1005 } 1287 }
1006 } 1288 }
1007 } 1289 }
1008 } 1290 }
1009 } 1291 }
1010 finally 1292 finally
1011 { 1293 {
1012 _databaseLock.Release(); 1294 _databaseLock.Release();
1013 } 1295 }
1014 } 1296 }
1015   1297  
1016 public async Task RemoveColorAsync(string hash, CancellationToken cancellationToken) 1298 public async Task RemoveColorAsync(string hash, CancellationToken cancellationToken)
1017 { 1299 {
1018 await _databaseLock.WaitAsync(cancellationToken); 1300 await _databaseLock.WaitAsync(cancellationToken);
1019 try 1301 try
1020 { 1302 {
1021 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 1303 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
1022 { 1304 {
1023 await sqliteConnection.OpenAsync(cancellationToken); 1305 await sqliteConnection.OpenAsync(cancellationToken);
1024   1306  
1025 using (var dbTransaction = sqliteConnection.BeginTransaction()) 1307 using (var dbTransaction = sqliteConnection.BeginTransaction())
1026 { 1308 {
1027 // Insert the file change. 1309 // Insert the file change.
1028 using (var sqliteCommand = 1310 using (var sqliteCommand =
1029 new SQLiteCommand(RemoveColorFromHashSql, sqliteConnection, dbTransaction)) 1311 new SQLiteCommand(RemoveColorFromHashSql, sqliteConnection, dbTransaction))
1030 { 1312 {
1031 sqliteCommand.Parameters.AddRange(new[] 1313 sqliteCommand.Parameters.AddRange(new[]
1032 { 1314 {
1033 new SQLiteParameter("@hash", hash) 1315 new SQLiteParameter("@hash", hash)
1034 }); 1316 });
1035   1317  
1036 sqliteCommand.Prepare(); 1318 sqliteCommand.Prepare();
1037   1319  
1038 try 1320 try
1039 { 1321 {
1040 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); 1322 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
1041   1323  
1042 dbTransaction.Commit(); 1324 dbTransaction.Commit();
1043 } 1325 }
1044 catch 1326 catch
1045 { 1327 {
1046 dbTransaction.Rollback(); 1328 dbTransaction.Rollback();
1047   1329  
1048 throw; 1330 throw;
1049 } 1331 }
1050 } 1332 }
1051 } 1333 }
1052 } 1334 }
1053 } 1335 }
1054 finally 1336 finally
1055 { 1337 {
1056 _databaseLock.Release(); 1338 _databaseLock.Release();
1057 } 1339 }
1058 } 1340 }
1059   1341  
1060 public async Task<SnapshotPreview> RetrievePreviewAsync(string hash, CancellationToken cancellationToken) 1342 public async Task<SnapshotPreview> RetrievePreviewAsync(string hash, CancellationToken cancellationToken)
1061 { 1343 {
1062 await _databaseLock.WaitAsync(cancellationToken); 1344 await _databaseLock.WaitAsync(cancellationToken);
1063 try 1345 try
1064 { 1346 {
1065 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 1347 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
1066 { 1348 {
1067 await sqliteConnection.OpenAsync(cancellationToken); 1349 await sqliteConnection.OpenAsync(cancellationToken);
1068   1350  
1069 // Insert the file change. 1351 // Insert the file change.
1070 using (var sqliteCommand = new SQLiteCommand(RetrievePreviewFromHashSql, sqliteConnection)) 1352 using (var sqliteCommand = new SQLiteCommand(RetrievePreviewFromHashSql, sqliteConnection))
1071 { 1353 {
1072 sqliteCommand.Parameters.AddRange(new[] 1354 sqliteCommand.Parameters.AddRange(new[]
1073 { 1355 {
1074 new SQLiteParameter("@hash", hash) 1356 new SQLiteParameter("@hash", hash)
1075 }); 1357 });
1076   1358  
1077 var note = string.Empty; 1359 var note = string.Empty;
1078   1360  
1079 sqliteCommand.Prepare(); 1361 sqliteCommand.Prepare();
1080   1362  
1081 using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken)) 1363 using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken))
1082 { 1364 {
1083 while (await sqlDataReader.ReadAsync(cancellationToken)) 1365 while (await sqlDataReader.ReadAsync(cancellationToken))
1084 { 1366 {
1085 if (!(sqlDataReader["Note"] is DBNull)) 1367 if (!(sqlDataReader["Note"] is DBNull))
1086 { 1368 {
1087 note = (string)sqlDataReader["Note"]; 1369 note = (string)sqlDataReader["Note"];
1088 } 1370 }
1089   1371  
1090 Bitmap shot = null; 1372 Bitmap shot = null;
1091   1373  
1092 if (!(sqlDataReader["Shot"] is DBNull)) 1374 if (!(sqlDataReader["Shot"] is DBNull))
1093 { 1375 {
1094 var readStream = sqlDataReader.GetStream(2); 1376 var readStream = sqlDataReader.GetStream(2);
1095   1377  
1096 readStream.Position = 0L; 1378 readStream.Position = 0L;
1097   1379  
1098 using (var zipStream = new GZipStream(readStream, CompressionMode.Decompress)) 1380 try
1099 { 1381 {
1100 using (var image = Image.FromStream(zipStream)) 1382 using (var zipStream = new GZipStream(readStream, CompressionMode.Decompress))
-   1383 {
-   1384 using (var image = Image.FromStream(zipStream))
1101 { 1385 {
-   1386 shot = new Bitmap(image);
1102 shot = new Bitmap(image); 1387 }
1103 } 1388 }
-   1389 }
-   1390 catch (Exception exception)
-   1391 {
-   1392 Log.Error(exception, $"Could not retrieve image preview for snapshot {hash}.");
-   1393 return null;
1104 } 1394 }
1105 } 1395 }
1106   1396  
1107 return new SnapshotPreview(hash, shot, note); 1397 return new SnapshotPreview(hash, shot, note);
1108 } 1398 }
1109   1399  
1110 return null; 1400 return null;
1111 } 1401 }
1112 } 1402 }
1113 } 1403 }
1114 } 1404 }
1115 finally 1405 finally
1116 { 1406 {
1117 _databaseLock.Release(); 1407 _databaseLock.Release();
1118 } 1408 }
1119 } 1409 }
1120   1410  
1121 public async Task<MemoryStream> RetrieveFileStreamAsync(string hash, CancellationToken cancellationToken) 1411 public async Task<MemoryStream> RetrieveFileStreamAsync(string hash, CancellationToken cancellationToken)
1122 { 1412 {
1123 await _databaseLock.WaitAsync(cancellationToken); 1413 await _databaseLock.WaitAsync(cancellationToken);
1124 try 1414 try
1125 { 1415 {
1126 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 1416 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
1127 { 1417 {
1128 await sqliteConnection.OpenAsync(cancellationToken); 1418 await sqliteConnection.OpenAsync(cancellationToken);
1129   1419  
1130 // Insert the file change. 1420 // Insert the file change.
1131 using (var sqliteCommand = new SQLiteCommand(RetrieveDataFromHashSql, sqliteConnection)) 1421 using (var sqliteCommand = new SQLiteCommand(RetrieveDataFromHashSql, sqliteConnection))
1132 { 1422 {
1133 sqliteCommand.Parameters.AddRange(new[] 1423 sqliteCommand.Parameters.AddRange(new[]
1134 { 1424 {
1135 new SQLiteParameter("@hash", hash) 1425 new SQLiteParameter("@hash", hash)
1136 }); 1426 });
1137   1427  
1138 sqliteCommand.Prepare(); 1428 sqliteCommand.Prepare();
1139   1429  
1140 using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken)) 1430 using (var sqlDataReader = await sqliteCommand.ExecuteReaderAsync(cancellationToken))
1141 { 1431 {
1142 while (await sqlDataReader.ReadAsync(cancellationToken)) 1432 while (await sqlDataReader.ReadAsync(cancellationToken))
1143 { 1433 {
1144 using (var readStream = sqlDataReader.GetStream(1)) 1434 using (var readStream = sqlDataReader.GetStream(1))
1145 { 1435 {
1146 using (var memoryStream = new MemoryStream()) 1436 using (var memoryStream = new MemoryStream())
1147 { 1437 {
1148 readStream.Position = 0L; 1438 readStream.Position = 0L;
1149   1439  
1150 await readStream.CopyToAsync(memoryStream); 1440 await readStream.CopyToAsync(memoryStream);
1151   1441  
1152 memoryStream.Position = 0L; 1442 memoryStream.Position = 0L;
1153   1443  
1154 using (var zipStream = new GZipStream(memoryStream, CompressionMode.Decompress)) 1444 using (var zipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
1155 { 1445 {
1156 // Do not dispose the returned stream and leave it up to callers to dispose. 1446 // Do not dispose the returned stream and leave it up to callers to dispose.
1157 var outputStream = new MemoryStream(); 1447 var outputStream = new MemoryStream();
1158   1448  
1159 await zipStream.CopyToAsync(outputStream); 1449 await zipStream.CopyToAsync(outputStream);
1160   1450  
1161 outputStream.Position = 0L; 1451 outputStream.Position = 0L;
1162   1452  
1163 return outputStream; 1453 return outputStream;
1164 } 1454 }
1165 } 1455 }
1166 } 1456 }
1167 } 1457 }
1168   1458  
1169 return null; 1459 return null;
1170 } 1460 }
1171 } 1461 }
1172 } 1462 }
1173 } 1463 }
1174 finally 1464 finally
1175 { 1465 {
1176 _databaseLock.Release(); 1466 _databaseLock.Release();
1177 } 1467 }
1178 } 1468 }
1179   1469  
1180 public async Task RelocateFileAsync(string hash, string path, CancellationToken cancellationToken) 1470 public async Task RelocateFileAsync(string hash, string path, CancellationToken cancellationToken)
1181 { 1471 {
1182 await _databaseLock.WaitAsync(cancellationToken); 1472 await _databaseLock.WaitAsync(cancellationToken);
1183 try 1473 try
1184 { 1474 {
1185 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 1475 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
1186 { 1476 {
1187 await sqliteConnection.OpenAsync(cancellationToken); 1477 await sqliteConnection.OpenAsync(cancellationToken);
1188   1478  
1189 using (var dbTransaction = sqliteConnection.BeginTransaction()) 1479 using (var dbTransaction = sqliteConnection.BeginTransaction())
1190 { 1480 {
1191 // Insert the file change. 1481 // Insert the file change.
1192 using (var sqliteCommand = 1482 using (var sqliteCommand =
1193 new SQLiteCommand(RelocateFileFromHashSql, sqliteConnection, dbTransaction)) 1483 new SQLiteCommand(RelocateFileFromHashSql, sqliteConnection, dbTransaction))
1194 { 1484 {
1195 sqliteCommand.Parameters.AddRange(new[] 1485 sqliteCommand.Parameters.AddRange(new[]
1196 { 1486 {
1197 new SQLiteParameter("@hash", hash), 1487 new SQLiteParameter("@hash", hash),
1198 new SQLiteParameter("@path", path) 1488 new SQLiteParameter("@path", path)
1199 }); 1489 });
1200   1490  
1201 sqliteCommand.Prepare(); 1491 sqliteCommand.Prepare();
1202   1492  
1203 try 1493 try
1204 { 1494 {
1205 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); 1495 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
1206   1496  
1207 dbTransaction.Commit(); 1497 dbTransaction.Commit();
1208 } 1498 }
1209 catch 1499 catch
1210 { 1500 {
1211 dbTransaction.Rollback(); 1501 dbTransaction.Rollback();
1212   1502  
1213 throw; 1503 throw;
1214 } 1504 }
1215 } 1505 }
1216 } 1506 }
1217 } 1507 }
1218 } 1508 }
1219   1509  
1220 finally 1510 finally
1221 { 1511 {
1222 _databaseLock.Release(); 1512 _databaseLock.Release();
1223 } 1513 }
1224 } 1514 }
1225   1515  
1226 public async Task UpdateNoteAsync(string hash, string note, CancellationToken cancellationToken) 1516 public async Task UpdateNoteAsync(string hash, string note, CancellationToken cancellationToken)
1227 { 1517 {
1228 await _databaseLock.WaitAsync(cancellationToken); 1518 await _databaseLock.WaitAsync(cancellationToken);
1229 try 1519 try
1230 { 1520 {
1231 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 1521 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
1232 { 1522 {
1233 await sqliteConnection.OpenAsync(cancellationToken); 1523 await sqliteConnection.OpenAsync(cancellationToken);
1234   1524  
1235 using (var dbTransaction = sqliteConnection.BeginTransaction()) 1525 using (var dbTransaction = sqliteConnection.BeginTransaction())
1236 { 1526 {
1237 // Insert the file change. 1527 // Insert the file change.
1238 using (var sqliteCommand = 1528 using (var sqliteCommand =
1239 new SQLiteCommand(UpdateNoteFromHashSql, sqliteConnection, dbTransaction)) 1529 new SQLiteCommand(UpdateNoteFromHashSql, sqliteConnection, dbTransaction))
1240 { 1530 {
1241 sqliteCommand.Parameters.AddRange(new[] 1531 sqliteCommand.Parameters.AddRange(new[]
1242 { 1532 {
1243 new SQLiteParameter("@hash", hash), 1533 new SQLiteParameter("@hash", hash),
1244 new SQLiteParameter("@note", note) 1534 new SQLiteParameter("@note", note)
1245 }); 1535 });
1246   1536  
1247 sqliteCommand.Prepare(); 1537 sqliteCommand.Prepare();
1248   1538  
1249 try 1539 try
1250 { 1540 {
1251 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); 1541 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
1252   1542  
1253 dbTransaction.Commit(); 1543 dbTransaction.Commit();
1254   1544  
1255 SnapshotNoteUpdate?.Invoke(this, new SnapshotNoteUpdateSuccessEventArgs(note)); 1545 SnapshotNoteUpdate?.Invoke(this, new SnapshotNoteUpdateSuccessEventArgs(note));
1256 } 1546 }
1257 catch 1547 catch
1258 { 1548 {
1259 dbTransaction.Rollback(); 1549 dbTransaction.Rollback();
1260   1550  
1261 SnapshotNoteUpdate?.Invoke(this, new SnapshotNoteUpdateFailureEventArgs()); 1551 SnapshotNoteUpdate?.Invoke(this, new SnapshotNoteUpdateFailureEventArgs());
1262   1552  
1263 throw; 1553 throw;
1264 } 1554 }
1265 } 1555 }
1266 } 1556 }
1267 } 1557 }
1268 } 1558 }
1269 finally 1559 finally
1270 { 1560 {
1271 _databaseLock.Release(); 1561 _databaseLock.Release();
1272 } 1562 }
1273 } 1563 }
1274   1564  
1275 public async Task<string> UpdateFileAsync(string hash, byte[] data, CancellationToken cancellationToken) 1565 public async Task<string> UpdateFileAsync(string hash, byte[] data, CancellationToken cancellationToken)
1276 { 1566 {
1277 await _databaseLock.WaitAsync(cancellationToken); 1567 await _databaseLock.WaitAsync(cancellationToken);
1278 try 1568 try
1279 { 1569 {
1280 using (var dataMemoryStream = new MemoryStream(data)) 1570 using (var dataMemoryStream = new MemoryStream(data))
1281 { 1571 {
1282 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 1572 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
1283 { 1573 {
1284 await sqliteConnection.OpenAsync(cancellationToken); 1574 await sqliteConnection.OpenAsync(cancellationToken);
1285   1575  
1286 using (var dbTransaction = sqliteConnection.BeginTransaction()) 1576 using (var dbTransaction = sqliteConnection.BeginTransaction())
1287 { 1577 {
1288 try 1578 try
1289 { 1579 {
1290 using (var md5 = MD5.Create()) 1580 using (var md5 = MD5.Create())
1291 { 1581 {
1292 using (var hashMemoryStream = new MemoryStream()) 1582 using (var hashMemoryStream = new MemoryStream())
1293 { 1583 {
1294 dataMemoryStream.Position = 0L; 1584 dataMemoryStream.Position = 0L;
1295 await dataMemoryStream.CopyToAsync(hashMemoryStream); 1585 await dataMemoryStream.CopyToAsync(hashMemoryStream);
1296   1586  
1297 hashMemoryStream.Position = 0L; 1587 hashMemoryStream.Position = 0L;
1298 var recomputedHash = md5.ComputeHash(hashMemoryStream); 1588 var recomputedHash = md5.ComputeHash(hashMemoryStream);
1299 var hashHex = BitConverter.ToString(recomputedHash).Replace("-", "") 1589 var hashHex = BitConverter.ToString(recomputedHash).Replace("-", "")
1300 .ToLowerInvariant(); 1590 .ToLowerInvariant();
1301   1591  
1302 using (var fileMemoryStream = new MemoryStream()) 1592 using (var fileMemoryStream = new MemoryStream())
1303 { 1593 {
1304 using (var fileZipStream = 1594 using (var fileZipStream =
1305 new GZipStream(fileMemoryStream, CompressionMode.Compress, true)) 1595 new GZipStream(fileMemoryStream, CompressionMode.Compress, true))
1306 { 1596 {
1307 dataMemoryStream.Position = 0L; 1597 dataMemoryStream.Position = 0L;
1308 await dataMemoryStream.CopyToAsync(fileZipStream); 1598 await dataMemoryStream.CopyToAsync(fileZipStream);
1309 fileZipStream.Close(); 1599 fileZipStream.Close();
1310   1600  
1311 fileMemoryStream.Position = 0L; 1601 fileMemoryStream.Position = 0L;
1312   1602  
1313 // Insert the file change. 1603 // Insert the file change.
1314 using (var sqliteCommand = 1604 using (var sqliteCommand =
1315 new SQLiteCommand(UpdateFileSql, sqliteConnection, 1605 new SQLiteCommand(UpdateFileSql, sqliteConnection,
1316 dbTransaction)) 1606 dbTransaction))
1317 { 1607 {
1318 sqliteCommand.Parameters.AddRange(new[] 1608 sqliteCommand.Parameters.AddRange(new[]
1319 { 1609 {
1320 new SQLiteParameter("@dataLength", fileMemoryStream.Length), 1610 new SQLiteParameter("@dataLength", fileMemoryStream.Length),
1321 new SQLiteParameter("@recomputedHash", hashHex), 1611 new SQLiteParameter("@recomputedHash", hashHex),
1322 new SQLiteParameter("@hash", hash) 1612 new SQLiteParameter("@hash", hash)
1323 }); 1613 });
1324   1614  
1325 sqliteCommand.Prepare(); 1615 sqliteCommand.Prepare();
1326 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); 1616 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
1327 } 1617 }
1328   1618  
1329 using (var sqliteCommand = 1619 using (var sqliteCommand =
1330 new SQLiteCommand(GetRowFromHashSql, sqliteConnection, 1620 new SQLiteCommand(GetRowFromHashSql, sqliteConnection,
1331 dbTransaction)) 1621 dbTransaction))
1332 { 1622 {
1333 sqliteCommand.Parameters.AddRange(new[] 1623 sqliteCommand.Parameters.AddRange(new[]
1334 { 1624 {
1335 new SQLiteParameter("@hash", hashHex) 1625 new SQLiteParameter("@hash", hashHex)
1336 }); 1626 });
1337   1627  
1338 sqliteCommand.Prepare(); 1628 sqliteCommand.Prepare();
1339   1629  
1340 using (var sqlDataReader = 1630 using (var sqlDataReader =
1341 await sqliteCommand.ExecuteReaderAsync(cancellationToken)) 1631 await sqliteCommand.ExecuteReaderAsync(cancellationToken))
1342 { 1632 {
1343 while (await sqlDataReader.ReadAsync(cancellationToken)) 1633 while (await sqlDataReader.ReadAsync(cancellationToken))
1344 { 1634 {
1345 if (sqlDataReader["id"] is long rowId) 1635 if (sqlDataReader["id"] is long rowId)
1346 { 1636 {
1347 using (var sqliteBlob = SQLiteBlob.Create( 1637 using (var sqliteBlob = SQLiteBlob.Create(
1348 sqliteConnection, 1638 sqliteConnection,
1349 "main", 1639 "main",
1350 "Snapshots", 1640 "Snapshots",
1351 "Data", 1641 "Data",
1352 rowId, false)) 1642 rowId, false))
1353 { 1643 {
1354 var fileMemoryStreamData = 1644 var fileMemoryStreamData =
1355 fileMemoryStream.ToArray(); 1645 fileMemoryStream.ToArray();
1356   1646  
1357 sqliteBlob.Write(fileMemoryStreamData, 1647 sqliteBlob.Write(fileMemoryStreamData,
1358 fileMemoryStreamData.Length, 1648 fileMemoryStreamData.Length,
1359 0); 1649 0);
1360 } 1650 }
1361 } 1651 }
1362 } 1652 }
1363 } 1653 }
1364 } 1654 }
1365   1655  
1366 dbTransaction.Commit(); 1656 dbTransaction.Commit();
1367   1657  
1368 SnapshotDataUpdate?.Invoke(this, 1658 SnapshotDataUpdate?.Invoke(this,
1369 new SnapshotDataUpdateSuccessEventArgs(hash, hashHex)); 1659 new SnapshotDataUpdateSuccessEventArgs(hash, hashHex));
1370   1660  
1371 return hashHex; 1661 return hashHex;
1372 } 1662 }
1373 } 1663 }
1374 } 1664 }
1375 } 1665 }
1376 } 1666 }
1377 catch 1667 catch
1378 { 1668 {
1379 dbTransaction.Rollback(); 1669 dbTransaction.Rollback();
1380   1670  
1381 SnapshotDataUpdate?.Invoke(this, new SnapshotDataUpdateFailureEventArgs(hash)); 1671 SnapshotDataUpdate?.Invoke(this, new SnapshotDataUpdateFailureEventArgs(hash));
1382   1672  
1383 throw; 1673 throw;
1384 } 1674 }
1385 } 1675 }
1386 } 1676 }
1387 } 1677 }
1388 } 1678 }
1389 finally 1679 finally
1390 { 1680 {
1391 _databaseLock.Release(); 1681 _databaseLock.Release();
1392 } 1682 }
1393 } 1683 }
1394   1684  
1395 public async Task UpdateHashAsync(string from, string to, CancellationToken cancellationToken) 1685 public async Task UpdateHashAsync(string from, string to, CancellationToken cancellationToken)
1396 { 1686 {
1397 await _databaseLock.WaitAsync(cancellationToken); 1687 await _databaseLock.WaitAsync(cancellationToken);
1398 try 1688 try
1399 { 1689 {
1400 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 1690 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
1401 { 1691 {
1402 await sqliteConnection.OpenAsync(cancellationToken); 1692 await sqliteConnection.OpenAsync(cancellationToken);
1403   1693  
1404 using (var dbTransaction = sqliteConnection.BeginTransaction()) 1694 using (var dbTransaction = sqliteConnection.BeginTransaction())
1405 { 1695 {
1406 // Insert the file change. 1696 // Insert the file change.
1407 using (var sqliteCommand = 1697 using (var sqliteCommand =
1408 new SQLiteCommand(UpdateHashFromHashSql, sqliteConnection, dbTransaction)) 1698 new SQLiteCommand(UpdateHashFromHashSql, sqliteConnection, dbTransaction))
1409 { 1699 {
1410 sqliteCommand.Parameters.AddRange(new[] 1700 sqliteCommand.Parameters.AddRange(new[]
1411 { 1701 {
1412 new SQLiteParameter("@from", from), 1702 new SQLiteParameter("@from", from),
1413 new SQLiteParameter("@to", to) 1703 new SQLiteParameter("@to", to)
1414 }); 1704 });
1415   1705  
1416 sqliteCommand.Prepare(); 1706 sqliteCommand.Prepare();
1417   1707  
1418 try 1708 try
1419 { 1709 {
1420 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); 1710 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
1421   1711  
1422 dbTransaction.Commit(); 1712 dbTransaction.Commit();
1423 } 1713 }
1424 catch 1714 catch
1425 { 1715 {
1426 dbTransaction.Rollback(); 1716 dbTransaction.Rollback();
1427   1717  
1428 throw; 1718 throw;
1429 } 1719 }
1430 } 1720 }
1431 } 1721 }
1432 } 1722 }
1433 } 1723 }
1434 finally 1724 finally
1435 { 1725 {
1436 _databaseLock.Release(); 1726 _databaseLock.Release();
1437 } 1727 }
1438 } 1728 }
1439   1729  
1440 #endregion 1730 #endregion
1441   1731  
1442 #region Private Methods 1732 #region Private Methods
1443   1733  
1444 private async Task SetAutoVacuum(CancellationToken cancellationToken) 1734 private async Task SetAutoVacuum(CancellationToken cancellationToken)
1445 { 1735 {
1446 await _databaseLock.WaitAsync(cancellationToken); 1736 await _databaseLock.WaitAsync(cancellationToken);
1447 try 1737 try
1448 { 1738 {
1449 using (var sqliteConnection = 1739 using (var sqliteConnection =
1450 new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 1740 new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
1451 { 1741 {
1452 await sqliteConnection.OpenAsync(cancellationToken); 1742 await sqliteConnection.OpenAsync(cancellationToken);
1453   1743  
1454 // Set auto vacuum. 1744 // Set auto vacuum.
1455 using (var sqliteCommand = new SQLiteCommand(SetAutoVacuumSql, sqliteConnection)) 1745 using (var sqliteCommand = new SQLiteCommand(SetAutoVacuumSql, sqliteConnection))
1456 { 1746 {
1457 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); 1747 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
1458 } 1748 }
1459 } 1749 }
1460 } 1750 }
1461 finally 1751 finally
1462 { 1752 {
1463 _databaseLock.Release(); 1753 _databaseLock.Release();
1464 } 1754 }
1465 } 1755 }
1466   1756  
1467 private async Task CreateDatabase(CancellationToken cancellationToken) 1757 private async Task CreateDatabase(CancellationToken cancellationToken)
1468 { 1758 {
1469 await _databaseLock.WaitAsync(cancellationToken); 1759 await _databaseLock.WaitAsync(cancellationToken);
1470 try 1760 try
1471 { 1761 {
1472 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString)) 1762 using (var sqliteConnection = new SQLiteConnection(_sqliteConnectionStringBuilder.ConnectionString))
1473 { 1763 {
1474 await sqliteConnection.OpenAsync(cancellationToken); 1764 await sqliteConnection.OpenAsync(cancellationToken);
1475   1765  
1476 using (var dbTransaction = sqliteConnection.BeginTransaction()) 1766 using (var dbTransaction = sqliteConnection.BeginTransaction())
1477 { 1767 {
1478 // Create the table if it does not exist. 1768 // Create the table if it does not exist.
1479 using (var sqliteCommand = new SQLiteCommand(CreateTableSql, sqliteConnection, dbTransaction)) 1769 using (var sqliteCommand = new SQLiteCommand(CreateTableSql, sqliteConnection, dbTransaction))
1480 { 1770 {
1481 try 1771 try
1482 { 1772 {
1483 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken); 1773 await sqliteCommand.ExecuteNonQueryAsync(cancellationToken);
1484   1774  
1485 dbTransaction.Commit(); 1775 dbTransaction.Commit();
1486 } 1776 }
1487 catch 1777 catch
1488 { 1778 {
1489 dbTransaction.Rollback(); 1779 dbTransaction.Rollback();
1490   1780  
1491 throw; 1781 throw;
1492 } 1782 }
1493 } 1783 }
1494 } 1784 }
1495 } 1785 }
1496 } 1786 }
1497 finally 1787 finally
1498 { 1788 {
1499 _databaseLock.Release(); 1789 _databaseLock.Release();
1500 } 1790 }
1501 } 1791 }
1502   1792  
1503 #endregion 1793 #endregion
-   1794  
-   1795  
1504 } 1796 }
1505 } 1797 }
1506   1798  
1507
Generated by GNU Enscript 1.6.5.90.
1799
Generated by GNU Enscript 1.6.5.90.
1508   1800  
1509   1801  
1510   1802