12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628 |
- //
- // Copyright (c) 2012-2024 Krueger Systems, Inc.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Threading;
- using System.Threading.Tasks;
- #pragma warning disable 1591 // XML Doc Comments
- namespace SQLite
- {
- public interface ISQLiteAsyncConnection
- {
- string DatabasePath { get; }
- int LibVersionNumber { get; }
- string DateTimeStringFormat { get; }
- bool StoreDateTimeAsTicks { get; }
- bool StoreTimeSpanAsTicks { get; }
- bool Trace { get; set; }
- Action<string> Tracer { get; set; }
- bool TimeExecution { get; set; }
- IEnumerable<TableMapping> TableMappings { get; }
- Task BackupAsync (string destinationDatabasePath, string databaseName = "main");
- Task CloseAsync ();
- Task<int> CreateIndexAsync (string tableName, string columnName, bool unique = false);
- Task<int> CreateIndexAsync (string indexName, string tableName, string columnName, bool unique = false);
- Task<int> CreateIndexAsync (string tableName, string[] columnNames, bool unique = false);
- Task<int> CreateIndexAsync (string indexName, string tableName, string[] columnNames, bool unique = false);
- Task<int> CreateIndexAsync<T> (Expression<Func<T, object>> property, bool unique = false);
- Task<CreateTableResult> CreateTableAsync<T> (CreateFlags createFlags = CreateFlags.None,string tableName = "") where T : new();
- Task<CreateTableResult> CreateTableAsync (Type ty, CreateFlags createFlags = CreateFlags.None,string tableName = "");
- Task<CreateTablesResult> CreateTablesAsync<T, T2> (CreateFlags createFlags = CreateFlags.None)
- where T : new()
- where T2 : new();
- Task<CreateTablesResult> CreateTablesAsync<T, T2, T3> (CreateFlags createFlags = CreateFlags.None)
- where T : new()
- where T2 : new()
- where T3 : new();
- Task<CreateTablesResult> CreateTablesAsync<T, T2, T3, T4> (CreateFlags createFlags = CreateFlags.None)
- where T : new()
- where T2 : new()
- where T3 : new()
- where T4 : new();
- Task<CreateTablesResult> CreateTablesAsync<T, T2, T3, T4, T5> (CreateFlags createFlags = CreateFlags.None)
- where T : new()
- where T2 : new()
- where T3 : new()
- where T4 : new()
- where T5 : new();
- Task<CreateTablesResult> CreateTablesAsync (CreateFlags createFlags = CreateFlags.None, params Type[] types);
- Task<IEnumerable<T>> DeferredQueryAsync<T> (string query, params object[] args) where T : new();
- Task<IEnumerable<object>> DeferredQueryAsync (TableMapping map, string query, params object[] args);
- Task<int> DeleteAllAsync<T> (string tableName = "");
- Task<int> DeleteAllAsync (TableMapping map);
- Task<int> DeleteAsync (object objectToDelete,string tableName = "");
- Task<int> DeleteAsync<T> (object primaryKey,string tableName = "");
- Task<int> DeleteAsync (object primaryKey, TableMapping map);
- Task<int> DropTableAsync<T> (string tableName = "") where T : new();
- Task<int> DropTableAsync (TableMapping map);
- Task EnableLoadExtensionAsync (bool enabled);
- Task EnableWriteAheadLoggingAsync ();
- Task<int> ExecuteAsync (string query, params object[] args);
- Task<T> ExecuteScalarAsync<T> (string query, params object[] args);
- Task<T> FindAsync<T> (object pk,string tableName = "") where T : new();
- Task<object> FindAsync (object pk, TableMapping map);
- Task<T> FindAsync<T> (Expression<Func<T, bool>> predicate,string tableName = "") where T : new();
- Task<T> FindWithQueryAsync<T> (string query, params object[] args) where T : new();
- Task<object> FindWithQueryAsync (TableMapping map, string query, params object[] args);
- Task<T> GetAsync<T> (object pk,string tableName = "") where T : new();
- Task<object> GetAsync (object pk, TableMapping map);
- Task<T> GetAsync<T> (Expression<Func<T, bool>> predicate,string tableName = "") where T : new();
- TimeSpan GetBusyTimeout ();
- SQLiteConnectionWithLock GetConnection ();
- Task<TableMapping> GetMappingAsync (Type type, CreateFlags createFlags = CreateFlags.None,string tableName = "");
- Task<TableMapping> GetMappingAsync<T> (CreateFlags createFlags = CreateFlags.None,string tableName = "") where T : new();
- Task<List<SQLiteConnection.ColumnInfo>> GetTableInfoAsync (string tableName);
- Task<int> InsertAllAsync (IEnumerable objects, bool runInTransaction = true,string tableName = "");
- Task<int> InsertAllAsync (IEnumerable objects, string extra, bool runInTransaction = true,string tableName = "");
- Task<int> InsertAllAsync (IEnumerable objects, Type objType, bool runInTransaction = true,string tableName = "");
- Task<int> InsertAsync (object obj,string tableName = "");
- Task<int> InsertAsync (object obj, Type objType,string tableName = "");
- Task<int> InsertAsync (object obj, string extra,string tableName = "");
- Task<int> InsertAsync (object obj, string extra, Type objType,string tableName = "");
- Task<int> InsertOrReplaceAsync (object obj,string tableName = "");
- Task<int> InsertOrReplaceAsync (object obj, Type objType,string tableName = "");
- Task<List<T>> QueryAsync<T> (string query, params object[] args) where T : new();
- Task<List<object>> QueryAsync (TableMapping map, string query, params object[] args);
- Task<List<T>> QueryScalarsAsync<T> (string query, params object[] args);
- Task ReKeyAsync (string key);
- Task ReKeyAsync (byte[] key);
- Task RunInTransactionAsync (Action<SQLiteConnection> action);
- Task SetBusyTimeoutAsync (TimeSpan value);
- AsyncTableQuery<T> Table<T> (string tableName = "") where T : new();
- Task<int> UpdateAllAsync (IEnumerable objects, bool runInTransaction = true,string tableName = "");
- Task<int> UpdateAsync (object obj,string tableName = "");
- Task<int> UpdateAsync (object obj, Type objType,string tableName = "");
- }
- /// <summary>
- /// A pooled asynchronous connection to a SQLite database.
- /// </summary>
- public partial class SQLiteAsyncConnection : ISQLiteAsyncConnection
- {
- readonly SQLiteConnectionString _connectionString;
- /// <summary>
- /// Constructs a new SQLiteAsyncConnection and opens a pooled SQLite database specified by databasePath.
- /// </summary>
- /// <param name="databasePath">
- /// Specifies the path to the database file.
- /// </param>
- /// <param name="storeDateTimeAsTicks">
- /// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
- /// absolutely do want to store them as Ticks in all new projects. The value of false is
- /// only here for backwards compatibility. There is a *significant* speed advantage, with no
- /// down sides, when setting storeDateTimeAsTicks = true.
- /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
- /// the storeDateTimeAsTicks parameter.
- /// </param>
- public SQLiteAsyncConnection (string databasePath, bool storeDateTimeAsTicks = true)
- : this (new SQLiteConnectionString (databasePath, SQLiteOpenFlags.Create | SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.FullMutex, storeDateTimeAsTicks))
- {
- }
- /// <summary>
- /// Constructs a new SQLiteAsyncConnection and opens a pooled SQLite database specified by databasePath.
- /// </summary>
- /// <param name="databasePath">
- /// Specifies the path to the database file.
- /// </param>
- /// <param name="openFlags">
- /// Flags controlling how the connection should be opened.
- /// Async connections should have the FullMutex flag set to provide best performance.
- /// </param>
- /// <param name="storeDateTimeAsTicks">
- /// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
- /// absolutely do want to store them as Ticks in all new projects. The value of false is
- /// only here for backwards compatibility. There is a *significant* speed advantage, with no
- /// down sides, when setting storeDateTimeAsTicks = true.
- /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
- /// the storeDateTimeAsTicks parameter.
- /// </param>
- public SQLiteAsyncConnection (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true)
- : this (new SQLiteConnectionString (databasePath, openFlags, storeDateTimeAsTicks))
- {
- }
- /// <summary>
- /// Constructs a new SQLiteAsyncConnection and opens a pooled SQLite database
- /// using the given connection string.
- /// </summary>
- /// <param name="connectionString">
- /// Details on how to find and open the database.
- /// </param>
- public SQLiteAsyncConnection (SQLiteConnectionString connectionString)
- {
- _connectionString = connectionString;
- }
- /// <summary>
- /// Gets the database path used by this connection.
- /// </summary>
- public string DatabasePath => GetConnection ().DatabasePath;
- /// <summary>
- /// Gets the SQLite library version number. 3007014 would be v3.7.14
- /// </summary>
- public int LibVersionNumber => GetConnection ().LibVersionNumber;
- /// <summary>
- /// The format to use when storing DateTime properties as strings. Ignored if StoreDateTimeAsTicks is true.
- /// </summary>
- /// <value>The date time string format.</value>
- public string DateTimeStringFormat => GetConnection ().DateTimeStringFormat;
- /// <summary>
- /// The amount of time to wait for a table to become unlocked.
- /// </summary>
- public TimeSpan GetBusyTimeout ()
- {
- return GetConnection ().BusyTimeout;
- }
- /// <summary>
- /// Sets the amount of time to wait for a table to become unlocked.
- /// </summary>
- public Task SetBusyTimeoutAsync (TimeSpan value)
- {
- return ReadAsync<object> (conn => {
- conn.BusyTimeout = value;
- return null;
- });
- }
- /// <summary>
- /// Enables the write ahead logging. WAL is significantly faster in most scenarios
- /// by providing better concurrency and better disk IO performance than the normal
- /// journal mode. You only need to call this function once in the lifetime of the database.
- /// </summary>
- public Task EnableWriteAheadLoggingAsync ()
- {
- return WriteAsync<object> (conn => {
- conn.EnableWriteAheadLogging ();
- return null;
- });
- }
- /// <summary>
- /// Whether to store DateTime properties as ticks (true) or strings (false).
- /// </summary>
- public bool StoreDateTimeAsTicks => GetConnection ().StoreDateTimeAsTicks;
- /// <summary>
- /// Whether to store TimeSpan properties as ticks (true) or strings (false).
- /// </summary>
- public bool StoreTimeSpanAsTicks => GetConnection ().StoreTimeSpanAsTicks;
- /// <summary>
- /// Whether to writer queries to <see cref="Tracer"/> during execution.
- /// </summary>
- /// <value>The tracer.</value>
- public bool Trace {
- get { return GetConnection ().Trace; }
- set { GetConnection ().Trace = value; }
- }
- /// <summary>
- /// The delegate responsible for writing trace lines.
- /// </summary>
- /// <value>The tracer.</value>
- public Action<string> Tracer {
- get { return GetConnection ().Tracer; }
- set { GetConnection ().Tracer = value; }
- }
- /// <summary>
- /// Whether Trace lines should be written that show the execution time of queries.
- /// </summary>
- public bool TimeExecution {
- get { return GetConnection ().TimeExecution; }
- set { GetConnection ().TimeExecution = value; }
- }
- /// <summary>
- /// Returns the mappings from types to tables that the connection
- /// currently understands.
- /// </summary>
- public IEnumerable<TableMapping> TableMappings => GetConnection ().TableMappings;
- /// <summary>
- /// Closes all connections to all async databases.
- /// You should *never* need to do this.
- /// This is a blocking operation that will return when all connections
- /// have been closed.
- /// </summary>
- public static void ResetPool ()
- {
- SQLiteConnectionPool.Shared.Reset ();
- }
- /// <summary>
- /// Gets the pooled lockable connection used by this async connection.
- /// You should never need to use this. This is provided only to add additional
- /// functionality to SQLite-net. If you use this connection, you must use
- /// the Lock method on it while using it.
- /// </summary>
- public SQLiteConnectionWithLock GetConnection ()
- {
- return SQLiteConnectionPool.Shared.GetConnection (_connectionString);
- }
- SQLiteConnectionWithLock GetConnectionAndTransactionLock (out object transactionLock)
- {
- return SQLiteConnectionPool.Shared.GetConnectionAndTransactionLock (_connectionString, out transactionLock);
- }
- /// <summary>
- /// Closes any pooled connections used by the database.
- /// </summary>
- public Task CloseAsync ()
- {
- return Task.Factory.StartNew (() => {
- SQLiteConnectionPool.Shared.CloseConnection (_connectionString);
- }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
- Task<T> ReadAsync<T> (Func<SQLiteConnectionWithLock, T> read)
- {
- return Task.Factory.StartNew (() => {
- var conn = GetConnection ();
- using (conn.Lock ()) {
- return read (conn);
- }
- }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
- Task<T> WriteAsync<T> (Func<SQLiteConnectionWithLock, T> write)
- {
- return Task.Factory.StartNew (() => {
- var conn = GetConnection ();
- using (conn.Lock ()) {
- return write (conn);
- }
- }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
- Task<T> TransactAsync<T> (Func<SQLiteConnectionWithLock, T> transact)
- {
- return Task.Factory.StartNew (() => {
- var conn = GetConnectionAndTransactionLock (out var transactionLock);
- lock (transactionLock) {
- using (conn.Lock ()) {
- return transact (conn);
- }
- }
- }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
- /// <summary>
- /// Enable or disable extension loading.
- /// </summary>
- public Task EnableLoadExtensionAsync (bool enabled)
- {
- return WriteAsync<object> (conn => {
- conn.EnableLoadExtension (enabled);
- return null;
- });
- }
- /// <summary>
- /// Executes a "create table if not exists" on the database. It also
- /// creates any specified indexes on the columns of the table. It uses
- /// a schema automatically generated from the specified type. You can
- /// later access this schema by calling GetMapping.
- /// </summary>
- /// <returns>
- /// Whether the table was created or migrated.
- /// </returns>
- public Task<CreateTableResult> CreateTableAsync<T> (CreateFlags createFlags = CreateFlags.None,string tableName = "")
- where T : new()
- {
- return WriteAsync (conn => conn.CreateTable<T> (createFlags,tableName));
- }
- /// <summary>
- /// Executes a "create table if not exists" on the database. It also
- /// creates any specified indexes on the columns of the table. It uses
- /// a schema automatically generated from the specified type. You can
- /// later access this schema by calling GetMapping.
- /// </summary>
- /// <param name="ty">Type to reflect to a database table.</param>
- /// <param name="createFlags">Optional flags allowing implicit PK and indexes based on naming conventions.</param>
- /// <returns>
- /// Whether the table was created or migrated.
- /// </returns>
- public Task<CreateTableResult> CreateTableAsync (Type ty, CreateFlags createFlags = CreateFlags.None,string tableName = "")
- {
- return WriteAsync (conn => conn.CreateTable (ty, createFlags,tableName));
- }
- /// <summary>
- /// Executes a "create table if not exists" on the database for each type. It also
- /// creates any specified indexes on the columns of the table. It uses
- /// a schema automatically generated from the specified type. You can
- /// later access this schema by calling GetMapping.
- /// </summary>
- /// <returns>
- /// Whether the table was created or migrated for each type.
- /// </returns>
- public Task<CreateTablesResult> CreateTablesAsync<T, T2> (CreateFlags createFlags = CreateFlags.None)
- where T : new()
- where T2 : new()
- {
- return CreateTablesAsync (createFlags, typeof (T), typeof (T2));
- }
- /// <summary>
- /// Executes a "create table if not exists" on the database for each type. It also
- /// creates any specified indexes on the columns of the table. It uses
- /// a schema automatically generated from the specified type. You can
- /// later access this schema by calling GetMapping.
- /// </summary>
- /// <returns>
- /// Whether the table was created or migrated for each type.
- /// </returns>
- public Task<CreateTablesResult> CreateTablesAsync<T, T2, T3> (CreateFlags createFlags = CreateFlags.None)
- where T : new()
- where T2 : new()
- where T3 : new()
- {
- return CreateTablesAsync (createFlags, typeof (T), typeof (T2), typeof (T3));
- }
- /// <summary>
- /// Executes a "create table if not exists" on the database for each type. It also
- /// creates any specified indexes on the columns of the table. It uses
- /// a schema automatically generated from the specified type. You can
- /// later access this schema by calling GetMapping.
- /// </summary>
- /// <returns>
- /// Whether the table was created or migrated for each type.
- /// </returns>
- public Task<CreateTablesResult> CreateTablesAsync<T, T2, T3, T4> (CreateFlags createFlags = CreateFlags.None)
- where T : new()
- where T2 : new()
- where T3 : new()
- where T4 : new()
- {
- return CreateTablesAsync (createFlags, typeof (T), typeof (T2), typeof (T3), typeof (T4));
- }
- /// <summary>
- /// Executes a "create table if not exists" on the database for each type. It also
- /// creates any specified indexes on the columns of the table. It uses
- /// a schema automatically generated from the specified type. You can
- /// later access this schema by calling GetMapping.
- /// </summary>
- /// <returns>
- /// Whether the table was created or migrated for each type.
- /// </returns>
- public Task<CreateTablesResult> CreateTablesAsync<T, T2, T3, T4, T5> (CreateFlags createFlags = CreateFlags.None)
- where T : new()
- where T2 : new()
- where T3 : new()
- where T4 : new()
- where T5 : new()
- {
- return CreateTablesAsync (createFlags, typeof (T), typeof (T2), typeof (T3), typeof (T4), typeof (T5));
- }
- /// <summary>
- /// Executes a "create table if not exists" on the database for each type. It also
- /// creates any specified indexes on the columns of the table. It uses
- /// a schema automatically generated from the specified type. You can
- /// later access this schema by calling GetMapping.
- /// </summary>
- /// <returns>
- /// Whether the table was created or migrated for each type.
- /// </returns>
- public Task<CreateTablesResult> CreateTablesAsync (CreateFlags createFlags = CreateFlags.None, params Type[] types)
- {
- return WriteAsync (conn => conn.CreateTables (createFlags, types));
- }
- /// <summary>
- /// Executes a "drop table" on the database. This is non-recoverable.
- /// </summary>
- public Task<int> DropTableAsync<T> (string tableName = "")
- where T : new()
- {
- return WriteAsync (conn => conn.DropTable<T> (tableName));
- }
- /// <summary>
- /// Executes a "drop table" on the database. This is non-recoverable.
- /// </summary>
- /// <param name="map">
- /// The TableMapping used to identify the table.
- /// </param>
- public Task<int> DropTableAsync (TableMapping map)
- {
- return WriteAsync (conn => conn.DropTable (map));
- }
- /// <summary>
- /// Creates an index for the specified table and column.
- /// </summary>
- /// <param name="tableName">Name of the database table</param>
- /// <param name="columnName">Name of the column to index</param>
- /// <param name="unique">Whether the index should be unique</param>
- /// <returns>Zero on success.</returns>
- public Task<int> CreateIndexAsync (string tableName, string columnName, bool unique = false)
- {
- return WriteAsync (conn => conn.CreateIndex (tableName, columnName, unique));
- }
- /// <summary>
- /// Creates an index for the specified table and column.
- /// </summary>
- /// <param name="indexName">Name of the index to create</param>
- /// <param name="tableName">Name of the database table</param>
- /// <param name="columnName">Name of the column to index</param>
- /// <param name="unique">Whether the index should be unique</param>
- /// <returns>Zero on success.</returns>
- public Task<int> CreateIndexAsync (string indexName, string tableName, string columnName, bool unique = false)
- {
- return WriteAsync (conn => conn.CreateIndex (indexName, tableName, columnName, unique));
- }
- /// <summary>
- /// Creates an index for the specified table and columns.
- /// </summary>
- /// <param name="tableName">Name of the database table</param>
- /// <param name="columnNames">An array of column names to index</param>
- /// <param name="unique">Whether the index should be unique</param>
- /// <returns>Zero on success.</returns>
- public Task<int> CreateIndexAsync (string tableName, string[] columnNames, bool unique = false)
- {
- return WriteAsync (conn => conn.CreateIndex (tableName, columnNames, unique));
- }
- /// <summary>
- /// Creates an index for the specified table and columns.
- /// </summary>
- /// <param name="indexName">Name of the index to create</param>
- /// <param name="tableName">Name of the database table</param>
- /// <param name="columnNames">An array of column names to index</param>
- /// <param name="unique">Whether the index should be unique</param>
- /// <returns>Zero on success.</returns>
- public Task<int> CreateIndexAsync (string indexName, string tableName, string[] columnNames, bool unique = false)
- {
- return WriteAsync (conn => conn.CreateIndex (indexName, tableName, columnNames, unique));
- }
- /// <summary>
- /// Creates an index for the specified object property.
- /// e.g. CreateIndex<Client>(c => c.Name);
- /// </summary>
- /// <typeparam name="T">Type to reflect to a database table.</typeparam>
- /// <param name="property">Property to index</param>
- /// <param name="unique">Whether the index should be unique</param>
- /// <returns>Zero on success.</returns>
- public Task<int> CreateIndexAsync<T> (Expression<Func<T, object>> property, bool unique = false)
- {
- return WriteAsync (conn => conn.CreateIndex (property, unique));
- }
- /// <summary>
- /// Inserts the given object and (and updates its
- /// auto incremented primary key if it has one).
- /// </summary>
- /// <param name="obj">
- /// The object to insert.
- /// </param>
- /// <returns>
- /// The number of rows added to the table.
- /// </returns>
- public Task<int> InsertAsync (object obj,string tableName = "")
- {
- return WriteAsync (conn => conn.Insert (obj,tableName));
- }
- /// <summary>
- /// Inserts the given object (and updates its
- /// auto incremented primary key if it has one).
- /// The return value is the number of rows added to the table.
- /// </summary>
- /// <param name="obj">
- /// The object to insert.
- /// </param>
- /// <param name="objType">
- /// The type of object to insert.
- /// </param>
- /// <returns>
- /// The number of rows added to the table.
- /// </returns>
- public Task<int> InsertAsync (object obj, Type objType,string tableName = "")
- {
- return WriteAsync (conn => conn.Insert (obj, objType,tableName));
- }
- /// <summary>
- /// Inserts the given object (and updates its
- /// auto incremented primary key if it has one).
- /// The return value is the number of rows added to the table.
- /// </summary>
- /// <param name="obj">
- /// The object to insert.
- /// </param>
- /// <param name="extra">
- /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
- /// </param>
- /// <returns>
- /// The number of rows added to the table.
- /// </returns>
- public Task<int> InsertAsync (object obj, string extra,string tableName = "")
- {
- return WriteAsync (conn => conn.Insert (obj, extra,tableName));
- }
- /// <summary>
- /// Inserts the given object (and updates its
- /// auto incremented primary key if it has one).
- /// The return value is the number of rows added to the table.
- /// </summary>
- /// <param name="obj">
- /// The object to insert.
- /// </param>
- /// <param name="extra">
- /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
- /// </param>
- /// <param name="objType">
- /// The type of object to insert.
- /// </param>
- /// <returns>
- /// The number of rows added to the table.
- /// </returns>
- public Task<int> InsertAsync (object obj, string extra, Type objType,string tableName = "")
- {
- return WriteAsync (conn => conn.Insert (obj, extra, objType,tableName));
- }
- /// <summary>
- /// Inserts the given object (and updates its
- /// auto incremented primary key if it has one).
- /// The return value is the number of rows added to the table.
- /// If a UNIQUE constraint violation occurs with
- /// some pre-existing object, this function deletes
- /// the old object.
- /// </summary>
- /// <param name="obj">
- /// The object to insert.
- /// </param>
- /// <returns>
- /// The number of rows modified.
- /// </returns>
- public Task<int> InsertOrReplaceAsync (object obj,string tableName = "")
- {
- return WriteAsync (conn => conn.InsertOrReplace (obj,tableName));
- }
- /// <summary>
- /// Inserts the given object (and updates its
- /// auto incremented primary key if it has one).
- /// The return value is the number of rows added to the table.
- /// If a UNIQUE constraint violation occurs with
- /// some pre-existing object, this function deletes
- /// the old object.
- /// </summary>
- /// <param name="obj">
- /// The object to insert.
- /// </param>
- /// <param name="objType">
- /// The type of object to insert.
- /// </param>
- /// <returns>
- /// The number of rows modified.
- /// </returns>
- public Task<int> InsertOrReplaceAsync (object obj, Type objType,string tableName = "")
- {
- return WriteAsync (conn => conn.InsertOrReplace (obj, objType,tableName));
- }
- /// <summary>
- /// Updates all of the columns of a table using the specified object
- /// except for its primary key.
- /// The object is required to have a primary key.
- /// </summary>
- /// <param name="obj">
- /// The object to update. It must have a primary key designated using the PrimaryKeyAttribute.
- /// </param>
- /// <returns>
- /// The number of rows updated.
- /// </returns>
- public Task<int> UpdateAsync (object obj,string tableName = "")
- {
- return WriteAsync (conn => conn.Update (obj,tableName));
- }
- /// <summary>
- /// Updates all of the columns of a table using the specified object
- /// except for its primary key.
- /// The object is required to have a primary key.
- /// </summary>
- /// <param name="obj">
- /// The object to update. It must have a primary key designated using the PrimaryKeyAttribute.
- /// </param>
- /// <param name="objType">
- /// The type of object to insert.
- /// </param>
- /// <returns>
- /// The number of rows updated.
- /// </returns>
- public Task<int> UpdateAsync (object obj, Type objType,string tableName = "")
- {
- return WriteAsync (conn => conn.Update (obj, objType,tableName));
- }
- /// <summary>
- /// Updates all specified objects.
- /// </summary>
- /// <param name="objects">
- /// An <see cref="IEnumerable"/> of the objects to insert.
- /// </param>
- /// <param name="runInTransaction">
- /// A boolean indicating if the inserts should be wrapped in a transaction
- /// </param>
- /// <returns>
- /// The number of rows modified.
- /// </returns>
- public Task<int> UpdateAllAsync (IEnumerable objects, bool runInTransaction = true,string tableName = "")
- {
- return WriteAsync (conn => conn.UpdateAll (objects, runInTransaction,tableName));
- }
- /// <summary>
- /// Deletes the given object from the database using its primary key.
- /// </summary>
- /// <param name="objectToDelete">
- /// The object to delete. It must have a primary key designated using the PrimaryKeyAttribute.
- /// </param>
- /// <returns>
- /// The number of rows deleted.
- /// </returns>
- public Task<int> DeleteAsync (object objectToDelete,string tableName = "")
- {
- return WriteAsync (conn => conn.Delete (objectToDelete,tableName));
- }
- /// <summary>
- /// Deletes the object with the specified primary key.
- /// </summary>
- /// <param name="primaryKey">
- /// The primary key of the object to delete.
- /// </param>
- /// <returns>
- /// The number of objects deleted.
- /// </returns>
- /// <typeparam name='T'>
- /// The type of object.
- /// </typeparam>
- public Task<int> DeleteAsync<T> (object primaryKey,string tableName = "")
- {
- return WriteAsync (conn => conn.Delete<T> (primaryKey,tableName));
- }
- /// <summary>
- /// Deletes the object with the specified primary key.
- /// </summary>
- /// <param name="primaryKey">
- /// The primary key of the object to delete.
- /// </param>
- /// <param name="map">
- /// The TableMapping used to identify the table.
- /// </param>
- /// <returns>
- /// The number of objects deleted.
- /// </returns>
- public Task<int> DeleteAsync (object primaryKey, TableMapping map)
- {
- return WriteAsync (conn => conn.Delete (primaryKey, map));
- }
- /// <summary>
- /// Deletes all the objects from the specified table.
- /// WARNING WARNING: Let me repeat. It deletes ALL the objects from the
- /// specified table. Do you really want to do that?
- /// </summary>
- /// <returns>
- /// The number of objects deleted.
- /// </returns>
- /// <typeparam name='T'>
- /// The type of objects to delete.
- /// </typeparam>
- public Task<int> DeleteAllAsync<T> (string tableName = "")
- {
- return WriteAsync (conn => conn.DeleteAll<T> (tableName));
- }
- /// <summary>
- /// Deletes all the objects from the specified table.
- /// WARNING WARNING: Let me repeat. It deletes ALL the objects from the
- /// specified table. Do you really want to do that?
- /// </summary>
- /// <param name="map">
- /// The TableMapping used to identify the table.
- /// </param>
- /// <returns>
- /// The number of objects deleted.
- /// </returns>
- public Task<int> DeleteAllAsync (TableMapping map)
- {
- return WriteAsync (conn => conn.DeleteAll (map));
- }
- /// <summary>
- /// Backup the entire database to the specified path.
- /// </summary>
- /// <param name="destinationDatabasePath">Path to backup file.</param>
- /// <param name="databaseName">The name of the database to backup (usually "main").</param>
- public Task BackupAsync (string destinationDatabasePath, string databaseName = "main")
- {
- return WriteAsync (conn => {
- conn.Backup (destinationDatabasePath, databaseName);
- return 0;
- });
- }
- /// <summary>
- /// Attempts to retrieve an object with the given primary key from the table
- /// associated with the specified type. Use of this method requires that
- /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
- /// </summary>
- /// <param name="pk">
- /// The primary key.
- /// </param>
- /// <returns>
- /// The object with the given primary key. Throws a not found exception
- /// if the object is not found.
- /// </returns>
- public Task<T> GetAsync<T> (object pk,string tableName = "")
- where T : new()
- {
- return ReadAsync (conn => conn.Get<T> (pk,tableName));
- }
- /// <summary>
- /// Attempts to retrieve an object with the given primary key from the table
- /// associated with the specified type. Use of this method requires that
- /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
- /// </summary>
- /// <param name="pk">
- /// The primary key.
- /// </param>
- /// <param name="map">
- /// The TableMapping used to identify the table.
- /// </param>
- /// <returns>
- /// The object with the given primary key. Throws a not found exception
- /// if the object is not found.
- /// </returns>
- public Task<object> GetAsync (object pk, TableMapping map)
- {
- return ReadAsync (conn => conn.Get (pk, map));
- }
- /// <summary>
- /// Attempts to retrieve the first object that matches the predicate from the table
- /// associated with the specified type.
- /// </summary>
- /// <param name="predicate">
- /// A predicate for which object to find.
- /// </param>
- /// <returns>
- /// The object that matches the given predicate. Throws a not found exception
- /// if the object is not found.
- /// </returns>
- public Task<T> GetAsync<T> (Expression<Func<T, bool>> predicate,string tableName = "")
- where T : new()
- {
- return ReadAsync (conn => conn.Get<T> (predicate,tableName));
- }
- /// <summary>
- /// Attempts to retrieve an object with the given primary key from the table
- /// associated with the specified type. Use of this method requires that
- /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
- /// </summary>
- /// <param name="pk">
- /// The primary key.
- /// </param>
- /// <returns>
- /// The object with the given primary key or null
- /// if the object is not found.
- /// </returns>
- public Task<T> FindAsync<T> (object pk,string tableName = "")
- where T : new()
- {
- return ReadAsync (conn => conn.Find<T> (pk,tableName));
- }
- /// <summary>
- /// Attempts to retrieve an object with the given primary key from the table
- /// associated with the specified type. Use of this method requires that
- /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
- /// </summary>
- /// <param name="pk">
- /// The primary key.
- /// </param>
- /// <param name="map">
- /// The TableMapping used to identify the table.
- /// </param>
- /// <returns>
- /// The object with the given primary key or null
- /// if the object is not found.
- /// </returns>
- public Task<object> FindAsync (object pk, TableMapping map)
- {
- return ReadAsync (conn => conn.Find (pk, map));
- }
- /// <summary>
- /// Attempts to retrieve the first object that matches the predicate from the table
- /// associated with the specified type.
- /// </summary>
- /// <param name="predicate">
- /// A predicate for which object to find.
- /// </param>
- /// <returns>
- /// The object that matches the given predicate or null
- /// if the object is not found.
- /// </returns>
- public Task<T> FindAsync<T> (Expression<Func<T, bool>> predicate,string tableName = "")
- where T : new()
- {
- return ReadAsync (conn => conn.Find<T> (predicate,tableName));
- }
- /// <summary>
- /// Attempts to retrieve the first object that matches the query from the table
- /// associated with the specified type.
- /// </summary>
- /// <param name="query">
- /// The fully escaped SQL.
- /// </param>
- /// <param name="args">
- /// Arguments to substitute for the occurences of '?' in the query.
- /// </param>
- /// <returns>
- /// The object that matches the given predicate or null
- /// if the object is not found.
- /// </returns>
- public Task<T> FindWithQueryAsync<T> (string query, params object[] args)
- where T : new()
- {
- return ReadAsync (conn => conn.FindWithQuery<T> (query, args));
- }
- /// <summary>
- /// Attempts to retrieve the first object that matches the query from the table
- /// associated with the specified type.
- /// </summary>
- /// <param name="map">
- /// The TableMapping used to identify the table.
- /// </param>
- /// <param name="query">
- /// The fully escaped SQL.
- /// </param>
- /// <param name="args">
- /// Arguments to substitute for the occurences of '?' in the query.
- /// </param>
- /// <returns>
- /// The object that matches the given predicate or null
- /// if the object is not found.
- /// </returns>
- public Task<object> FindWithQueryAsync (TableMapping map, string query, params object[] args)
- {
- return ReadAsync (conn => conn.FindWithQuery (map, query, args));
- }
- /// <summary>
- /// Retrieves the mapping that is automatically generated for the given type.
- /// </summary>
- /// <param name="type">
- /// The type whose mapping to the database is returned.
- /// </param>
- /// <param name="createFlags">
- /// Optional flags allowing implicit PK and indexes based on naming conventions
- /// </param>
- /// <returns>
- /// The mapping represents the schema of the columns of the database and contains
- /// methods to set and get properties of objects.
- /// </returns>
- public Task<TableMapping> GetMappingAsync (Type type, CreateFlags createFlags = CreateFlags.None,string tableName = "")
- {
- return ReadAsync (conn => conn.GetMapping (type, createFlags,tableName));
- }
- /// <summary>
- /// Retrieves the mapping that is automatically generated for the given type.
- /// </summary>
- /// <param name="createFlags">
- /// Optional flags allowing implicit PK and indexes based on naming conventions
- /// </param>
- /// <returns>
- /// The mapping represents the schema of the columns of the database and contains
- /// methods to set and get properties of objects.
- /// </returns>
- public Task<TableMapping> GetMappingAsync<T> (CreateFlags createFlags = CreateFlags.None,string tableName = "")
- where T : new()
- {
- return ReadAsync (conn => conn.GetMapping<T> (createFlags,tableName));
- }
- /// <summary>
- /// Query the built-in sqlite table_info table for a specific tables columns.
- /// </summary>
- /// <returns>The columns contains in the table.</returns>
- /// <param name="tableName">Table name.</param>
- public Task<List<SQLiteConnection.ColumnInfo>> GetTableInfoAsync (string tableName)
- {
- return ReadAsync (conn => conn.GetTableInfo (tableName));
- }
- /// <summary>
- /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
- /// in the command text for each of the arguments and then executes that command.
- /// Use this method instead of Query when you don't expect rows back. Such cases include
- /// INSERTs, UPDATEs, and DELETEs.
- /// You can set the Trace or TimeExecution properties of the connection
- /// to profile execution.
- /// </summary>
- /// <param name="query">
- /// The fully escaped SQL.
- /// </param>
- /// <param name="args">
- /// Arguments to substitute for the occurences of '?' in the query.
- /// </param>
- /// <returns>
- /// The number of rows modified in the database as a result of this execution.
- /// </returns>
- public Task<int> ExecuteAsync (string query, params object[] args)
- {
- return WriteAsync (conn => conn.Execute (query, args));
- }
- /// <summary>
- /// Inserts all specified objects.
- /// </summary>
- /// <param name="objects">
- /// An <see cref="IEnumerable"/> of the objects to insert.
- /// <param name="runInTransaction"/>
- /// A boolean indicating if the inserts should be wrapped in a transaction.
- /// </param>
- /// <returns>
- /// The number of rows added to the table.
- /// </returns>
- public Task<int> InsertAllAsync (IEnumerable objects, bool runInTransaction = true,string tableName = "")
- {
- return WriteAsync (conn => conn.InsertAll (objects, runInTransaction,tableName));
- }
- /// <summary>
- /// Inserts all specified objects.
- /// </summary>
- /// <param name="objects">
- /// An <see cref="IEnumerable"/> of the objects to insert.
- /// </param>
- /// <param name="extra">
- /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
- /// </param>
- /// <param name="runInTransaction">
- /// A boolean indicating if the inserts should be wrapped in a transaction.
- /// </param>
- /// <returns>
- /// The number of rows added to the table.
- /// </returns>
- public Task<int> InsertAllAsync (IEnumerable objects, string extra, bool runInTransaction = true,string tableName = "")
- {
- return WriteAsync (conn => conn.InsertAll (objects, extra, runInTransaction,tableName));
- }
- /// <summary>
- /// Inserts all specified objects.
- /// </summary>
- /// <param name="objects">
- /// An <see cref="IEnumerable"/> of the objects to insert.
- /// </param>
- /// <param name="objType">
- /// The type of object to insert.
- /// </param>
- /// <param name="runInTransaction">
- /// A boolean indicating if the inserts should be wrapped in a transaction.
- /// </param>
- /// <returns>
- /// The number of rows added to the table.
- /// </returns>
- public Task<int> InsertAllAsync (IEnumerable objects, Type objType, bool runInTransaction = true,string tableName = "")
- {
- return WriteAsync (conn => conn.InsertAll (objects, objType, runInTransaction,tableName));
- }
- /// <summary>
- /// Executes <paramref name="action"/> within a (possibly nested) transaction by wrapping it in a SAVEPOINT. If an
- /// exception occurs the whole transaction is rolled back, not just the current savepoint. The exception
- /// is rethrown.
- /// </summary>
- /// <param name="action">
- /// The <see cref="Action"/> to perform within a transaction. <paramref name="action"/> can contain any number
- /// of operations on the connection but should never call <see cref="SQLiteConnection.Commit"/> or
- /// <see cref="SQLiteConnection.Commit"/>.
- /// </param>
- public Task RunInTransactionAsync (Action<SQLiteConnection> action)
- {
- return TransactAsync<object> (conn => {
- conn.BeginTransaction ();
- try {
- action (conn);
- conn.Commit ();
- return null;
- }
- catch (Exception) {
- conn.Rollback ();
- throw;
- }
- });
- }
- /// <summary>
- /// Returns a queryable interface to the table represented by the given type.
- /// </summary>
- /// <returns>
- /// A queryable object that is able to translate Where, OrderBy, and Take
- /// queries into native SQL.
- /// </returns>
- public AsyncTableQuery<T> Table<T> (string tableName = "")
- where T : new()
- {
- //
- // This isn't async as the underlying connection doesn't go out to the database
- // until the query is performed. The Async methods are on the query iteself.
- //
- var conn = GetConnection ();
- return new AsyncTableQuery<T> (conn.Table<T> (tableName));
- }
- /// <summary>
- /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
- /// in the command text for each of the arguments and then executes that command.
- /// Use this method when return primitive values.
- /// You can set the Trace or TimeExecution properties of the connection
- /// to profile execution.
- /// </summary>
- /// <param name="query">
- /// The fully escaped SQL.
- /// </param>
- /// <param name="args">
- /// Arguments to substitute for the occurences of '?' in the query.
- /// </param>
- /// <returns>
- /// The number of rows modified in the database as a result of this execution.
- /// </returns>
- public Task<T> ExecuteScalarAsync<T> (string query, params object[] args)
- {
- return WriteAsync (conn => {
- var command = conn.CreateCommand (query, args);
- return command.ExecuteScalar<T> ();
- });
- }
- /// <summary>
- /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
- /// in the command text for each of the arguments and then executes that command.
- /// It returns each row of the result using the mapping automatically generated for
- /// the given type.
- /// </summary>
- /// <param name="query">
- /// The fully escaped SQL.
- /// </param>
- /// <param name="args">
- /// Arguments to substitute for the occurences of '?' in the query.
- /// </param>
- /// <returns>
- /// A list with one result for each row returned by the query.
- /// </returns>
- public Task<List<T>> QueryAsync<T> (string query, params object[] args)
- where T : new()
- {
- return ReadAsync (conn => conn.Query<T> (query, args));
- }
- /// <summary>
- /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
- /// in the command text for each of the arguments and then executes that command.
- /// It returns the first column of each row of the result.
- /// </summary>
- /// <param name="query">
- /// The fully escaped SQL.
- /// </param>
- /// <param name="args">
- /// Arguments to substitute for the occurences of '?' in the query.
- /// </param>
- /// <returns>
- /// A list with one result for the first column of each row returned by the query.
- /// </returns>
- public Task<List<T>> QueryScalarsAsync<T> (string query, params object[] args)
- {
- return ReadAsync (conn => conn.QueryScalars<T> (query, args));
- }
- /// <summary>
- /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
- /// in the command text for each of the arguments and then executes that command.
- /// It returns each row of the result using the specified mapping. This function is
- /// only used by libraries in order to query the database via introspection. It is
- /// normally not used.
- /// </summary>
- /// <param name="map">
- /// A <see cref="TableMapping"/> to use to convert the resulting rows
- /// into objects.
- /// </param>
- /// <param name="query">
- /// The fully escaped SQL.
- /// </param>
- /// <param name="args">
- /// Arguments to substitute for the occurences of '?' in the query.
- /// </param>
- /// <returns>
- /// An enumerable with one result for each row returned by the query.
- /// </returns>
- public Task<List<object>> QueryAsync (TableMapping map, string query, params object[] args)
- {
- return ReadAsync (conn => conn.Query (map, query, args));
- }
- /// <summary>
- /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
- /// in the command text for each of the arguments and then executes that command.
- /// It returns each row of the result using the mapping automatically generated for
- /// the given type.
- /// </summary>
- /// <param name="query">
- /// The fully escaped SQL.
- /// </param>
- /// <param name="args">
- /// Arguments to substitute for the occurences of '?' in the query.
- /// </param>
- /// <returns>
- /// An enumerable with one result for each row returned by the query.
- /// The enumerator will call sqlite3_step on each call to MoveNext, so the database
- /// connection must remain open for the lifetime of the enumerator.
- /// </returns>
- public Task<IEnumerable<T>> DeferredQueryAsync<T> (string query, params object[] args)
- where T : new()
- {
- return ReadAsync (conn => (IEnumerable<T>)conn.DeferredQuery<T> (query, args).ToList ());
- }
- /// <summary>
- /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
- /// in the command text for each of the arguments and then executes that command.
- /// It returns each row of the result using the specified mapping. This function is
- /// only used by libraries in order to query the database via introspection. It is
- /// normally not used.
- /// </summary>
- /// <param name="map">
- /// A <see cref="TableMapping"/> to use to convert the resulting rows
- /// into objects.
- /// </param>
- /// <param name="query">
- /// The fully escaped SQL.
- /// </param>
- /// <param name="args">
- /// Arguments to substitute for the occurences of '?' in the query.
- /// </param>
- /// <returns>
- /// An enumerable with one result for each row returned by the query.
- /// The enumerator will call sqlite3_step on each call to MoveNext, so the database
- /// connection must remain open for the lifetime of the enumerator.
- /// </returns>
- public Task<IEnumerable<object>> DeferredQueryAsync (TableMapping map, string query, params object[] args)
- {
- return ReadAsync (conn => (IEnumerable<object>)conn.DeferredQuery (map, query, args).ToList ());
- }
- /// <summary>
- /// Change the encryption key for a SQLCipher database with "pragma rekey = ...".
- /// </summary>
- /// <param name="key">Encryption key plain text that is converted to the real encryption key using PBKDF2 key derivation</param>
- public Task ReKeyAsync (string key)
- {
- return WriteAsync<object> (conn => {
- conn.ReKey (key);
- return null;
- });
- }
- /// <summary>
- /// Change the encryption key for a SQLCipher database.
- /// </summary>
- /// <param name="key">256-bit (32 byte) or 384-bit (48 bytes) encryption key data</param>
- public Task ReKeyAsync (byte[] key)
- {
- return WriteAsync<object> (conn => {
- conn.ReKey (key);
- return null;
- });
- }
- }
- /// <summary>
- /// Query to an asynchronous database connection.
- /// </summary>
- public class AsyncTableQuery<T>
- where T : new()
- {
- TableQuery<T> _innerQuery;
- /// <summary>
- /// Creates a new async query that uses given the synchronous query.
- /// </summary>
- public AsyncTableQuery (TableQuery<T> innerQuery)
- {
- _innerQuery = innerQuery;
- }
- Task<U> ReadAsync<U> (Func<SQLiteConnectionWithLock, U> read)
- {
- return Task.Factory.StartNew (() => {
- var conn = (SQLiteConnectionWithLock)_innerQuery.Connection;
- using (conn.Lock ()) {
- return read (conn);
- }
- }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
- Task<U> WriteAsync<U> (Func<SQLiteConnectionWithLock, U> write)
- {
- return Task.Factory.StartNew (() => {
- var conn = (SQLiteConnectionWithLock)_innerQuery.Connection;
- using (conn.Lock ()) {
- return write (conn);
- }
- }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
- }
- /// <summary>
- /// Filters the query based on a predicate.
- /// </summary>
- public AsyncTableQuery<T> Where (Expression<Func<T, bool>> predExpr)
- {
- return new AsyncTableQuery<T> (_innerQuery.Where (predExpr));
- }
- /// <summary>
- /// Skips a given number of elements from the query and then yields the remainder.
- /// </summary>
- public AsyncTableQuery<T> Skip (int n)
- {
- return new AsyncTableQuery<T> (_innerQuery.Skip (n));
- }
- /// <summary>
- /// Yields a given number of elements from the query and then skips the remainder.
- /// </summary>
- public AsyncTableQuery<T> Take (int n)
- {
- return new AsyncTableQuery<T> (_innerQuery.Take (n));
- }
- /// <summary>
- /// Order the query results according to a key.
- /// </summary>
- public AsyncTableQuery<T> OrderBy<U> (Expression<Func<T, U>> orderExpr)
- {
- return new AsyncTableQuery<T> (_innerQuery.OrderBy<U> (orderExpr));
- }
- /// <summary>
- /// Order the query results according to a key.
- /// </summary>
- public AsyncTableQuery<T> OrderByDescending<U> (Expression<Func<T, U>> orderExpr)
- {
- return new AsyncTableQuery<T> (_innerQuery.OrderByDescending<U> (orderExpr));
- }
- /// <summary>
- /// Order the query results according to a key.
- /// </summary>
- public AsyncTableQuery<T> ThenBy<U> (Expression<Func<T, U>> orderExpr)
- {
- return new AsyncTableQuery<T> (_innerQuery.ThenBy<U> (orderExpr));
- }
- /// <summary>
- /// Order the query results according to a key.
- /// </summary>
- public AsyncTableQuery<T> ThenByDescending<U> (Expression<Func<T, U>> orderExpr)
- {
- return new AsyncTableQuery<T> (_innerQuery.ThenByDescending<U> (orderExpr));
- }
- /// <summary>
- /// Queries the database and returns the results as a List.
- /// </summary>
- public Task<List<T>> ToListAsync ()
- {
- return ReadAsync (conn => _innerQuery.ToList ());
- }
- /// <summary>
- /// Queries the database and returns the results as an array.
- /// </summary>
- public Task<T[]> ToArrayAsync ()
- {
- return ReadAsync (conn => _innerQuery.ToArray ());
- }
- /// <summary>
- /// Execute SELECT COUNT(*) on the query
- /// </summary>
- public Task<int> CountAsync ()
- {
- return ReadAsync (conn => _innerQuery.Count ());
- }
- /// <summary>
- /// Execute SELECT COUNT(*) on the query with an additional WHERE clause.
- /// </summary>
- public Task<int> CountAsync (Expression<Func<T, bool>> predExpr)
- {
- return ReadAsync (conn => _innerQuery.Count (predExpr));
- }
- /// <summary>
- /// Returns the element at a given index
- /// </summary>
- public Task<T> ElementAtAsync (int index)
- {
- return ReadAsync (conn => _innerQuery.ElementAt (index));
- }
- /// <summary>
- /// Returns the first element of this query.
- /// </summary>
- public Task<T> FirstAsync ()
- {
- return ReadAsync (conn => _innerQuery.First ());
- }
- /// <summary>
- /// Returns the first element of this query, or null if no element is found.
- /// </summary>
- public Task<T> FirstOrDefaultAsync ()
- {
- return ReadAsync (conn => _innerQuery.FirstOrDefault ());
- }
- /// <summary>
- /// Returns the first element of this query that matches the predicate.
- /// </summary>
- public Task<T> FirstAsync (Expression<Func<T, bool>> predExpr)
- {
- return ReadAsync (conn => _innerQuery.First (predExpr));
- }
- /// <summary>
- /// Returns the first element of this query that matches the predicate.
- /// </summary>
- public Task<T> FirstOrDefaultAsync (Expression<Func<T, bool>> predExpr)
- {
- return ReadAsync (conn => _innerQuery.FirstOrDefault (predExpr));
- }
- /// <summary>
- /// Delete all the rows that match this query and the given predicate.
- /// </summary>
- public Task<int> DeleteAsync (Expression<Func<T, bool>> predExpr)
- {
- return WriteAsync (conn => _innerQuery.Delete (predExpr));
- }
- /// <summary>
- /// Delete all the rows that match this query.
- /// </summary>
- public Task<int> DeleteAsync ()
- {
- return WriteAsync (conn => _innerQuery.Delete ());
- }
- }
- class SQLiteConnectionPool
- {
- class Entry
- {
- public SQLiteConnectionWithLock Connection { get; private set; }
- public SQLiteConnectionString ConnectionString { get; }
- public object TransactionLock { get; } = new object ();
- public Entry (SQLiteConnectionString connectionString)
- {
- ConnectionString = connectionString;
- Connection = new SQLiteConnectionWithLock (ConnectionString);
- // If the database is FullMutex, then we don't need to bother locking
- if (ConnectionString.OpenFlags.HasFlag (SQLiteOpenFlags.FullMutex)) {
- Connection.SkipLock = true;
- }
- }
- public void Close ()
- {
- var wc = Connection;
- Connection = null;
- if (wc != null) {
- wc.Close ();
- }
- }
- }
- readonly Dictionary<string, Entry> _entries = new Dictionary<string, Entry> ();
- readonly object _entriesLock = new object ();
- static readonly SQLiteConnectionPool _shared = new SQLiteConnectionPool ();
- /// <summary>
- /// Gets the singleton instance of the connection tool.
- /// </summary>
- public static SQLiteConnectionPool Shared {
- get {
- return _shared;
- }
- }
- public SQLiteConnectionWithLock GetConnection (SQLiteConnectionString connectionString)
- {
- return GetConnectionAndTransactionLock (connectionString, out var _);
- }
- public SQLiteConnectionWithLock GetConnectionAndTransactionLock (SQLiteConnectionString connectionString, out object transactionLock)
- {
- var key = connectionString.UniqueKey;
- Entry entry;
- lock (_entriesLock) {
- if (!_entries.TryGetValue (key, out entry)) {
- // The opens the database while we're locked
- // This is to ensure another thread doesn't get an unopened database
- entry = new Entry (connectionString);
- _entries[key] = entry;
- }
- transactionLock = entry.TransactionLock;
- return entry.Connection;
- }
- }
- public void CloseConnection (SQLiteConnectionString connectionString)
- {
- var key = connectionString.UniqueKey;
- Entry entry;
- lock (_entriesLock) {
- if (_entries.TryGetValue (key, out entry)) {
- _entries.Remove (key);
- }
- }
- entry?.Close ();
- }
- /// <summary>
- /// Closes all connections managed by this pool.
- /// </summary>
- public void Reset ()
- {
- List<Entry> entries;
- lock (_entriesLock) {
- entries = new List<Entry> (_entries.Values);
- _entries.Clear ();
- }
- foreach (var e in entries) {
- e.Close ();
- }
- }
- }
- /// <summary>
- /// This is a normal connection except it contains a Lock method that
- /// can be used to serialize access to the database
- /// in lieu of using the sqlite's FullMutex support.
- /// </summary>
- public class SQLiteConnectionWithLock : SQLiteConnection
- {
- readonly object _lockPoint = new object ();
- /// <summary>
- /// Initializes a new instance of the <see cref="T:SQLite.SQLiteConnectionWithLock"/> class.
- /// </summary>
- /// <param name="connectionString">Connection string containing the DatabasePath.</param>
- public SQLiteConnectionWithLock (SQLiteConnectionString connectionString)
- : base (connectionString)
- {
- }
- /// <summary>
- /// Gets or sets a value indicating whether this <see cref="T:SQLite.SQLiteConnectionWithLock"/> skip lock.
- /// </summary>
- /// <value><c>true</c> if skip lock; otherwise, <c>false</c>.</value>
- public bool SkipLock { get; set; }
- /// <summary>
- /// Lock the database to serialize access to it. To unlock it, call Dispose
- /// on the returned object.
- /// </summary>
- /// <returns>The lock.</returns>
- public IDisposable Lock ()
- {
- return SkipLock ? (IDisposable)new FakeLockWrapper() : new LockWrapper (_lockPoint);
- }
- class LockWrapper : IDisposable
- {
- object _lockPoint;
- public LockWrapper (object lockPoint)
- {
- _lockPoint = lockPoint;
- Monitor.Enter (_lockPoint);
- }
- public void Dispose ()
- {
- Monitor.Exit (_lockPoint);
- }
- }
- class FakeLockWrapper : IDisposable
- {
- public void Dispose ()
- {
- }
- }
- }
- }
|