SQLiteAsync.cs 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628
  1. //
  2. // Copyright (c) 2012-2024 Krueger Systems, Inc.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. using System;
  23. using System.Collections;
  24. using System.Collections.Generic;
  25. using System.Linq;
  26. using System.Linq.Expressions;
  27. using System.Threading;
  28. using System.Threading.Tasks;
  29. #pragma warning disable 1591 // XML Doc Comments
  30. namespace SQLite
  31. {
  32. public interface ISQLiteAsyncConnection
  33. {
  34. string DatabasePath { get; }
  35. int LibVersionNumber { get; }
  36. string DateTimeStringFormat { get; }
  37. bool StoreDateTimeAsTicks { get; }
  38. bool StoreTimeSpanAsTicks { get; }
  39. bool Trace { get; set; }
  40. Action<string> Tracer { get; set; }
  41. bool TimeExecution { get; set; }
  42. IEnumerable<TableMapping> TableMappings { get; }
  43. Task BackupAsync (string destinationDatabasePath, string databaseName = "main");
  44. Task CloseAsync ();
  45. Task<int> CreateIndexAsync (string tableName, string columnName, bool unique = false);
  46. Task<int> CreateIndexAsync (string indexName, string tableName, string columnName, bool unique = false);
  47. Task<int> CreateIndexAsync (string tableName, string[] columnNames, bool unique = false);
  48. Task<int> CreateIndexAsync (string indexName, string tableName, string[] columnNames, bool unique = false);
  49. Task<int> CreateIndexAsync<T> (Expression<Func<T, object>> property, bool unique = false);
  50. Task<CreateTableResult> CreateTableAsync<T> (CreateFlags createFlags = CreateFlags.None,string tableName = "") where T : new();
  51. Task<CreateTableResult> CreateTableAsync (Type ty, CreateFlags createFlags = CreateFlags.None,string tableName = "");
  52. Task<CreateTablesResult> CreateTablesAsync<T, T2> (CreateFlags createFlags = CreateFlags.None)
  53. where T : new()
  54. where T2 : new();
  55. Task<CreateTablesResult> CreateTablesAsync<T, T2, T3> (CreateFlags createFlags = CreateFlags.None)
  56. where T : new()
  57. where T2 : new()
  58. where T3 : new();
  59. Task<CreateTablesResult> CreateTablesAsync<T, T2, T3, T4> (CreateFlags createFlags = CreateFlags.None)
  60. where T : new()
  61. where T2 : new()
  62. where T3 : new()
  63. where T4 : new();
  64. Task<CreateTablesResult> CreateTablesAsync<T, T2, T3, T4, T5> (CreateFlags createFlags = CreateFlags.None)
  65. where T : new()
  66. where T2 : new()
  67. where T3 : new()
  68. where T4 : new()
  69. where T5 : new();
  70. Task<CreateTablesResult> CreateTablesAsync (CreateFlags createFlags = CreateFlags.None, params Type[] types);
  71. Task<IEnumerable<T>> DeferredQueryAsync<T> (string query, params object[] args) where T : new();
  72. Task<IEnumerable<object>> DeferredQueryAsync (TableMapping map, string query, params object[] args);
  73. Task<int> DeleteAllAsync<T> (string tableName = "");
  74. Task<int> DeleteAllAsync (TableMapping map);
  75. Task<int> DeleteAsync (object objectToDelete,string tableName = "");
  76. Task<int> DeleteAsync<T> (object primaryKey,string tableName = "");
  77. Task<int> DeleteAsync (object primaryKey, TableMapping map);
  78. Task<int> DropTableAsync<T> (string tableName = "") where T : new();
  79. Task<int> DropTableAsync (TableMapping map);
  80. Task EnableLoadExtensionAsync (bool enabled);
  81. Task EnableWriteAheadLoggingAsync ();
  82. Task<int> ExecuteAsync (string query, params object[] args);
  83. Task<T> ExecuteScalarAsync<T> (string query, params object[] args);
  84. Task<T> FindAsync<T> (object pk,string tableName = "") where T : new();
  85. Task<object> FindAsync (object pk, TableMapping map);
  86. Task<T> FindAsync<T> (Expression<Func<T, bool>> predicate,string tableName = "") where T : new();
  87. Task<T> FindWithQueryAsync<T> (string query, params object[] args) where T : new();
  88. Task<object> FindWithQueryAsync (TableMapping map, string query, params object[] args);
  89. Task<T> GetAsync<T> (object pk,string tableName = "") where T : new();
  90. Task<object> GetAsync (object pk, TableMapping map);
  91. Task<T> GetAsync<T> (Expression<Func<T, bool>> predicate,string tableName = "") where T : new();
  92. TimeSpan GetBusyTimeout ();
  93. SQLiteConnectionWithLock GetConnection ();
  94. Task<TableMapping> GetMappingAsync (Type type, CreateFlags createFlags = CreateFlags.None,string tableName = "");
  95. Task<TableMapping> GetMappingAsync<T> (CreateFlags createFlags = CreateFlags.None,string tableName = "") where T : new();
  96. Task<List<SQLiteConnection.ColumnInfo>> GetTableInfoAsync (string tableName);
  97. Task<int> InsertAllAsync (IEnumerable objects, bool runInTransaction = true,string tableName = "");
  98. Task<int> InsertAllAsync (IEnumerable objects, string extra, bool runInTransaction = true,string tableName = "");
  99. Task<int> InsertAllAsync (IEnumerable objects, Type objType, bool runInTransaction = true,string tableName = "");
  100. Task<int> InsertAsync (object obj,string tableName = "");
  101. Task<int> InsertAsync (object obj, Type objType,string tableName = "");
  102. Task<int> InsertAsync (object obj, string extra,string tableName = "");
  103. Task<int> InsertAsync (object obj, string extra, Type objType,string tableName = "");
  104. Task<int> InsertOrReplaceAsync (object obj,string tableName = "");
  105. Task<int> InsertOrReplaceAsync (object obj, Type objType,string tableName = "");
  106. Task<List<T>> QueryAsync<T> (string query, params object[] args) where T : new();
  107. Task<List<object>> QueryAsync (TableMapping map, string query, params object[] args);
  108. Task<List<T>> QueryScalarsAsync<T> (string query, params object[] args);
  109. Task ReKeyAsync (string key);
  110. Task ReKeyAsync (byte[] key);
  111. Task RunInTransactionAsync (Action<SQLiteConnection> action);
  112. Task SetBusyTimeoutAsync (TimeSpan value);
  113. AsyncTableQuery<T> Table<T> (string tableName = "") where T : new();
  114. Task<int> UpdateAllAsync (IEnumerable objects, bool runInTransaction = true,string tableName = "");
  115. Task<int> UpdateAsync (object obj,string tableName = "");
  116. Task<int> UpdateAsync (object obj, Type objType,string tableName = "");
  117. }
  118. /// <summary>
  119. /// A pooled asynchronous connection to a SQLite database.
  120. /// </summary>
  121. public partial class SQLiteAsyncConnection : ISQLiteAsyncConnection
  122. {
  123. readonly SQLiteConnectionString _connectionString;
  124. /// <summary>
  125. /// Constructs a new SQLiteAsyncConnection and opens a pooled SQLite database specified by databasePath.
  126. /// </summary>
  127. /// <param name="databasePath">
  128. /// Specifies the path to the database file.
  129. /// </param>
  130. /// <param name="storeDateTimeAsTicks">
  131. /// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
  132. /// absolutely do want to store them as Ticks in all new projects. The value of false is
  133. /// only here for backwards compatibility. There is a *significant* speed advantage, with no
  134. /// down sides, when setting storeDateTimeAsTicks = true.
  135. /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
  136. /// the storeDateTimeAsTicks parameter.
  137. /// </param>
  138. public SQLiteAsyncConnection (string databasePath, bool storeDateTimeAsTicks = true)
  139. : this (new SQLiteConnectionString (databasePath, SQLiteOpenFlags.Create | SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.FullMutex, storeDateTimeAsTicks))
  140. {
  141. }
  142. /// <summary>
  143. /// Constructs a new SQLiteAsyncConnection and opens a pooled SQLite database specified by databasePath.
  144. /// </summary>
  145. /// <param name="databasePath">
  146. /// Specifies the path to the database file.
  147. /// </param>
  148. /// <param name="openFlags">
  149. /// Flags controlling how the connection should be opened.
  150. /// Async connections should have the FullMutex flag set to provide best performance.
  151. /// </param>
  152. /// <param name="storeDateTimeAsTicks">
  153. /// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
  154. /// absolutely do want to store them as Ticks in all new projects. The value of false is
  155. /// only here for backwards compatibility. There is a *significant* speed advantage, with no
  156. /// down sides, when setting storeDateTimeAsTicks = true.
  157. /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
  158. /// the storeDateTimeAsTicks parameter.
  159. /// </param>
  160. public SQLiteAsyncConnection (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true)
  161. : this (new SQLiteConnectionString (databasePath, openFlags, storeDateTimeAsTicks))
  162. {
  163. }
  164. /// <summary>
  165. /// Constructs a new SQLiteAsyncConnection and opens a pooled SQLite database
  166. /// using the given connection string.
  167. /// </summary>
  168. /// <param name="connectionString">
  169. /// Details on how to find and open the database.
  170. /// </param>
  171. public SQLiteAsyncConnection (SQLiteConnectionString connectionString)
  172. {
  173. _connectionString = connectionString;
  174. }
  175. /// <summary>
  176. /// Gets the database path used by this connection.
  177. /// </summary>
  178. public string DatabasePath => GetConnection ().DatabasePath;
  179. /// <summary>
  180. /// Gets the SQLite library version number. 3007014 would be v3.7.14
  181. /// </summary>
  182. public int LibVersionNumber => GetConnection ().LibVersionNumber;
  183. /// <summary>
  184. /// The format to use when storing DateTime properties as strings. Ignored if StoreDateTimeAsTicks is true.
  185. /// </summary>
  186. /// <value>The date time string format.</value>
  187. public string DateTimeStringFormat => GetConnection ().DateTimeStringFormat;
  188. /// <summary>
  189. /// The amount of time to wait for a table to become unlocked.
  190. /// </summary>
  191. public TimeSpan GetBusyTimeout ()
  192. {
  193. return GetConnection ().BusyTimeout;
  194. }
  195. /// <summary>
  196. /// Sets the amount of time to wait for a table to become unlocked.
  197. /// </summary>
  198. public Task SetBusyTimeoutAsync (TimeSpan value)
  199. {
  200. return ReadAsync<object> (conn => {
  201. conn.BusyTimeout = value;
  202. return null;
  203. });
  204. }
  205. /// <summary>
  206. /// Enables the write ahead logging. WAL is significantly faster in most scenarios
  207. /// by providing better concurrency and better disk IO performance than the normal
  208. /// journal mode. You only need to call this function once in the lifetime of the database.
  209. /// </summary>
  210. public Task EnableWriteAheadLoggingAsync ()
  211. {
  212. return WriteAsync<object> (conn => {
  213. conn.EnableWriteAheadLogging ();
  214. return null;
  215. });
  216. }
  217. /// <summary>
  218. /// Whether to store DateTime properties as ticks (true) or strings (false).
  219. /// </summary>
  220. public bool StoreDateTimeAsTicks => GetConnection ().StoreDateTimeAsTicks;
  221. /// <summary>
  222. /// Whether to store TimeSpan properties as ticks (true) or strings (false).
  223. /// </summary>
  224. public bool StoreTimeSpanAsTicks => GetConnection ().StoreTimeSpanAsTicks;
  225. /// <summary>
  226. /// Whether to writer queries to <see cref="Tracer"/> during execution.
  227. /// </summary>
  228. /// <value>The tracer.</value>
  229. public bool Trace {
  230. get { return GetConnection ().Trace; }
  231. set { GetConnection ().Trace = value; }
  232. }
  233. /// <summary>
  234. /// The delegate responsible for writing trace lines.
  235. /// </summary>
  236. /// <value>The tracer.</value>
  237. public Action<string> Tracer {
  238. get { return GetConnection ().Tracer; }
  239. set { GetConnection ().Tracer = value; }
  240. }
  241. /// <summary>
  242. /// Whether Trace lines should be written that show the execution time of queries.
  243. /// </summary>
  244. public bool TimeExecution {
  245. get { return GetConnection ().TimeExecution; }
  246. set { GetConnection ().TimeExecution = value; }
  247. }
  248. /// <summary>
  249. /// Returns the mappings from types to tables that the connection
  250. /// currently understands.
  251. /// </summary>
  252. public IEnumerable<TableMapping> TableMappings => GetConnection ().TableMappings;
  253. /// <summary>
  254. /// Closes all connections to all async databases.
  255. /// You should *never* need to do this.
  256. /// This is a blocking operation that will return when all connections
  257. /// have been closed.
  258. /// </summary>
  259. public static void ResetPool ()
  260. {
  261. SQLiteConnectionPool.Shared.Reset ();
  262. }
  263. /// <summary>
  264. /// Gets the pooled lockable connection used by this async connection.
  265. /// You should never need to use this. This is provided only to add additional
  266. /// functionality to SQLite-net. If you use this connection, you must use
  267. /// the Lock method on it while using it.
  268. /// </summary>
  269. public SQLiteConnectionWithLock GetConnection ()
  270. {
  271. return SQLiteConnectionPool.Shared.GetConnection (_connectionString);
  272. }
  273. SQLiteConnectionWithLock GetConnectionAndTransactionLock (out object transactionLock)
  274. {
  275. return SQLiteConnectionPool.Shared.GetConnectionAndTransactionLock (_connectionString, out transactionLock);
  276. }
  277. /// <summary>
  278. /// Closes any pooled connections used by the database.
  279. /// </summary>
  280. public Task CloseAsync ()
  281. {
  282. return Task.Factory.StartNew (() => {
  283. SQLiteConnectionPool.Shared.CloseConnection (_connectionString);
  284. }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
  285. }
  286. Task<T> ReadAsync<T> (Func<SQLiteConnectionWithLock, T> read)
  287. {
  288. return Task.Factory.StartNew (() => {
  289. var conn = GetConnection ();
  290. using (conn.Lock ()) {
  291. return read (conn);
  292. }
  293. }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
  294. }
  295. Task<T> WriteAsync<T> (Func<SQLiteConnectionWithLock, T> write)
  296. {
  297. return Task.Factory.StartNew (() => {
  298. var conn = GetConnection ();
  299. using (conn.Lock ()) {
  300. return write (conn);
  301. }
  302. }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
  303. }
  304. Task<T> TransactAsync<T> (Func<SQLiteConnectionWithLock, T> transact)
  305. {
  306. return Task.Factory.StartNew (() => {
  307. var conn = GetConnectionAndTransactionLock (out var transactionLock);
  308. lock (transactionLock) {
  309. using (conn.Lock ()) {
  310. return transact (conn);
  311. }
  312. }
  313. }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
  314. }
  315. /// <summary>
  316. /// Enable or disable extension loading.
  317. /// </summary>
  318. public Task EnableLoadExtensionAsync (bool enabled)
  319. {
  320. return WriteAsync<object> (conn => {
  321. conn.EnableLoadExtension (enabled);
  322. return null;
  323. });
  324. }
  325. /// <summary>
  326. /// Executes a "create table if not exists" on the database. It also
  327. /// creates any specified indexes on the columns of the table. It uses
  328. /// a schema automatically generated from the specified type. You can
  329. /// later access this schema by calling GetMapping.
  330. /// </summary>
  331. /// <returns>
  332. /// Whether the table was created or migrated.
  333. /// </returns>
  334. public Task<CreateTableResult> CreateTableAsync<T> (CreateFlags createFlags = CreateFlags.None,string tableName = "")
  335. where T : new()
  336. {
  337. return WriteAsync (conn => conn.CreateTable<T> (createFlags,tableName));
  338. }
  339. /// <summary>
  340. /// Executes a "create table if not exists" on the database. It also
  341. /// creates any specified indexes on the columns of the table. It uses
  342. /// a schema automatically generated from the specified type. You can
  343. /// later access this schema by calling GetMapping.
  344. /// </summary>
  345. /// <param name="ty">Type to reflect to a database table.</param>
  346. /// <param name="createFlags">Optional flags allowing implicit PK and indexes based on naming conventions.</param>
  347. /// <returns>
  348. /// Whether the table was created or migrated.
  349. /// </returns>
  350. public Task<CreateTableResult> CreateTableAsync (Type ty, CreateFlags createFlags = CreateFlags.None,string tableName = "")
  351. {
  352. return WriteAsync (conn => conn.CreateTable (ty, createFlags,tableName));
  353. }
  354. /// <summary>
  355. /// Executes a "create table if not exists" on the database for each type. It also
  356. /// creates any specified indexes on the columns of the table. It uses
  357. /// a schema automatically generated from the specified type. You can
  358. /// later access this schema by calling GetMapping.
  359. /// </summary>
  360. /// <returns>
  361. /// Whether the table was created or migrated for each type.
  362. /// </returns>
  363. public Task<CreateTablesResult> CreateTablesAsync<T, T2> (CreateFlags createFlags = CreateFlags.None)
  364. where T : new()
  365. where T2 : new()
  366. {
  367. return CreateTablesAsync (createFlags, typeof (T), typeof (T2));
  368. }
  369. /// <summary>
  370. /// Executes a "create table if not exists" on the database for each type. It also
  371. /// creates any specified indexes on the columns of the table. It uses
  372. /// a schema automatically generated from the specified type. You can
  373. /// later access this schema by calling GetMapping.
  374. /// </summary>
  375. /// <returns>
  376. /// Whether the table was created or migrated for each type.
  377. /// </returns>
  378. public Task<CreateTablesResult> CreateTablesAsync<T, T2, T3> (CreateFlags createFlags = CreateFlags.None)
  379. where T : new()
  380. where T2 : new()
  381. where T3 : new()
  382. {
  383. return CreateTablesAsync (createFlags, typeof (T), typeof (T2), typeof (T3));
  384. }
  385. /// <summary>
  386. /// Executes a "create table if not exists" on the database for each type. It also
  387. /// creates any specified indexes on the columns of the table. It uses
  388. /// a schema automatically generated from the specified type. You can
  389. /// later access this schema by calling GetMapping.
  390. /// </summary>
  391. /// <returns>
  392. /// Whether the table was created or migrated for each type.
  393. /// </returns>
  394. public Task<CreateTablesResult> CreateTablesAsync<T, T2, T3, T4> (CreateFlags createFlags = CreateFlags.None)
  395. where T : new()
  396. where T2 : new()
  397. where T3 : new()
  398. where T4 : new()
  399. {
  400. return CreateTablesAsync (createFlags, typeof (T), typeof (T2), typeof (T3), typeof (T4));
  401. }
  402. /// <summary>
  403. /// Executes a "create table if not exists" on the database for each type. It also
  404. /// creates any specified indexes on the columns of the table. It uses
  405. /// a schema automatically generated from the specified type. You can
  406. /// later access this schema by calling GetMapping.
  407. /// </summary>
  408. /// <returns>
  409. /// Whether the table was created or migrated for each type.
  410. /// </returns>
  411. public Task<CreateTablesResult> CreateTablesAsync<T, T2, T3, T4, T5> (CreateFlags createFlags = CreateFlags.None)
  412. where T : new()
  413. where T2 : new()
  414. where T3 : new()
  415. where T4 : new()
  416. where T5 : new()
  417. {
  418. return CreateTablesAsync (createFlags, typeof (T), typeof (T2), typeof (T3), typeof (T4), typeof (T5));
  419. }
  420. /// <summary>
  421. /// Executes a "create table if not exists" on the database for each type. It also
  422. /// creates any specified indexes on the columns of the table. It uses
  423. /// a schema automatically generated from the specified type. You can
  424. /// later access this schema by calling GetMapping.
  425. /// </summary>
  426. /// <returns>
  427. /// Whether the table was created or migrated for each type.
  428. /// </returns>
  429. public Task<CreateTablesResult> CreateTablesAsync (CreateFlags createFlags = CreateFlags.None, params Type[] types)
  430. {
  431. return WriteAsync (conn => conn.CreateTables (createFlags, types));
  432. }
  433. /// <summary>
  434. /// Executes a "drop table" on the database. This is non-recoverable.
  435. /// </summary>
  436. public Task<int> DropTableAsync<T> (string tableName = "")
  437. where T : new()
  438. {
  439. return WriteAsync (conn => conn.DropTable<T> (tableName));
  440. }
  441. /// <summary>
  442. /// Executes a "drop table" on the database. This is non-recoverable.
  443. /// </summary>
  444. /// <param name="map">
  445. /// The TableMapping used to identify the table.
  446. /// </param>
  447. public Task<int> DropTableAsync (TableMapping map)
  448. {
  449. return WriteAsync (conn => conn.DropTable (map));
  450. }
  451. /// <summary>
  452. /// Creates an index for the specified table and column.
  453. /// </summary>
  454. /// <param name="tableName">Name of the database table</param>
  455. /// <param name="columnName">Name of the column to index</param>
  456. /// <param name="unique">Whether the index should be unique</param>
  457. /// <returns>Zero on success.</returns>
  458. public Task<int> CreateIndexAsync (string tableName, string columnName, bool unique = false)
  459. {
  460. return WriteAsync (conn => conn.CreateIndex (tableName, columnName, unique));
  461. }
  462. /// <summary>
  463. /// Creates an index for the specified table and column.
  464. /// </summary>
  465. /// <param name="indexName">Name of the index to create</param>
  466. /// <param name="tableName">Name of the database table</param>
  467. /// <param name="columnName">Name of the column to index</param>
  468. /// <param name="unique">Whether the index should be unique</param>
  469. /// <returns>Zero on success.</returns>
  470. public Task<int> CreateIndexAsync (string indexName, string tableName, string columnName, bool unique = false)
  471. {
  472. return WriteAsync (conn => conn.CreateIndex (indexName, tableName, columnName, unique));
  473. }
  474. /// <summary>
  475. /// Creates an index for the specified table and columns.
  476. /// </summary>
  477. /// <param name="tableName">Name of the database table</param>
  478. /// <param name="columnNames">An array of column names to index</param>
  479. /// <param name="unique">Whether the index should be unique</param>
  480. /// <returns>Zero on success.</returns>
  481. public Task<int> CreateIndexAsync (string tableName, string[] columnNames, bool unique = false)
  482. {
  483. return WriteAsync (conn => conn.CreateIndex (tableName, columnNames, unique));
  484. }
  485. /// <summary>
  486. /// Creates an index for the specified table and columns.
  487. /// </summary>
  488. /// <param name="indexName">Name of the index to create</param>
  489. /// <param name="tableName">Name of the database table</param>
  490. /// <param name="columnNames">An array of column names to index</param>
  491. /// <param name="unique">Whether the index should be unique</param>
  492. /// <returns>Zero on success.</returns>
  493. public Task<int> CreateIndexAsync (string indexName, string tableName, string[] columnNames, bool unique = false)
  494. {
  495. return WriteAsync (conn => conn.CreateIndex (indexName, tableName, columnNames, unique));
  496. }
  497. /// <summary>
  498. /// Creates an index for the specified object property.
  499. /// e.g. CreateIndex&lt;Client&gt;(c => c.Name);
  500. /// </summary>
  501. /// <typeparam name="T">Type to reflect to a database table.</typeparam>
  502. /// <param name="property">Property to index</param>
  503. /// <param name="unique">Whether the index should be unique</param>
  504. /// <returns>Zero on success.</returns>
  505. public Task<int> CreateIndexAsync<T> (Expression<Func<T, object>> property, bool unique = false)
  506. {
  507. return WriteAsync (conn => conn.CreateIndex (property, unique));
  508. }
  509. /// <summary>
  510. /// Inserts the given object and (and updates its
  511. /// auto incremented primary key if it has one).
  512. /// </summary>
  513. /// <param name="obj">
  514. /// The object to insert.
  515. /// </param>
  516. /// <returns>
  517. /// The number of rows added to the table.
  518. /// </returns>
  519. public Task<int> InsertAsync (object obj,string tableName = "")
  520. {
  521. return WriteAsync (conn => conn.Insert (obj,tableName));
  522. }
  523. /// <summary>
  524. /// Inserts the given object (and updates its
  525. /// auto incremented primary key if it has one).
  526. /// The return value is the number of rows added to the table.
  527. /// </summary>
  528. /// <param name="obj">
  529. /// The object to insert.
  530. /// </param>
  531. /// <param name="objType">
  532. /// The type of object to insert.
  533. /// </param>
  534. /// <returns>
  535. /// The number of rows added to the table.
  536. /// </returns>
  537. public Task<int> InsertAsync (object obj, Type objType,string tableName = "")
  538. {
  539. return WriteAsync (conn => conn.Insert (obj, objType,tableName));
  540. }
  541. /// <summary>
  542. /// Inserts the given object (and updates its
  543. /// auto incremented primary key if it has one).
  544. /// The return value is the number of rows added to the table.
  545. /// </summary>
  546. /// <param name="obj">
  547. /// The object to insert.
  548. /// </param>
  549. /// <param name="extra">
  550. /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
  551. /// </param>
  552. /// <returns>
  553. /// The number of rows added to the table.
  554. /// </returns>
  555. public Task<int> InsertAsync (object obj, string extra,string tableName = "")
  556. {
  557. return WriteAsync (conn => conn.Insert (obj, extra,tableName));
  558. }
  559. /// <summary>
  560. /// Inserts the given object (and updates its
  561. /// auto incremented primary key if it has one).
  562. /// The return value is the number of rows added to the table.
  563. /// </summary>
  564. /// <param name="obj">
  565. /// The object to insert.
  566. /// </param>
  567. /// <param name="extra">
  568. /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
  569. /// </param>
  570. /// <param name="objType">
  571. /// The type of object to insert.
  572. /// </param>
  573. /// <returns>
  574. /// The number of rows added to the table.
  575. /// </returns>
  576. public Task<int> InsertAsync (object obj, string extra, Type objType,string tableName = "")
  577. {
  578. return WriteAsync (conn => conn.Insert (obj, extra, objType,tableName));
  579. }
  580. /// <summary>
  581. /// Inserts the given object (and updates its
  582. /// auto incremented primary key if it has one).
  583. /// The return value is the number of rows added to the table.
  584. /// If a UNIQUE constraint violation occurs with
  585. /// some pre-existing object, this function deletes
  586. /// the old object.
  587. /// </summary>
  588. /// <param name="obj">
  589. /// The object to insert.
  590. /// </param>
  591. /// <returns>
  592. /// The number of rows modified.
  593. /// </returns>
  594. public Task<int> InsertOrReplaceAsync (object obj,string tableName = "")
  595. {
  596. return WriteAsync (conn => conn.InsertOrReplace (obj,tableName));
  597. }
  598. /// <summary>
  599. /// Inserts the given object (and updates its
  600. /// auto incremented primary key if it has one).
  601. /// The return value is the number of rows added to the table.
  602. /// If a UNIQUE constraint violation occurs with
  603. /// some pre-existing object, this function deletes
  604. /// the old object.
  605. /// </summary>
  606. /// <param name="obj">
  607. /// The object to insert.
  608. /// </param>
  609. /// <param name="objType">
  610. /// The type of object to insert.
  611. /// </param>
  612. /// <returns>
  613. /// The number of rows modified.
  614. /// </returns>
  615. public Task<int> InsertOrReplaceAsync (object obj, Type objType,string tableName = "")
  616. {
  617. return WriteAsync (conn => conn.InsertOrReplace (obj, objType,tableName));
  618. }
  619. /// <summary>
  620. /// Updates all of the columns of a table using the specified object
  621. /// except for its primary key.
  622. /// The object is required to have a primary key.
  623. /// </summary>
  624. /// <param name="obj">
  625. /// The object to update. It must have a primary key designated using the PrimaryKeyAttribute.
  626. /// </param>
  627. /// <returns>
  628. /// The number of rows updated.
  629. /// </returns>
  630. public Task<int> UpdateAsync (object obj,string tableName = "")
  631. {
  632. return WriteAsync (conn => conn.Update (obj,tableName));
  633. }
  634. /// <summary>
  635. /// Updates all of the columns of a table using the specified object
  636. /// except for its primary key.
  637. /// The object is required to have a primary key.
  638. /// </summary>
  639. /// <param name="obj">
  640. /// The object to update. It must have a primary key designated using the PrimaryKeyAttribute.
  641. /// </param>
  642. /// <param name="objType">
  643. /// The type of object to insert.
  644. /// </param>
  645. /// <returns>
  646. /// The number of rows updated.
  647. /// </returns>
  648. public Task<int> UpdateAsync (object obj, Type objType,string tableName = "")
  649. {
  650. return WriteAsync (conn => conn.Update (obj, objType,tableName));
  651. }
  652. /// <summary>
  653. /// Updates all specified objects.
  654. /// </summary>
  655. /// <param name="objects">
  656. /// An <see cref="IEnumerable"/> of the objects to insert.
  657. /// </param>
  658. /// <param name="runInTransaction">
  659. /// A boolean indicating if the inserts should be wrapped in a transaction
  660. /// </param>
  661. /// <returns>
  662. /// The number of rows modified.
  663. /// </returns>
  664. public Task<int> UpdateAllAsync (IEnumerable objects, bool runInTransaction = true,string tableName = "")
  665. {
  666. return WriteAsync (conn => conn.UpdateAll (objects, runInTransaction,tableName));
  667. }
  668. /// <summary>
  669. /// Deletes the given object from the database using its primary key.
  670. /// </summary>
  671. /// <param name="objectToDelete">
  672. /// The object to delete. It must have a primary key designated using the PrimaryKeyAttribute.
  673. /// </param>
  674. /// <returns>
  675. /// The number of rows deleted.
  676. /// </returns>
  677. public Task<int> DeleteAsync (object objectToDelete,string tableName = "")
  678. {
  679. return WriteAsync (conn => conn.Delete (objectToDelete,tableName));
  680. }
  681. /// <summary>
  682. /// Deletes the object with the specified primary key.
  683. /// </summary>
  684. /// <param name="primaryKey">
  685. /// The primary key of the object to delete.
  686. /// </param>
  687. /// <returns>
  688. /// The number of objects deleted.
  689. /// </returns>
  690. /// <typeparam name='T'>
  691. /// The type of object.
  692. /// </typeparam>
  693. public Task<int> DeleteAsync<T> (object primaryKey,string tableName = "")
  694. {
  695. return WriteAsync (conn => conn.Delete<T> (primaryKey,tableName));
  696. }
  697. /// <summary>
  698. /// Deletes the object with the specified primary key.
  699. /// </summary>
  700. /// <param name="primaryKey">
  701. /// The primary key of the object to delete.
  702. /// </param>
  703. /// <param name="map">
  704. /// The TableMapping used to identify the table.
  705. /// </param>
  706. /// <returns>
  707. /// The number of objects deleted.
  708. /// </returns>
  709. public Task<int> DeleteAsync (object primaryKey, TableMapping map)
  710. {
  711. return WriteAsync (conn => conn.Delete (primaryKey, map));
  712. }
  713. /// <summary>
  714. /// Deletes all the objects from the specified table.
  715. /// WARNING WARNING: Let me repeat. It deletes ALL the objects from the
  716. /// specified table. Do you really want to do that?
  717. /// </summary>
  718. /// <returns>
  719. /// The number of objects deleted.
  720. /// </returns>
  721. /// <typeparam name='T'>
  722. /// The type of objects to delete.
  723. /// </typeparam>
  724. public Task<int> DeleteAllAsync<T> (string tableName = "")
  725. {
  726. return WriteAsync (conn => conn.DeleteAll<T> (tableName));
  727. }
  728. /// <summary>
  729. /// Deletes all the objects from the specified table.
  730. /// WARNING WARNING: Let me repeat. It deletes ALL the objects from the
  731. /// specified table. Do you really want to do that?
  732. /// </summary>
  733. /// <param name="map">
  734. /// The TableMapping used to identify the table.
  735. /// </param>
  736. /// <returns>
  737. /// The number of objects deleted.
  738. /// </returns>
  739. public Task<int> DeleteAllAsync (TableMapping map)
  740. {
  741. return WriteAsync (conn => conn.DeleteAll (map));
  742. }
  743. /// <summary>
  744. /// Backup the entire database to the specified path.
  745. /// </summary>
  746. /// <param name="destinationDatabasePath">Path to backup file.</param>
  747. /// <param name="databaseName">The name of the database to backup (usually "main").</param>
  748. public Task BackupAsync (string destinationDatabasePath, string databaseName = "main")
  749. {
  750. return WriteAsync (conn => {
  751. conn.Backup (destinationDatabasePath, databaseName);
  752. return 0;
  753. });
  754. }
  755. /// <summary>
  756. /// Attempts to retrieve an object with the given primary key from the table
  757. /// associated with the specified type. Use of this method requires that
  758. /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
  759. /// </summary>
  760. /// <param name="pk">
  761. /// The primary key.
  762. /// </param>
  763. /// <returns>
  764. /// The object with the given primary key. Throws a not found exception
  765. /// if the object is not found.
  766. /// </returns>
  767. public Task<T> GetAsync<T> (object pk,string tableName = "")
  768. where T : new()
  769. {
  770. return ReadAsync (conn => conn.Get<T> (pk,tableName));
  771. }
  772. /// <summary>
  773. /// Attempts to retrieve an object with the given primary key from the table
  774. /// associated with the specified type. Use of this method requires that
  775. /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
  776. /// </summary>
  777. /// <param name="pk">
  778. /// The primary key.
  779. /// </param>
  780. /// <param name="map">
  781. /// The TableMapping used to identify the table.
  782. /// </param>
  783. /// <returns>
  784. /// The object with the given primary key. Throws a not found exception
  785. /// if the object is not found.
  786. /// </returns>
  787. public Task<object> GetAsync (object pk, TableMapping map)
  788. {
  789. return ReadAsync (conn => conn.Get (pk, map));
  790. }
  791. /// <summary>
  792. /// Attempts to retrieve the first object that matches the predicate from the table
  793. /// associated with the specified type.
  794. /// </summary>
  795. /// <param name="predicate">
  796. /// A predicate for which object to find.
  797. /// </param>
  798. /// <returns>
  799. /// The object that matches the given predicate. Throws a not found exception
  800. /// if the object is not found.
  801. /// </returns>
  802. public Task<T> GetAsync<T> (Expression<Func<T, bool>> predicate,string tableName = "")
  803. where T : new()
  804. {
  805. return ReadAsync (conn => conn.Get<T> (predicate,tableName));
  806. }
  807. /// <summary>
  808. /// Attempts to retrieve an object with the given primary key from the table
  809. /// associated with the specified type. Use of this method requires that
  810. /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
  811. /// </summary>
  812. /// <param name="pk">
  813. /// The primary key.
  814. /// </param>
  815. /// <returns>
  816. /// The object with the given primary key or null
  817. /// if the object is not found.
  818. /// </returns>
  819. public Task<T> FindAsync<T> (object pk,string tableName = "")
  820. where T : new()
  821. {
  822. return ReadAsync (conn => conn.Find<T> (pk,tableName));
  823. }
  824. /// <summary>
  825. /// Attempts to retrieve an object with the given primary key from the table
  826. /// associated with the specified type. Use of this method requires that
  827. /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
  828. /// </summary>
  829. /// <param name="pk">
  830. /// The primary key.
  831. /// </param>
  832. /// <param name="map">
  833. /// The TableMapping used to identify the table.
  834. /// </param>
  835. /// <returns>
  836. /// The object with the given primary key or null
  837. /// if the object is not found.
  838. /// </returns>
  839. public Task<object> FindAsync (object pk, TableMapping map)
  840. {
  841. return ReadAsync (conn => conn.Find (pk, map));
  842. }
  843. /// <summary>
  844. /// Attempts to retrieve the first object that matches the predicate from the table
  845. /// associated with the specified type.
  846. /// </summary>
  847. /// <param name="predicate">
  848. /// A predicate for which object to find.
  849. /// </param>
  850. /// <returns>
  851. /// The object that matches the given predicate or null
  852. /// if the object is not found.
  853. /// </returns>
  854. public Task<T> FindAsync<T> (Expression<Func<T, bool>> predicate,string tableName = "")
  855. where T : new()
  856. {
  857. return ReadAsync (conn => conn.Find<T> (predicate,tableName));
  858. }
  859. /// <summary>
  860. /// Attempts to retrieve the first object that matches the query from the table
  861. /// associated with the specified type.
  862. /// </summary>
  863. /// <param name="query">
  864. /// The fully escaped SQL.
  865. /// </param>
  866. /// <param name="args">
  867. /// Arguments to substitute for the occurences of '?' in the query.
  868. /// </param>
  869. /// <returns>
  870. /// The object that matches the given predicate or null
  871. /// if the object is not found.
  872. /// </returns>
  873. public Task<T> FindWithQueryAsync<T> (string query, params object[] args)
  874. where T : new()
  875. {
  876. return ReadAsync (conn => conn.FindWithQuery<T> (query, args));
  877. }
  878. /// <summary>
  879. /// Attempts to retrieve the first object that matches the query from the table
  880. /// associated with the specified type.
  881. /// </summary>
  882. /// <param name="map">
  883. /// The TableMapping used to identify the table.
  884. /// </param>
  885. /// <param name="query">
  886. /// The fully escaped SQL.
  887. /// </param>
  888. /// <param name="args">
  889. /// Arguments to substitute for the occurences of '?' in the query.
  890. /// </param>
  891. /// <returns>
  892. /// The object that matches the given predicate or null
  893. /// if the object is not found.
  894. /// </returns>
  895. public Task<object> FindWithQueryAsync (TableMapping map, string query, params object[] args)
  896. {
  897. return ReadAsync (conn => conn.FindWithQuery (map, query, args));
  898. }
  899. /// <summary>
  900. /// Retrieves the mapping that is automatically generated for the given type.
  901. /// </summary>
  902. /// <param name="type">
  903. /// The type whose mapping to the database is returned.
  904. /// </param>
  905. /// <param name="createFlags">
  906. /// Optional flags allowing implicit PK and indexes based on naming conventions
  907. /// </param>
  908. /// <returns>
  909. /// The mapping represents the schema of the columns of the database and contains
  910. /// methods to set and get properties of objects.
  911. /// </returns>
  912. public Task<TableMapping> GetMappingAsync (Type type, CreateFlags createFlags = CreateFlags.None,string tableName = "")
  913. {
  914. return ReadAsync (conn => conn.GetMapping (type, createFlags,tableName));
  915. }
  916. /// <summary>
  917. /// Retrieves the mapping that is automatically generated for the given type.
  918. /// </summary>
  919. /// <param name="createFlags">
  920. /// Optional flags allowing implicit PK and indexes based on naming conventions
  921. /// </param>
  922. /// <returns>
  923. /// The mapping represents the schema of the columns of the database and contains
  924. /// methods to set and get properties of objects.
  925. /// </returns>
  926. public Task<TableMapping> GetMappingAsync<T> (CreateFlags createFlags = CreateFlags.None,string tableName = "")
  927. where T : new()
  928. {
  929. return ReadAsync (conn => conn.GetMapping<T> (createFlags,tableName));
  930. }
  931. /// <summary>
  932. /// Query the built-in sqlite table_info table for a specific tables columns.
  933. /// </summary>
  934. /// <returns>The columns contains in the table.</returns>
  935. /// <param name="tableName">Table name.</param>
  936. public Task<List<SQLiteConnection.ColumnInfo>> GetTableInfoAsync (string tableName)
  937. {
  938. return ReadAsync (conn => conn.GetTableInfo (tableName));
  939. }
  940. /// <summary>
  941. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  942. /// in the command text for each of the arguments and then executes that command.
  943. /// Use this method instead of Query when you don't expect rows back. Such cases include
  944. /// INSERTs, UPDATEs, and DELETEs.
  945. /// You can set the Trace or TimeExecution properties of the connection
  946. /// to profile execution.
  947. /// </summary>
  948. /// <param name="query">
  949. /// The fully escaped SQL.
  950. /// </param>
  951. /// <param name="args">
  952. /// Arguments to substitute for the occurences of '?' in the query.
  953. /// </param>
  954. /// <returns>
  955. /// The number of rows modified in the database as a result of this execution.
  956. /// </returns>
  957. public Task<int> ExecuteAsync (string query, params object[] args)
  958. {
  959. return WriteAsync (conn => conn.Execute (query, args));
  960. }
  961. /// <summary>
  962. /// Inserts all specified objects.
  963. /// </summary>
  964. /// <param name="objects">
  965. /// An <see cref="IEnumerable"/> of the objects to insert.
  966. /// <param name="runInTransaction"/>
  967. /// A boolean indicating if the inserts should be wrapped in a transaction.
  968. /// </param>
  969. /// <returns>
  970. /// The number of rows added to the table.
  971. /// </returns>
  972. public Task<int> InsertAllAsync (IEnumerable objects, bool runInTransaction = true,string tableName = "")
  973. {
  974. return WriteAsync (conn => conn.InsertAll (objects, runInTransaction,tableName));
  975. }
  976. /// <summary>
  977. /// Inserts all specified objects.
  978. /// </summary>
  979. /// <param name="objects">
  980. /// An <see cref="IEnumerable"/> of the objects to insert.
  981. /// </param>
  982. /// <param name="extra">
  983. /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
  984. /// </param>
  985. /// <param name="runInTransaction">
  986. /// A boolean indicating if the inserts should be wrapped in a transaction.
  987. /// </param>
  988. /// <returns>
  989. /// The number of rows added to the table.
  990. /// </returns>
  991. public Task<int> InsertAllAsync (IEnumerable objects, string extra, bool runInTransaction = true,string tableName = "")
  992. {
  993. return WriteAsync (conn => conn.InsertAll (objects, extra, runInTransaction,tableName));
  994. }
  995. /// <summary>
  996. /// Inserts all specified objects.
  997. /// </summary>
  998. /// <param name="objects">
  999. /// An <see cref="IEnumerable"/> of the objects to insert.
  1000. /// </param>
  1001. /// <param name="objType">
  1002. /// The type of object to insert.
  1003. /// </param>
  1004. /// <param name="runInTransaction">
  1005. /// A boolean indicating if the inserts should be wrapped in a transaction.
  1006. /// </param>
  1007. /// <returns>
  1008. /// The number of rows added to the table.
  1009. /// </returns>
  1010. public Task<int> InsertAllAsync (IEnumerable objects, Type objType, bool runInTransaction = true,string tableName = "")
  1011. {
  1012. return WriteAsync (conn => conn.InsertAll (objects, objType, runInTransaction,tableName));
  1013. }
  1014. /// <summary>
  1015. /// Executes <paramref name="action"/> within a (possibly nested) transaction by wrapping it in a SAVEPOINT. If an
  1016. /// exception occurs the whole transaction is rolled back, not just the current savepoint. The exception
  1017. /// is rethrown.
  1018. /// </summary>
  1019. /// <param name="action">
  1020. /// The <see cref="Action"/> to perform within a transaction. <paramref name="action"/> can contain any number
  1021. /// of operations on the connection but should never call <see cref="SQLiteConnection.Commit"/> or
  1022. /// <see cref="SQLiteConnection.Commit"/>.
  1023. /// </param>
  1024. public Task RunInTransactionAsync (Action<SQLiteConnection> action)
  1025. {
  1026. return TransactAsync<object> (conn => {
  1027. conn.BeginTransaction ();
  1028. try {
  1029. action (conn);
  1030. conn.Commit ();
  1031. return null;
  1032. }
  1033. catch (Exception) {
  1034. conn.Rollback ();
  1035. throw;
  1036. }
  1037. });
  1038. }
  1039. /// <summary>
  1040. /// Returns a queryable interface to the table represented by the given type.
  1041. /// </summary>
  1042. /// <returns>
  1043. /// A queryable object that is able to translate Where, OrderBy, and Take
  1044. /// queries into native SQL.
  1045. /// </returns>
  1046. public AsyncTableQuery<T> Table<T> (string tableName = "")
  1047. where T : new()
  1048. {
  1049. //
  1050. // This isn't async as the underlying connection doesn't go out to the database
  1051. // until the query is performed. The Async methods are on the query iteself.
  1052. //
  1053. var conn = GetConnection ();
  1054. return new AsyncTableQuery<T> (conn.Table<T> (tableName));
  1055. }
  1056. /// <summary>
  1057. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  1058. /// in the command text for each of the arguments and then executes that command.
  1059. /// Use this method when return primitive values.
  1060. /// You can set the Trace or TimeExecution properties of the connection
  1061. /// to profile execution.
  1062. /// </summary>
  1063. /// <param name="query">
  1064. /// The fully escaped SQL.
  1065. /// </param>
  1066. /// <param name="args">
  1067. /// Arguments to substitute for the occurences of '?' in the query.
  1068. /// </param>
  1069. /// <returns>
  1070. /// The number of rows modified in the database as a result of this execution.
  1071. /// </returns>
  1072. public Task<T> ExecuteScalarAsync<T> (string query, params object[] args)
  1073. {
  1074. return WriteAsync (conn => {
  1075. var command = conn.CreateCommand (query, args);
  1076. return command.ExecuteScalar<T> ();
  1077. });
  1078. }
  1079. /// <summary>
  1080. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  1081. /// in the command text for each of the arguments and then executes that command.
  1082. /// It returns each row of the result using the mapping automatically generated for
  1083. /// the given type.
  1084. /// </summary>
  1085. /// <param name="query">
  1086. /// The fully escaped SQL.
  1087. /// </param>
  1088. /// <param name="args">
  1089. /// Arguments to substitute for the occurences of '?' in the query.
  1090. /// </param>
  1091. /// <returns>
  1092. /// A list with one result for each row returned by the query.
  1093. /// </returns>
  1094. public Task<List<T>> QueryAsync<T> (string query, params object[] args)
  1095. where T : new()
  1096. {
  1097. return ReadAsync (conn => conn.Query<T> (query, args));
  1098. }
  1099. /// <summary>
  1100. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  1101. /// in the command text for each of the arguments and then executes that command.
  1102. /// It returns the first column of each row of the result.
  1103. /// </summary>
  1104. /// <param name="query">
  1105. /// The fully escaped SQL.
  1106. /// </param>
  1107. /// <param name="args">
  1108. /// Arguments to substitute for the occurences of '?' in the query.
  1109. /// </param>
  1110. /// <returns>
  1111. /// A list with one result for the first column of each row returned by the query.
  1112. /// </returns>
  1113. public Task<List<T>> QueryScalarsAsync<T> (string query, params object[] args)
  1114. {
  1115. return ReadAsync (conn => conn.QueryScalars<T> (query, args));
  1116. }
  1117. /// <summary>
  1118. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  1119. /// in the command text for each of the arguments and then executes that command.
  1120. /// It returns each row of the result using the specified mapping. This function is
  1121. /// only used by libraries in order to query the database via introspection. It is
  1122. /// normally not used.
  1123. /// </summary>
  1124. /// <param name="map">
  1125. /// A <see cref="TableMapping"/> to use to convert the resulting rows
  1126. /// into objects.
  1127. /// </param>
  1128. /// <param name="query">
  1129. /// The fully escaped SQL.
  1130. /// </param>
  1131. /// <param name="args">
  1132. /// Arguments to substitute for the occurences of '?' in the query.
  1133. /// </param>
  1134. /// <returns>
  1135. /// An enumerable with one result for each row returned by the query.
  1136. /// </returns>
  1137. public Task<List<object>> QueryAsync (TableMapping map, string query, params object[] args)
  1138. {
  1139. return ReadAsync (conn => conn.Query (map, query, args));
  1140. }
  1141. /// <summary>
  1142. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  1143. /// in the command text for each of the arguments and then executes that command.
  1144. /// It returns each row of the result using the mapping automatically generated for
  1145. /// the given type.
  1146. /// </summary>
  1147. /// <param name="query">
  1148. /// The fully escaped SQL.
  1149. /// </param>
  1150. /// <param name="args">
  1151. /// Arguments to substitute for the occurences of '?' in the query.
  1152. /// </param>
  1153. /// <returns>
  1154. /// An enumerable with one result for each row returned by the query.
  1155. /// The enumerator will call sqlite3_step on each call to MoveNext, so the database
  1156. /// connection must remain open for the lifetime of the enumerator.
  1157. /// </returns>
  1158. public Task<IEnumerable<T>> DeferredQueryAsync<T> (string query, params object[] args)
  1159. where T : new()
  1160. {
  1161. return ReadAsync (conn => (IEnumerable<T>)conn.DeferredQuery<T> (query, args).ToList ());
  1162. }
  1163. /// <summary>
  1164. /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
  1165. /// in the command text for each of the arguments and then executes that command.
  1166. /// It returns each row of the result using the specified mapping. This function is
  1167. /// only used by libraries in order to query the database via introspection. It is
  1168. /// normally not used.
  1169. /// </summary>
  1170. /// <param name="map">
  1171. /// A <see cref="TableMapping"/> to use to convert the resulting rows
  1172. /// into objects.
  1173. /// </param>
  1174. /// <param name="query">
  1175. /// The fully escaped SQL.
  1176. /// </param>
  1177. /// <param name="args">
  1178. /// Arguments to substitute for the occurences of '?' in the query.
  1179. /// </param>
  1180. /// <returns>
  1181. /// An enumerable with one result for each row returned by the query.
  1182. /// The enumerator will call sqlite3_step on each call to MoveNext, so the database
  1183. /// connection must remain open for the lifetime of the enumerator.
  1184. /// </returns>
  1185. public Task<IEnumerable<object>> DeferredQueryAsync (TableMapping map, string query, params object[] args)
  1186. {
  1187. return ReadAsync (conn => (IEnumerable<object>)conn.DeferredQuery (map, query, args).ToList ());
  1188. }
  1189. /// <summary>
  1190. /// Change the encryption key for a SQLCipher database with "pragma rekey = ...".
  1191. /// </summary>
  1192. /// <param name="key">Encryption key plain text that is converted to the real encryption key using PBKDF2 key derivation</param>
  1193. public Task ReKeyAsync (string key)
  1194. {
  1195. return WriteAsync<object> (conn => {
  1196. conn.ReKey (key);
  1197. return null;
  1198. });
  1199. }
  1200. /// <summary>
  1201. /// Change the encryption key for a SQLCipher database.
  1202. /// </summary>
  1203. /// <param name="key">256-bit (32 byte) or 384-bit (48 bytes) encryption key data</param>
  1204. public Task ReKeyAsync (byte[] key)
  1205. {
  1206. return WriteAsync<object> (conn => {
  1207. conn.ReKey (key);
  1208. return null;
  1209. });
  1210. }
  1211. }
  1212. /// <summary>
  1213. /// Query to an asynchronous database connection.
  1214. /// </summary>
  1215. public class AsyncTableQuery<T>
  1216. where T : new()
  1217. {
  1218. TableQuery<T> _innerQuery;
  1219. /// <summary>
  1220. /// Creates a new async query that uses given the synchronous query.
  1221. /// </summary>
  1222. public AsyncTableQuery (TableQuery<T> innerQuery)
  1223. {
  1224. _innerQuery = innerQuery;
  1225. }
  1226. Task<U> ReadAsync<U> (Func<SQLiteConnectionWithLock, U> read)
  1227. {
  1228. return Task.Factory.StartNew (() => {
  1229. var conn = (SQLiteConnectionWithLock)_innerQuery.Connection;
  1230. using (conn.Lock ()) {
  1231. return read (conn);
  1232. }
  1233. }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
  1234. }
  1235. Task<U> WriteAsync<U> (Func<SQLiteConnectionWithLock, U> write)
  1236. {
  1237. return Task.Factory.StartNew (() => {
  1238. var conn = (SQLiteConnectionWithLock)_innerQuery.Connection;
  1239. using (conn.Lock ()) {
  1240. return write (conn);
  1241. }
  1242. }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
  1243. }
  1244. /// <summary>
  1245. /// Filters the query based on a predicate.
  1246. /// </summary>
  1247. public AsyncTableQuery<T> Where (Expression<Func<T, bool>> predExpr)
  1248. {
  1249. return new AsyncTableQuery<T> (_innerQuery.Where (predExpr));
  1250. }
  1251. /// <summary>
  1252. /// Skips a given number of elements from the query and then yields the remainder.
  1253. /// </summary>
  1254. public AsyncTableQuery<T> Skip (int n)
  1255. {
  1256. return new AsyncTableQuery<T> (_innerQuery.Skip (n));
  1257. }
  1258. /// <summary>
  1259. /// Yields a given number of elements from the query and then skips the remainder.
  1260. /// </summary>
  1261. public AsyncTableQuery<T> Take (int n)
  1262. {
  1263. return new AsyncTableQuery<T> (_innerQuery.Take (n));
  1264. }
  1265. /// <summary>
  1266. /// Order the query results according to a key.
  1267. /// </summary>
  1268. public AsyncTableQuery<T> OrderBy<U> (Expression<Func<T, U>> orderExpr)
  1269. {
  1270. return new AsyncTableQuery<T> (_innerQuery.OrderBy<U> (orderExpr));
  1271. }
  1272. /// <summary>
  1273. /// Order the query results according to a key.
  1274. /// </summary>
  1275. public AsyncTableQuery<T> OrderByDescending<U> (Expression<Func<T, U>> orderExpr)
  1276. {
  1277. return new AsyncTableQuery<T> (_innerQuery.OrderByDescending<U> (orderExpr));
  1278. }
  1279. /// <summary>
  1280. /// Order the query results according to a key.
  1281. /// </summary>
  1282. public AsyncTableQuery<T> ThenBy<U> (Expression<Func<T, U>> orderExpr)
  1283. {
  1284. return new AsyncTableQuery<T> (_innerQuery.ThenBy<U> (orderExpr));
  1285. }
  1286. /// <summary>
  1287. /// Order the query results according to a key.
  1288. /// </summary>
  1289. public AsyncTableQuery<T> ThenByDescending<U> (Expression<Func<T, U>> orderExpr)
  1290. {
  1291. return new AsyncTableQuery<T> (_innerQuery.ThenByDescending<U> (orderExpr));
  1292. }
  1293. /// <summary>
  1294. /// Queries the database and returns the results as a List.
  1295. /// </summary>
  1296. public Task<List<T>> ToListAsync ()
  1297. {
  1298. return ReadAsync (conn => _innerQuery.ToList ());
  1299. }
  1300. /// <summary>
  1301. /// Queries the database and returns the results as an array.
  1302. /// </summary>
  1303. public Task<T[]> ToArrayAsync ()
  1304. {
  1305. return ReadAsync (conn => _innerQuery.ToArray ());
  1306. }
  1307. /// <summary>
  1308. /// Execute SELECT COUNT(*) on the query
  1309. /// </summary>
  1310. public Task<int> CountAsync ()
  1311. {
  1312. return ReadAsync (conn => _innerQuery.Count ());
  1313. }
  1314. /// <summary>
  1315. /// Execute SELECT COUNT(*) on the query with an additional WHERE clause.
  1316. /// </summary>
  1317. public Task<int> CountAsync (Expression<Func<T, bool>> predExpr)
  1318. {
  1319. return ReadAsync (conn => _innerQuery.Count (predExpr));
  1320. }
  1321. /// <summary>
  1322. /// Returns the element at a given index
  1323. /// </summary>
  1324. public Task<T> ElementAtAsync (int index)
  1325. {
  1326. return ReadAsync (conn => _innerQuery.ElementAt (index));
  1327. }
  1328. /// <summary>
  1329. /// Returns the first element of this query.
  1330. /// </summary>
  1331. public Task<T> FirstAsync ()
  1332. {
  1333. return ReadAsync (conn => _innerQuery.First ());
  1334. }
  1335. /// <summary>
  1336. /// Returns the first element of this query, or null if no element is found.
  1337. /// </summary>
  1338. public Task<T> FirstOrDefaultAsync ()
  1339. {
  1340. return ReadAsync (conn => _innerQuery.FirstOrDefault ());
  1341. }
  1342. /// <summary>
  1343. /// Returns the first element of this query that matches the predicate.
  1344. /// </summary>
  1345. public Task<T> FirstAsync (Expression<Func<T, bool>> predExpr)
  1346. {
  1347. return ReadAsync (conn => _innerQuery.First (predExpr));
  1348. }
  1349. /// <summary>
  1350. /// Returns the first element of this query that matches the predicate.
  1351. /// </summary>
  1352. public Task<T> FirstOrDefaultAsync (Expression<Func<T, bool>> predExpr)
  1353. {
  1354. return ReadAsync (conn => _innerQuery.FirstOrDefault (predExpr));
  1355. }
  1356. /// <summary>
  1357. /// Delete all the rows that match this query and the given predicate.
  1358. /// </summary>
  1359. public Task<int> DeleteAsync (Expression<Func<T, bool>> predExpr)
  1360. {
  1361. return WriteAsync (conn => _innerQuery.Delete (predExpr));
  1362. }
  1363. /// <summary>
  1364. /// Delete all the rows that match this query.
  1365. /// </summary>
  1366. public Task<int> DeleteAsync ()
  1367. {
  1368. return WriteAsync (conn => _innerQuery.Delete ());
  1369. }
  1370. }
  1371. class SQLiteConnectionPool
  1372. {
  1373. class Entry
  1374. {
  1375. public SQLiteConnectionWithLock Connection { get; private set; }
  1376. public SQLiteConnectionString ConnectionString { get; }
  1377. public object TransactionLock { get; } = new object ();
  1378. public Entry (SQLiteConnectionString connectionString)
  1379. {
  1380. ConnectionString = connectionString;
  1381. Connection = new SQLiteConnectionWithLock (ConnectionString);
  1382. // If the database is FullMutex, then we don't need to bother locking
  1383. if (ConnectionString.OpenFlags.HasFlag (SQLiteOpenFlags.FullMutex)) {
  1384. Connection.SkipLock = true;
  1385. }
  1386. }
  1387. public void Close ()
  1388. {
  1389. var wc = Connection;
  1390. Connection = null;
  1391. if (wc != null) {
  1392. wc.Close ();
  1393. }
  1394. }
  1395. }
  1396. readonly Dictionary<string, Entry> _entries = new Dictionary<string, Entry> ();
  1397. readonly object _entriesLock = new object ();
  1398. static readonly SQLiteConnectionPool _shared = new SQLiteConnectionPool ();
  1399. /// <summary>
  1400. /// Gets the singleton instance of the connection tool.
  1401. /// </summary>
  1402. public static SQLiteConnectionPool Shared {
  1403. get {
  1404. return _shared;
  1405. }
  1406. }
  1407. public SQLiteConnectionWithLock GetConnection (SQLiteConnectionString connectionString)
  1408. {
  1409. return GetConnectionAndTransactionLock (connectionString, out var _);
  1410. }
  1411. public SQLiteConnectionWithLock GetConnectionAndTransactionLock (SQLiteConnectionString connectionString, out object transactionLock)
  1412. {
  1413. var key = connectionString.UniqueKey;
  1414. Entry entry;
  1415. lock (_entriesLock) {
  1416. if (!_entries.TryGetValue (key, out entry)) {
  1417. // The opens the database while we're locked
  1418. // This is to ensure another thread doesn't get an unopened database
  1419. entry = new Entry (connectionString);
  1420. _entries[key] = entry;
  1421. }
  1422. transactionLock = entry.TransactionLock;
  1423. return entry.Connection;
  1424. }
  1425. }
  1426. public void CloseConnection (SQLiteConnectionString connectionString)
  1427. {
  1428. var key = connectionString.UniqueKey;
  1429. Entry entry;
  1430. lock (_entriesLock) {
  1431. if (_entries.TryGetValue (key, out entry)) {
  1432. _entries.Remove (key);
  1433. }
  1434. }
  1435. entry?.Close ();
  1436. }
  1437. /// <summary>
  1438. /// Closes all connections managed by this pool.
  1439. /// </summary>
  1440. public void Reset ()
  1441. {
  1442. List<Entry> entries;
  1443. lock (_entriesLock) {
  1444. entries = new List<Entry> (_entries.Values);
  1445. _entries.Clear ();
  1446. }
  1447. foreach (var e in entries) {
  1448. e.Close ();
  1449. }
  1450. }
  1451. }
  1452. /// <summary>
  1453. /// This is a normal connection except it contains a Lock method that
  1454. /// can be used to serialize access to the database
  1455. /// in lieu of using the sqlite's FullMutex support.
  1456. /// </summary>
  1457. public class SQLiteConnectionWithLock : SQLiteConnection
  1458. {
  1459. readonly object _lockPoint = new object ();
  1460. /// <summary>
  1461. /// Initializes a new instance of the <see cref="T:SQLite.SQLiteConnectionWithLock"/> class.
  1462. /// </summary>
  1463. /// <param name="connectionString">Connection string containing the DatabasePath.</param>
  1464. public SQLiteConnectionWithLock (SQLiteConnectionString connectionString)
  1465. : base (connectionString)
  1466. {
  1467. }
  1468. /// <summary>
  1469. /// Gets or sets a value indicating whether this <see cref="T:SQLite.SQLiteConnectionWithLock"/> skip lock.
  1470. /// </summary>
  1471. /// <value><c>true</c> if skip lock; otherwise, <c>false</c>.</value>
  1472. public bool SkipLock { get; set; }
  1473. /// <summary>
  1474. /// Lock the database to serialize access to it. To unlock it, call Dispose
  1475. /// on the returned object.
  1476. /// </summary>
  1477. /// <returns>The lock.</returns>
  1478. public IDisposable Lock ()
  1479. {
  1480. return SkipLock ? (IDisposable)new FakeLockWrapper() : new LockWrapper (_lockPoint);
  1481. }
  1482. class LockWrapper : IDisposable
  1483. {
  1484. object _lockPoint;
  1485. public LockWrapper (object lockPoint)
  1486. {
  1487. _lockPoint = lockPoint;
  1488. Monitor.Enter (_lockPoint);
  1489. }
  1490. public void Dispose ()
  1491. {
  1492. Monitor.Exit (_lockPoint);
  1493. }
  1494. }
  1495. class FakeLockWrapper : IDisposable
  1496. {
  1497. public void Dispose ()
  1498. {
  1499. }
  1500. }
  1501. }
  1502. }